Add basic extended attributes support

This commit adds extended attributes support to TDEIO, tdeio_file and a read-write plugin for the file properties dialog.

Signed-off-by: Philippe Mavridis <philippe.mavridis@yandex.com>
feat/extended-attributes
Philippe Mavridis 1 week ago
parent 2e76346c68
commit c17df85bda
No known key found for this signature in database
GPG Key ID: BB7EB07B27B3C64B

@ -28,7 +28,6 @@
#include <tdestandarddirs.h>
#include <tdeglobal.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <ksimpleconfig.h>
#include <tdeconfig.h>
#include <kstringhandler.h>
@ -71,6 +70,8 @@ KProtocolInfo::KProtocolInfo(const TQString &path)
m_supportsDeleting = config.readBoolEntry( "deleting", false );
m_supportsLinking = config.readBoolEntry( "linking", false );
m_supportsMoving = config.readBoolEntry( "moving", false );
m_supportsReadingAttrs = config.readBoolEntry( "readattr", false );
m_supportsWritingAttrs = config.readBoolEntry( "writeattr", false );
m_canCopyFromFile = config.readBoolEntry( "copyFromFile", false );
m_canCopyToFile = config.readBoolEntry( "copyToFile", false );
d->canRenameFromFile = config.readBoolEntry( "renameFromFile", false );
@ -159,7 +160,8 @@ KProtocolInfo::load( TQDataStream& _str)
i_supportsMoving, i_determineMimetypeFromExtension,
i_canCopyFromFile, i_canCopyToFile, i_showPreviews,
i_uriMode, i_canRenameFromFile, i_canRenameToFile,
i_canDeleteRecursive, i_fileNameUsedForCopying;
i_canDeleteRecursive, i_fileNameUsedForCopying,
i_supportsReadingAttrs, i_supportsWritingAttrs;
_str >> m_name >> m_exec >> m_listing >> m_defaultMimetype
>> i_determineMimetypeFromExtension
@ -175,7 +177,8 @@ KProtocolInfo::load( TQDataStream& _str)
>> d->extraFields >> i_showPreviews >> i_uriMode
>> d->capabilities >> d->proxyProtocol
>> i_canRenameFromFile >> i_canRenameToFile
>> i_canDeleteRecursive >> i_fileNameUsedForCopying;
>> i_canDeleteRecursive >> i_fileNameUsedForCopying
>> i_supportsReadingAttrs >> i_supportsWritingAttrs;
m_inputType = (Type) i_inputType;
m_outputType = (Type) i_outputType;
@ -188,6 +191,8 @@ KProtocolInfo::load( TQDataStream& _str)
m_supportsDeleting = (i_supportsDeleting != 0);
m_supportsLinking = (i_supportsLinking != 0);
m_supportsMoving = (i_supportsMoving != 0);
m_supportsReadingAttrs = (i_supportsReadingAttrs != 0);
m_supportsWritingAttrs = (i_supportsWritingAttrs != 0);
m_canCopyFromFile = (i_canCopyFromFile != 0);
m_canCopyToFile = (i_canCopyToFile != 0);
d->canRenameFromFile = (i_canRenameFromFile != 0);
@ -214,7 +219,8 @@ KProtocolInfo::save( TQDataStream& _str)
i_supportsMoving, i_determineMimetypeFromExtension,
i_canCopyFromFile, i_canCopyToFile, i_showPreviews,
i_uriMode, i_canRenameFromFile, i_canRenameToFile,
i_canDeleteRecursive, i_fileNameUsedForCopying;
i_canDeleteRecursive, i_fileNameUsedForCopying,
i_supportsReadingAttrs, i_supportsWritingAttrs;
i_inputType = (TQ_INT32) m_inputType;
i_outputType = (TQ_INT32) m_outputType;
@ -227,6 +233,8 @@ KProtocolInfo::save( TQDataStream& _str)
i_supportsDeleting = m_supportsDeleting ? 1 : 0;
i_supportsLinking = m_supportsLinking ? 1 : 0;
i_supportsMoving = m_supportsMoving ? 1 : 0;
i_supportsReadingAttrs = m_supportsReadingAttrs ? 1 : 0;
i_supportsWritingAttrs = m_supportsWritingAttrs ? 1 : 0;
i_canCopyFromFile = m_canCopyFromFile ? 1 : 0;
i_canCopyToFile = m_canCopyToFile ? 1 : 0;
i_canRenameFromFile = d->canRenameFromFile ? 1 : 0;
@ -251,7 +259,8 @@ KProtocolInfo::save( TQDataStream& _str)
<< d->extraFields << i_showPreviews << i_uriMode
<< d->capabilities << d->proxyProtocol
<< i_canRenameFromFile << i_canRenameToFile
<< i_canDeleteRecursive << i_fileNameUsedForCopying;
<< i_canDeleteRecursive << i_fileNameUsedForCopying
<< i_supportsReadingAttrs << i_supportsWritingAttrs;
}

@ -124,6 +124,7 @@ extern "C" {
#include <krun.h>
#include <tdelistview.h>
#include <kacl.h>
#include <kprotocolinfo.h>
#include "tdefilesharedlg.h"
#include "kpropertiesdesktopbase.h"
@ -139,6 +140,17 @@ extern "C" {
# include <win32_utils.h>
#endif
// This KDE3-era HACK ensures that TDEIO jobs are properly terminated in the
// applyChanges() slots.
void tqt_enter_modal( TQWidget *widget );
void tqt_leave_modal( TQWidget *widget );
#define WAIT_FOR_JOB \
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); \
tqt_enter_modal(&dummy); \
tqApp->enter_loop(); \
tqt_leave_modal(&dummy);
#define JOB_DONE tqApp->exit_loop();
static TQString nameFromFileName(TQString nameStr)
{
if ( nameStr.endsWith(".desktop") )
@ -387,6 +399,7 @@ bool KPropertiesDialog::canDisplay( KFileItemList _items )
KBindingPropsPlugin::supports( _items ) ||
KURLPropsPlugin::supports( _items ) ||
KDevicePropsPlugin::supports( _items ) ||
TDEAttrPropsPlugin::supports(_items) ||
KFileMetaPropsPlugin::supports( _items ) ||
KPreviewPropsPlugin::supports( _items );
}
@ -486,6 +499,12 @@ void KPropertiesDialog::insertPages()
insertPlugin (p);
}
if (TDEAttrPropsPlugin::supports(m_items))
{
TDEAttrPropsPlugin *p = new TDEAttrPropsPlugin(this);
insertPlugin(p);
}
if ( KFileMetaPropsPlugin::supports( m_items ) )
{
KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
@ -1324,10 +1343,6 @@ bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
return true;
}
// Don't do this at home
void tqt_enter_modal( TQWidget *widget );
void tqt_leave_modal( TQWidget *widget );
void KFilePropsPlugin::applyChanges()
{
if ( d->dirSizeJob ) {
@ -1383,11 +1398,7 @@ void KFilePropsPlugin::applyChanges()
TQ_SLOT( slotCopyFinished( TDEIO::Job * ) ) );
connect( job, TQ_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ),
TQ_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) );
// wait for job
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
WAIT_FOR_JOB;
return;
}
properties->updateUrl(properties->kurl());
@ -1406,8 +1417,7 @@ void KFilePropsPlugin::slotCopyFinished( TDEIO::Job * job )
kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
if (job)
{
// allow apply() to return
tqApp->exit_loop();
JOB_DONE;
if ( job->error() )
{
job->showErrorDialog( d->m_frame );
@ -2555,11 +2565,7 @@ void KFilePermissionsPropsPlugin::applyChanges()
connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) );
// Wait for job
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
WAIT_FOR_JOB;
}
if (dirs.count() > 0) {
job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
@ -2571,11 +2577,7 @@ void KFilePermissionsPropsPlugin::applyChanges()
connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) );
// Wait for job
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
WAIT_FOR_JOB;
}
}
@ -2584,8 +2586,7 @@ void KFilePermissionsPropsPlugin::slotChmodResult( TDEIO::Job * job )
kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
if (job->error())
job->showErrorDialog( d->m_frame );
// allow apply() to return
tqApp->exit_loop();
JOB_DONE
}
@ -2861,6 +2862,450 @@ void KBindingPropsPlugin::applyChanges()
config.sync();
}
/* ----------------------------------------------------
*
* TDEAttrPropsPlugin
*
* -------------------------------------------------- */
class TDEAttrPropsPlugin::TDEAttrEntry : public TQHBox
{
friend class TDEAttrPropsPlugin;
public:
TDEAttrEntry(TQWidget *parent)
: TQHBox(parent)
{
m_combo = new TQComboBox(this);
m_combo->setEditable(true);
m_combo->setDuplicatesEnabled(false);
m_combo->lineEdit()->setMaxLength(255);
m_combo->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed);
m_value = new TQLineEdit(this);
m_value->setMaxLength(255);
m_value->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed);
m_delete = new TQPushButton(this);
m_delete->setPixmap(SmallIcon("edittrash"));
m_delete->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Expanding);
setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed);
}
TQCString key() { return m_combo->currentText().local8Bit(); }
void setKey(const TQCString& key)
{
m_combo->setCurrentText(TQString::fromLocal8Bit(key));
}
TQCString value() { return m_value->text().local8Bit(); }
void setValue(const TQCString& value)
{
m_value->setText(TQString::fromLocal8Bit(value));
}
void setReadOnly(bool ro)
{
m_combo->setDisabled(ro);
m_value->setReadOnly(ro);
m_delete->setDisabled(ro);
}
~TDEAttrEntry() {}
private:
TQComboBox *m_combo;
TQLineEdit *m_value;
TQPushButton *m_delete;
};
class TDEAttrPropsPlugin::TDEAttrPropsPluginPrivate
{
public:
TDEAttrPropsPluginPrivate() { m_entryWidgets.setAutoDelete(true); }
~TDEAttrPropsPluginPrivate() {}
TQStringList m_savedAttrs;
TQFrame *m_frame;
TQVBox *m_entriesBox;
TQHBox *m_buttonsBox;
TQLabel *m_label;
TQPushButton *m_newEntry, *m_editSavedAttrs;
TQMap<TQCString, TQCString> m_entries;
TQPtrList<TDEAttrEntry> m_entryWidgets, m_pendingWidgets;
};
TDEAttrPropsPlugin::TDEAttrPropsPlugin(KPropertiesDialog *_props)
: KPropsDlgPlugin(_props)
{
d = new TDEAttrPropsPluginPrivate;
d->m_frame = properties->addPage(i18n("A&ttributes"));
d->m_entriesBox = new TQVBox(d->m_frame);
d->m_buttonsBox = new TQHBox(d->m_frame);
d->m_label = new TQLabel(d->m_frame);
d->m_label->setText(i18n("Loading attributes information..."));
d->m_newEntry = new TQPushButton(
SmallIcon("add"),
i18n("Add attribute"),
d->m_buttonsBox
);
connect(d->m_newEntry, TQ_SIGNAL(clicked()),
this, TQ_SLOT(slotAddEntry()));
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
d->m_newEntry->setEnabled(false);
}
d->m_editSavedAttrs = new TQPushButton(
i18n("Saved attributes..."),
d->m_buttonsBox
);
connect(d->m_editSavedAttrs, TQ_SIGNAL(clicked()),
this, TQ_SLOT(slotEditSavedAttrs()));
TQVBoxLayout *layout = new TQVBoxLayout(d->m_frame);
layout->addWidget(d->m_entriesBox);
layout->addWidget(d->m_label);
layout->addStretch();
layout->addWidget(d->m_buttonsBox);
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
d->m_savedAttrs = config->readListEntry("SavedAttributes");
TDEIO::AttributeJob *listJob = TDEIO::listAttr(_props->kurl(), true);
connect(listJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotListJobResult(TDEIO::Job*)));
connect(this, TQ_SIGNAL(changed()), this, TQ_SLOT(slotCheckNoAttrs()));
}
TDEAttrPropsPlugin::~TDEAttrPropsPlugin()
{
delete d;
}
TDEAttrPropsPlugin::TDEAttrEntry *TDEAttrPropsPlugin::addEntry()
{
TDEAttrEntry *entryWidget = new TDEAttrEntry(d->m_entriesBox);
d->m_entryWidgets.append(entryWidget);
entryWidget->m_combo->insertStringList(d->m_savedAttrs);
entryWidget->m_combo->setCurrentText(TQString::null);
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
entryWidget->setReadOnly(true);
}
entryWidget->show();
return entryWidget;
}
void TDEAttrPropsPlugin::connectEntry(TDEAttrEntry *entry)
{
connect(entry->m_combo, TQ_SIGNAL(textChanged(const TQString&)),
this, TQ_SIGNAL(changed()));
connect(entry->m_value, TQ_SIGNAL(textChanged(const TQString&)),
this, TQ_SIGNAL(changed()));
connect(entry->m_delete, TQ_SIGNAL(clicked()),
this, TQ_SLOT(slotConfirmDelete()));
}
void TDEAttrPropsPlugin::slotAddEntry()
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
TDEAttrEntry *entryWidget = addEntry();
connectEntry(entryWidget);
entryWidget->m_combo->lineEdit()->setFocus();
emit changed();
}
void TDEAttrPropsPlugin::slotEditSavedAttrs()
{
KDialogBase *dlg = new KDialogBase(
properties,
"TDE Saved Attributes",
true,
i18n("Saved attributes"),
KDialogBase::Ok | KDialogBase::Cancel,
KDialogBase::Ok
);
KEditListBox *elb = new KEditListBox(dlg);
elb->setTitle(i18n("Edit saved attributes"));
elb->upButton()->hide();
elb->downButton()->hide();
elb->insertStringList(d->m_savedAttrs);
connect(dlg, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotApplySavedAttrs()));
dlg->setMainWidget(elb);
dlg->exec();
}
void TDEAttrPropsPlugin::slotApplySavedAttrs()
{
KDialogBase *dlg = static_cast<KDialogBase*>(const_cast<TQObject*>(TQObject::sender()));
Q_ASSERT(dlg);
KEditListBox *elb = static_cast<KEditListBox*>(dlg->mainWidget());
Q_ASSERT(elb);
d->m_savedAttrs = elb->items();
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
config->writeEntry("SavedAttributes", d->m_savedAttrs);
config->sync();
dlg->deleteLater();
// Update comboboxes immediately
TDEAttrEntry *entry;
for (entry = d->m_entryWidgets.first(); entry;
entry = d->m_entryWidgets.next())
{
TQString currentText = entry->m_combo->currentText();
entry->m_combo->clear();
entry->m_combo->insertStringList(d->m_savedAttrs);
entry->m_combo->setCurrentText(currentText);
}
}
void TDEAttrPropsPlugin::slotConfirmDelete()
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
const TQObject *sender = TQObject::sender();
TQWidget *button = static_cast<TQWidget*>(const_cast<TQObject*>(sender));
Q_ASSERT(button);
TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(button->parentWidget());
Q_ASSERT(entry);
TQCString attr = entry->key();
bool remove = false;
if (attr.isEmpty() && entry->value().isEmpty())
{
remove = true;
}
else
{
TQString msg;
if (!attr.isEmpty())
{
msg = i18n("<qt>Are you sure you want to delete attribute <b>%1</b>?<br>"
"Note that this won't actually take effect until you press \"Ok\".</qt>"
).arg(TQString::fromLocal8Bit(attr));
}
else
{
msg = i18n("<qt>Are you sure you want to delete this unnamed attribute?</qt>");
}
int result = KMessageBox::warningYesNo(properties, msg, i18n("Remove attribute?"));
remove = (result == KMessageBox::Yes);
}
if (remove)
{
d->m_entryWidgets.remove(entry);
emit changed();
}
}
void TDEAttrPropsPlugin::slotCheckNoAttrs()
{
if (d->m_entryWidgets.count() == 0)
{
d->m_label->show();
d->m_label->setText(i18n("This file has no attributes."));
}
else d->m_label->hide();
}
void TDEAttrPropsPlugin::slotListJobResult(TDEIO::Job *job)
{
if (job->error())
{
job->showErrorDialog();
d->m_label->setText(i18n("Error reading attributes information!"));
}
else
{
TDEIO::AttributeJob *listJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(listJob);
TQValueList<TQCString> attrs = listJob->attributes();
TQValueList<TQCString>::iterator it;
for (it = attrs.begin(); it != attrs.end(); ++it)
{
TDEAttrEntry *entryWidget = addEntry();
entryWidget->setKey(*it);
d->m_pendingWidgets.append(entryWidget);
TDEIO::AttributeJob *readJob = TDEIO::readAttr(properties->kurl(),
(*it), false);
connect(readJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotReadJobResult(TDEIO::Job*)));
}
slotCheckNoAttrs();
}
}
void TDEAttrPropsPlugin::slotReadJobResult(TDEIO::Job *job)
{
if (job->error())
{
job->showErrorDialog();
}
else
{
TDEIO::AttributeJob *readJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(readJob);
TQCString attr = readJob->attribute(),
val = readJob->value();
TDEAttrEntry *entryWidget;
for (entryWidget = d->m_pendingWidgets.first(); entryWidget;
entryWidget = d->m_pendingWidgets.next())
{
if (entryWidget->key() == attr)
{
entryWidget->setValue(val);
d->m_pendingWidgets.remove(entryWidget);
d->m_entries[attr] = val;
if (KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
entryWidget->setReadOnly(false);
connectEntry(entryWidget);
}
d->m_pendingWidgets.remove(entryWidget);
d->m_entries[attr] = val;
return;
}
}
kdWarning() << "attribute widget not found: " << attr << endl;
}
}
void TDEAttrPropsPlugin::slotWriteJobResult(TDEIO::Job *job)
{
if (job->error())
{
job->showErrorDialog();
}
else
{
TDEIO::AttributeJob *writeJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(writeJob);
d->m_entries[writeJob->attribute()] = writeJob->value();
}
JOB_DONE;
}
void TDEAttrPropsPlugin::slotRemoveJobResult(TDEIO::Job *job)
{
if (job->error())
{
job->showErrorDialog();
}
else
{
TDEIO::AttributeJob *removeJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(removeJob);
d->m_entries.remove(removeJob->attribute());
}
JOB_DONE;
}
void TDEAttrPropsPlugin::applyChanges()
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
TQStringList existingKeys;
TDEIO::AttributeJob *writeJob;
// First do a validation
TDEAttrEntry *entry;
for (entry = d->m_entryWidgets.first(); entry;
entry = d->m_entryWidgets.next())
{
if (entry->key().isEmpty())
{
KMessageBox::sorry(properties,
i18n("There is an attribute without a name."));
properties->abortApplying();
return;
}
if (existingKeys.contains(entry->key()))
{
KMessageBox::sorry(properties,
i18n("Duplicate attribute '%1'.")
.arg(TQString::fromLocal8Bit(entry->key())));
properties->abortApplying();
return;
}
existingKeys << entry->key();
}
// Then actually apply the settings
for (entry = d->m_entryWidgets.first(); entry;
entry = d->m_entryWidgets.next())
{
// Ignore if the attribute value is unchanged
if (d->m_entries.contains(entry->key()) &&
d->m_entries[entry->key()] == entry->value())
{
continue;
}
writeJob = TDEIO::writeAttr(properties->kurl(), entry->key(), entry->value(), true);
connect(writeJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotWriteJobResult(TDEIO::Job*)));
WAIT_FOR_JOB;
}
TQMap<TQCString, TQCString> entries(d->m_entries);
TQMap<TQCString, TQCString>::Iterator it;
for (it = entries.begin(); it != entries.end(); ++it)
{
if (!existingKeys.contains(it.key()))
{
TDEIO::AttributeJob *rmJob = TDEIO::removeAttr(properties->kurl(), it.key(), true);
connect(rmJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotRemoveJobResult(TDEIO::Job*)));
WAIT_FOR_JOB;
}
}
}
bool TDEAttrPropsPlugin::supports(KFileItemList _items)
{
if (_items.count() != 1)
{
return false;
}
KFileItem *item = _items.first();
if (item->isDir() || !KProtocolInfo::supportsReadingAttrs(item->url()))
{
return false;
}
return true;
}
/* ----------------------------------------------------
*
* KDevicePropsPlugin

@ -63,7 +63,7 @@ namespace TDEIO { class Job; }
*
* This class must be created with (void)new KPropertiesDialog(...)
* It will take care of deleting itself.
*
*
* If you are looking for more flexibility, see KFileMetaInfo and
* KFileMetaInfoWidget.
*/
@ -82,9 +82,9 @@ public:
static bool canDisplay( KFileItemList _items );
/**
* Brings up a Properties dialog, as shown above.
* Brings up a Properties dialog, as shown above.
* This is the normal constructor for
* file-manager type applications, where you have a KFileItem instance
* file-manager type applications, where you have a KFileItem instance
* to work with. Normally you will use this
* method rather than the one below.
*
@ -192,40 +192,40 @@ public:
virtual ~KPropertiesDialog();
/**
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p item points to a local file, native (non modal) property
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p item points to a local file, native (non modal) property
* dialog is displayed (@p parent and @p modal are ignored in this case).
*
*
* @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4
*/
static bool showDialog(KFileItem* item, TQWidget* parent = 0,
static bool showDialog(KFileItem* item, TQWidget* parent = 0,
const char* name = 0, bool modal = false);
/**
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p _url points to a local file, native (non modal) property
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p _url points to a local file, native (non modal) property
* dialog is displayed (@p parent and @p modal are ignored in this case).
*
*
* @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4
*/
static bool showDialog(const KURL& _url, TQWidget* parent = 0,
static bool showDialog(const KURL& _url, TQWidget* parent = 0,
const char* name = 0, bool modal = false);
/**
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p _items has one element and this element points
* to a local file, native (non modal) property dialog is displayed
* Immediately displays a Properties dialog using constructor with
* the same parameters.
* On MS Windows, if @p _items has one element and this element points
* to a local file, native (non modal) property dialog is displayed
* (@p parent and @p modal are ignored in this case).
*
*
* @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4
*/
static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0,
static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0,
const char* name = 0, bool modal = false);
/**
@ -244,7 +244,7 @@ public:
void insertPlugin (KPropsDlgPlugin *plugin);
/**
* The URL of the file that has its properties being displayed.
* The URL of the file that has its properties being displayed.
* This is only valid if the KPropertiesDialog was created/shown
* for one file or URL.
*
@ -323,7 +323,7 @@ public:
* @since 3.1
*/
void showFileSharingPage();
/**
* Sets the file sharing page.
* This page is shown when calling showFileSharingPage().
@ -715,6 +715,43 @@ private:
KBindingPropsPluginPrivate *d;
};
/**
* Properties plugin for extended attributes
* @internal
*/
class TDEIO_EXPORT TDEAttrPropsPlugin : public KPropsDlgPlugin
{
TQ_OBJECT
public:
TDEAttrPropsPlugin(KPropertiesDialog *_props);
virtual ~TDEAttrPropsPlugin();
virtual void applyChanges();
static bool supports(KFileItemList _items);
private slots:
void slotListJobResult(TDEIO::Job *);
void slotReadJobResult(TDEIO::Job *);
void slotWriteJobResult(TDEIO::Job *);
void slotRemoveJobResult(TDEIO::Job *);
void slotAddEntry();
void slotEditSavedAttrs();
void slotApplySavedAttrs();
void slotConfirmDelete();
void slotCheckNoAttrs();
private:
class TDEAttrEntry;
class TDEAttrPropsPluginPrivate;
TDEAttrPropsPluginPrivate *d;
protected:
void updateComboBoxes();
TDEAttrEntry *addEntry();
void connectEntry(TDEAttrEntry *entry);
};
/**
* Properties plugin for device .desktop files
* @internal

@ -46,7 +46,7 @@ install( FILES
kfilterbase.h kfilterdev.h tdeemailsettings.h kscan.h
kdatatool.h karchive.h tdefilefilter.h tdefilemetainfo.h
renamedlgplugin.h kmimetyperesolver.h kdcopservicestarter.h
kremoteencoding.h kmimetypechooser.h kacl.h
kremoteencoding.h kmimetypechooser.h kacl.h tdexattr.h
DESTINATION ${INCLUDE_INSTALL_DIR} )
install( FILES
@ -77,7 +77,7 @@ set( ${target}_SRCS
kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp
observer.cpp ../misc/uiserver.stub observer.skel tdeemailsettings.cpp
kprotocolinfo.cpp renamedlg.cpp skipdlg.cpp kremoteencoding.cpp
kmimetypechooser.cpp
kmimetypechooser.cpp tdexattr.cpp
)
tde_add_library( ${target} STATIC_PIC AUTOMOC

@ -426,7 +426,7 @@ TDEIO_EXPORT TQString TDEIO::buildErrorString(int errorCode, const TQString &err
result = i18n( "Access to restricted port in POST denied.");
break;
case TDEIO::ERR_OFFLINE_MODE:
result = i18n( "Could not access %1.\nOffline mode active.").arg( errorText );
result = i18n( "Could not access %1.\nOffline mode active.").arg( errorText );
break;
default:
result = i18n( "Unknown error code %1\n%2\nPlease send a full bug report at http://bugs.trinitydesktop.org." ).arg( errorCode ).arg( errorText );
@ -466,6 +466,11 @@ TDEIO_EXPORT TQString TDEIO::unsupportedActionErrorString(const TQString &protoc
return i18n("Creating folders is not supported with protocol %1.").arg(protocol);
case CMD_CHMOD:
return i18n("Changing the attributes of files is not supported with protocol %1.").arg(protocol);
case CMD_LISTATTR:
case CMD_READATTR:
case CMD_WRITEATTR:
case CMD_REMOVEATTR:
return i18n("Extended attributes are not supported with protocol %1.").arg(protocol);
case CMD_SUBURL:
return i18n("Using sub-URLs with %1 is not supported.").arg(protocol);
case CMD_MULTI_GET:
@ -1361,10 +1366,10 @@ extern "C" void endvfsent( );
# endif
#endif
#ifdef __CYGWIN__
#define hasmntopt(var,opt) (0)
#endif
#ifdef __CYGWIN__
#define hasmntopt(var,opt) (0)
#endif
// There are (at least) four kind of APIs:
// setmntent + getmntent + struct mntent (linux...)
// getmntent + struct mnttab
@ -1949,7 +1954,7 @@ TQString TDEIO::findPathMountPoint(const TQString& filename)
return get_mount_info(filename, isautofs, isslow, ismanual, fstype);
#else //!Q_OS_UNIX
return TQString::null;
#endif
#endif
}
bool TDEIO::manually_mounted(const TQString& filename)
@ -1961,7 +1966,7 @@ bool TDEIO::manually_mounted(const TQString& filename)
return !mountPoint.isNull() && (ismanual == Right);
#else //!Q_OS_UNIX
return false;
#endif
#endif
}
bool TDEIO::probably_slow_mounted(const TQString& filename)
@ -1973,7 +1978,7 @@ bool TDEIO::probably_slow_mounted(const TQString& filename)
return !mountPoint.isNull() && (isslow == Right);
#else //!Q_OS_UNIX
return false;
#endif
#endif
}
bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag)
@ -1995,7 +2000,7 @@ bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag)
case CaseInsensitive:
return isMsDos;
}
#endif
#endif
return false;
}

@ -164,7 +164,11 @@ namespace TDEIO
CMD_RESUMEANSWER = 'T', // 84
CMD_CONFIG = 'U', // 85
CMD_MULTI_GET = 'V', // 86
CMD_LOCALURL = 'W' // 87
CMD_LOCALURL = 'W', // 87
CMD_LISTATTR = 'X', // 88
CMD_READATTR = 'Y', // 89
CMD_WRITEATTR = 'Z', // 90
CMD_REMOVEATTR = '[' // 91
// Add new ones here once a release is done, to avoid breaking binary compatibility.
// Note that protocol-specific commands shouldn't be added here, but should use special.
};
@ -247,7 +251,7 @@ namespace TDEIO
// the server in order to continue.
ERR_POST_DENIED = 65, // Issued when trying to POST data to a certain Ports
// see job.cpp
ERR_OFFLINE_MODE = 66 // Used when an app is in offline mode and a
ERR_OFFLINE_MODE = 66 // Used when an app is in offline mode and a
// requested document is unavailable.
};
@ -350,7 +354,7 @@ namespace TDEIO
/// @since 3.5
UDS_DEFAULT_ACL_STRING = 104 | UDS_STRING,
// available: 112, 120
// available: 112, 120
/// Access permissions (part of the mode returned by stat)
UDS_ACCESS = 128 | UDS_LONG,

@ -837,6 +837,109 @@ SimpleJob *TDEIO::unmount( const TQString& point, bool showProgressInfo )
return job;
}
////////////
class AttributeJob::AttributeJobPrivate
{
public:
AttributeJobPrivate() {}
// The extended attributes of a file, result of the LISTATTR command
TQValueList<TQCString> m_attributes;
// The value of an attribute, result of the READATTR command or the value
// passed to WRITEATTR command
TQCString m_value;
// The current attribute
TQCString m_attribute;
};
AttributeJob::AttributeJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo)
: TransferJob(url, command, packedArgs, TQByteArray(), showProgressInfo)
{
d = new AttributeJobPrivate;
// Store a copy of some arguments so that they can be retrieved later from
// a job result slot.
TQByteArray args(packedArgs);
args.detach();
TQDataStream stream(packedArgs, IO_ReadOnly);
KURL _url;
stream >> _url;
if (command != CMD_LISTATTR)
{
stream >> d->m_attribute;
if (command == CMD_WRITEATTR)
{
stream >> d->m_value;
}
}
}
AttributeJob::~AttributeJob()
{
delete d;
}
void AttributeJob::start(Slave *slave)
{
TransferJob::start(slave);
}
void AttributeJob::slotData(const TQByteArray &data)
{
if (command() == CMD_LISTATTR)
{
TQCString attr;
TQDataStream stream(data, IO_ReadOnly);
stream >> attr;
d->m_attributes << attr;
}
else if (command() == CMD_READATTR)
{
TQDataStream stream(data, IO_ReadOnly);
stream >> d->m_value;
}
}
const TQValueList<TQCString> AttributeJob::attributes() { return d->m_attributes; }
TQCString AttributeJob::attribute() { return d->m_attribute; }
const TQCString AttributeJob::value() { return d->m_value; }
void AttributeJob::slotFinished()
{
// Return slave to the scheduler
TransferJob::slotFinished();
}
AttributeJob *TDEIO::listAttr(const KURL& url, bool showProgressInfo)
{
TDEIO_ARGS << url;
return new AttributeJob(url, CMD_LISTATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::readAttr(const KURL& url, const TQCString& attr, bool showProgressInfo)
{
TDEIO_ARGS << url << attr;
return new AttributeJob(url, CMD_READATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::writeAttr(const KURL& url, const TQCString& attr, const TQCString& val, bool showProgressInfo)
{
TDEIO_ARGS << url << attr << val;
return new AttributeJob(url, CMD_WRITEATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::removeAttr(const KURL& url, const TQCString& attr, bool showProgressInfo)
{
TDEIO_ARGS << url << attr;
return new AttributeJob(url, CMD_REMOVEATTR, packedArgs, showProgressInfo);
}
//////////
LocalURLJob::LocalURLJob( const KURL& url, int command,
const TQByteArray &packedArgs, bool showProgressInfo )
@ -4825,6 +4928,9 @@ void MultiGetJob::virtual_hook( int id, void* data )
void MimetypeJob::virtual_hook( int id, void* data )
{ TransferJob::virtual_hook( id, data ); }
void AttributeJob::virtual_hook( int id, void* data )
{ TransferJob::virtual_hook( id, data ); }
void FileCopyJob::virtual_hook( int id, void* data )
{ Job::virtual_hook( id, data ); }

@ -127,6 +127,58 @@ namespace TDEIO {
*/
TDEIO_EXPORT SimpleJob *unmount( const TQString & point, bool showProgressInfo = true );
/**
* List extended attributes.
*
* Currently only used by @p tdeio_file.
*
* @param url file URL
* @param showProgressInfo true to show progress information
* @return the job handling the operation.
* @since R14.1.5
*/
TDEIO_EXPORT AttributeJob *listAttr( const KURL& url, bool showProgressInfo = true );
/**
* Read an extended attribute.
*
* Currently only used by @p tdeio_file.
*
* @param url file URL
* @param attr attribute name
* @param showProgressInfo true to show progress information
* @return the job handling the operation.
* @since R14.1.5
*/
TDEIO_EXPORT AttributeJob *readAttr( const KURL& url, const TQCString& attr, bool showProgressInfo = true );
/**
* Write an extended attribute.
*
* Currently only used by @p tdeio_file.
*
* @param url file URL
* @param attr attribute name
* @param val attribute value
* @param showProgressInfo true to show progress information
* @return the job handling the operation.
* @since R14.1.5
*/
TDEIO_EXPORT AttributeJob *writeAttr( const KURL& url, const TQCString& attr, const TQCString& val, bool showProgressInfo = true );
/**
* Remove an extended attribute.
*
* Currently only used by @p tdeio_file.
*
* @param url file URL
* @param attr attribute name
* @param showProgressInfo true to show progress information
* @return the job handling the operation.
* @since R14.1.5
*/
TDEIO_EXPORT AttributeJob *removeAttr( const KURL& url, const TQCString& attr, bool showProgressInfo = true );
/**
* Retrieve local URL if available
*

@ -1241,6 +1241,72 @@ namespace TDEIO {
class MimetypeJobPrivate* d;
};
/**
* An AttributeJob is a TransferJob that allows you to query and modify
* the extended attributes of an URL. Don't create directly, but use
* TDEIO::listAttr(), TDEIO::readAttr(), TDEIO::writeAttr() or
* TDEIO::removeAttr() instead.
* @see TDEIO::listAttr(), TDEIO::readAttr(), TDEIO::writeAttr(), TDEIO::removeAttr()
*/
class TDEIO_EXPORT AttributeJob : public TransferJob {
TQ_OBJECT
public:
/**
* Do not create an AttributeJob directly. Use TDEIO::listAttr(),
* TDEIO::readAttr(), TDEIO::writeAttr() or TDEIO::removeAttr() instead.
* @param url the url to get
* @param command the command to issue
* @param packedArgs the arguments
* @param showProgressInfo true to show progress information to the user
*/
AttributeJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo);
~AttributeJob();
/**
* This function contains the result of a successful listAttr() operation.
* If an error occured, an empty list is returned instead.
* @return list of extended attributes of the file
*/
const TQValueList<TQCString> attributes();
/** This function stores the attribute name that this job is related to
* unless the current operation is listAttr(). Useful if you request
* multiple attribute values but have one handler slot.
* @return attribute name as passed to readAttr(), writeAttr() or removeAttr()
*/
TQCString attribute();
/**
* This function contains the result of a successful readAttr() operation.
* If an error occured, an empty TQCString is returned instead.
*
* Alternatively, in case of a writeAttr() operation, the function always
* returns the value that was passed to the writeAttr() function.
*
* @return result of successful readAttr() operation or value passed to writeAttr() operation
*/
const TQCString value();
/**
* @internal
* Called by the scheduler when a slave gets to work on this job.
* @param slave the slave that works on the job
*/
virtual void start( Slave *slave );
protected slots:
virtual void slotData(const TQByteArray &data);
virtual void slotFinished();
protected:
virtual void virtual_hook(int id, void* data);
private:
class AttributeJobPrivate;
AttributeJobPrivate *d;
};
/**
* The FileCopyJob copies data from one place to another.
* @see TDEIO::file_copy()

@ -190,6 +190,24 @@ bool KProtocolInfo::supportsMoving( const KURL &url )
return prot->m_supportsMoving;
}
bool KProtocolInfo::supportsReadingAttrs( const KURL &url )
{
KProtocolInfo::Ptr prot = findProtocol(url);
if ( !prot )
return false;
return prot->m_supportsReadingAttrs;
}
bool KProtocolInfo::supportsWritingAttrs( const KURL &url )
{
KProtocolInfo::Ptr prot = findProtocol(url);
if ( !prot )
return false;
return prot->m_supportsWritingAttrs;
}
bool KProtocolInfo::canCopyFromFile( const KURL &url )
{
KProtocolInfo::Ptr prot = findProtocol(url);

@ -343,6 +343,30 @@ public:
*/
static bool supportsMoving( const KURL &url );
/**
* Returns whether the protocol supports reading attributes.
*
* This corresponds to the "readattr=" field in the protocol description file.
* Valid values for this field are "true" or "false" (default).
*
* @param url the url to check
* @return true if the protocol supports reading attributes
* @since R14.1.5
*/
static bool supportsReadingAttrs( const KURL &url );
/**
* Returns whether the protocol supports reading attributes.
*
* This corresponds to the "writeattr=" field in the protocol description file.
* Valid values for this field are "true" or "false" (default).
*
* @param url the url to check
* @return true if the protocol supports reading attributes
* @since R14.1.5
*/
static bool supportsWritingAttrs( const KURL &url );
/**
* Returns whether the protocol can copy files/objects directly from the
* filesystem itself. If not, the application will read files from the
@ -661,6 +685,8 @@ protected:
bool m_supportsDeleting;
bool m_supportsLinking;
bool m_supportsMoving;
bool m_supportsReadingAttrs;
bool m_supportsWritingAttrs;
TQString m_defaultMimetype;
bool m_determineMimetypeFromExtension;
TQString m_icon;

@ -90,7 +90,7 @@ return false; }
KEntryMap internalEntryMap() const { return KEntryMap(); }
void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup)
void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup)
{ Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
KEntry lookupData(const KEntryKey &_key) const
@ -800,6 +800,14 @@ void SlaveBase::mkdir(KURL const &, int)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); }
void SlaveBase::chmod(KURL const &, int)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); }
void SlaveBase::listAttr(KURL const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTATTR)); }
void SlaveBase::readAttr(KURL const &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_READATTR)); }
void SlaveBase::writeAttr(KURL const &, TQCString const &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_WRITEATTR)); }
void SlaveBase::removeAttr(KURL const &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_REMOVEATTR)); }
void SlaveBase::setSubURL(KURL const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); }
void SlaveBase::multiGet(const TQByteArray &)
@ -1134,6 +1142,33 @@ void SlaveBase::dispatch( int command, const TQByteArray &data )
stream >> url >> i;
chmod( url, i);
break;
case CMD_LISTATTR:
{
stream >> url;
listAttr(url);
break;
}
case CMD_READATTR:
{
TQCString attr;
stream >> url >> attr;
readAttr(url, attr);
break;
}
case CMD_WRITEATTR:
{
TQCString attr, val;
stream >> url >> attr >> val;
writeAttr(url, attr, val);
break;
}
case CMD_REMOVEATTR:
{
TQCString attr;
stream >> url >> attr;
removeAttr(url, attr);
break;
}
case CMD_SPECIAL:
special( data );
break;

@ -461,6 +461,38 @@ public:
*/
virtual void chmod( const KURL& url, int permissions );
/**
* List extended attributes of @p path
* @param url file URL
* @since R14.1.5
*/
virtual void listAttr(const KURL& url);
/**
* Reads extended attribute @p attr of @p path
* @param url file URL
* @param attr attribute name
* @since R14.1.5
*/
virtual void readAttr(const KURL& url, const TQCString& attr);
/**
* Sets the value of extended attribute @p attr of @p path to @p val
* @param url file URL
* @param attr attribute name
* @param val attribute value
* @since R14.1.5
*/
virtual void writeAttr(const KURL& url, const TQCString& attr, const TQCString& val);
/**
* Removes extended attribute @p attr from @p path
* @param url file URL
* @param attr attribute name
* @since R14.1.5
*/
virtual void removeAttr(const KURL& url, const TQCString& attr);
/**
* Copy @p src into @p dest.
* If the slave returns an error ERR_UNSUPPORTED_ACTION, the job will

@ -0,0 +1,355 @@
/*******************************************************************************
Extended attributes support for TDE
Copyright © 2025 Philippe Mavridis <philippe.mavridis@yandex.com>
Based on xattr_p.h from KFileMetaData
Copyright © 2014 Raphael Kubo da Costa <rakuco@FreeBSD.org>
This program or library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*******************************************************************************/
#include <sys/types.h>
#include <errno.h>
#if defined(Q_OS_LINUX) || defined(__GLIBC__)
# include <sys/xattr.h>
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
# include <sys/extattr.h>
#endif
#include <tqfile.h>
#include "tdexattr.h"
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
# define CHECK_URL(url) \
if (!url.isValid() || url.isEmpty()) return TDEIO::ERR_MALFORMED_URL; \
if (!url.isLocalFile()) return 0; // silently ignore non-local urls
# define GET_ENCODED_PATH(url) \
const TQByteArray p = TQFile::encodeName(url.path()); \
const char* encodedPath = p.data();
#if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
static TQValueList<TQCString> // FIXME untested
splitLengthValue(TQByteArray data)
{
uint pos = 0;
TQValueList<TQCString> entries;
char *s;
while (pos < data.size())
{
uchar len = data[pos];
if (pos + 1 + len <= data.size())
{
strncat(s, &data[pos + 1], len);
entries.append(TQCString(s, len));
}
pos += 1 + len;
}
return entries;
}
#else
static TQValueList<TQCString>
splitZeroSeparator(TQByteArray data)
{
uint pos = 0;
TQValueList<TQCString> entries;
while (pos < data.size())
{
const char *c = &data[pos];
size_t len = strlen(c);
entries.append(TQCString(c));
pos += len + 1;
}
return entries;
}
#endif
uint
TDEXAttr::list(const KURL& url, TQValueList<TQCString> *list)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
#if defined(Q_OS_LINUX)
const ssize_t size = listxattr(encodedPath, nullptr, 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t size = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, nullptr, 0);
#endif
if (size == 0)
{
return 0;
}
else if (size == -1)
{
return parseError(errno, TDEIO::CMD_LISTATTR);
}
else
{
TQByteArray data(size);
for (;;)
{
#if defined(Q_OS_LINUX)
const ssize_t r = listxattr(encodedPath, data.data(), data.size());
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t r = extattr_list_file(encodedPath, EXTATTR_NAMESPACE_USER, data.data(), data.size());
#endif
if (r == 0) {
list->clear();
return 0;
}
if (r == -1 && errno != ERANGE)
{
list->clear();
return parseError(errno, TDEIO::CMD_LISTATTR);
}
if (r > 0)
{
data.resize(r);
break;
}
else // ERANGE
{
data.resize(data.size() * 2);
}
}
#if defined(Q_OS_LINUX)
const TQValueList<TQCString> entries = splitZeroSeparator(data);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const TQValueList<TQCString> entries = splitLengthValue(data);
#endif
list->clear();
TQValueList<TQCString>::const_iterator it;
for (it = entries.begin(); it != entries.end(); ++it)
{
TQCString attribute(*it);
#if defined(Q_OS_LINUX)
if (attribute.left(5) != "user.")
{
continue;
}
#endif
*list << getVisibleAttributeName(attribute);
}
return 0;
}
}
uint
TDEXAttr::read(const KURL& url, const TQCString& attribute, TQCString *value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
const ssize_t size = getxattr(encodedPath, attr, nullptr, 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t size = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, NULL, 0);
#endif
if (size == -1)
{
return parseError(errno, TDEIO::CMD_READATTR);
}
else if (size == 0)
{
*value = "";
return 0;
}
else
{
TQByteArray data(size);
for (;;)
{
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
const ssize_t r = getxattr(encodedPath, attr, data.data(), data.size());
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
const ssize_t r = extattr_get_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, data.data(), data.size());
#endif
if (r == -1 && errno != ERANGE)
{
*value = "";
return parseError(errno, TDEIO::CMD_READATTR);
}
else if (r == 0)
{
*value = "";
return 0;
}
else if (r >= 0)
{
*value = TQCString(data, r + 1);
return 0;
}
else // ERANGE
{
data.resize(data.size() * 2);
}
}
}
}
uint
TDEXAttr::write(const KURL& url, const TQCString& attribute, const TQCString& value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr))
r = setxattr(encodedPath, attr, value.data(), value.size(), 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
r = extattr_set_file(encodedPath, EXTATTR_NAMESPACE_USER, attr, value.data(), value.size());
#endif
return r == -1 ? parseError(errno, TDEIO::CMD_WRITEATTR) : 0;
}
uint
TDEXAttr::remove(const KURL& url, const TQCString& attribute)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
TQCString attr = getLocalAttributeName(attribute);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr))
r = removexattr(encodedPath, attr);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
r = extattr_delete_file (encodedPath, EXTATTR_NAMESPACE_USER, attr);
#else
return TDEIO::ERR_UNSUPPORTED_ACTION;
#endif
return r == -1 ? parseError(errno, TDEIO::CMD_REMOVEATTR) : 0;
}
bool
TDEXAttr::supported(const KURL& url)
{
return read(url, "test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION;
}
#else // stubs for unsupported platforms
uint
TDEXAttr::list(const KURL& url, TQValueList<TQCString> *list)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::read(const KURL& url, const TQCString& attr, TQCString *value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::write(const KURL& url, const TQCString& attr, const TQCString& value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
TDEIO::Error
TDEXAttr::remove(const KURL& url, const TQCString& attr)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
bool
TDEXAttr::supported(const KURL& url)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
#endif
uint
TDEXAttr::parseError(int _errno, int command)
{
switch (_errno)
{
case E2BIG:
case ENODATA:
{
return command == TDEIO::CMD_READATTR ?
TDEIO::ERR_COULD_NOT_READ :
TDEIO::ERR_COULD_NOT_WRITE;
}
case ENOTSUP:
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
case ERANGE:
{
return TDEIO::ERR_OUT_OF_MEMORY;
}
case EDQUOT:
case ENOSPC:
{
return TDEIO::ERR_DISK_FULL;
}
case EPERM:
{
return TDEIO::ERR_WRITE_ACCESS_DENIED;
}
default:
{
return TDEIO::ERR_INTERNAL;
}
}
}
TQCString
TDEXAttr::getLocalAttributeName(const TQCString& attr)
{
TQCString a(attr);
#if defined(Q_OS_LINUX)
a = a.prepend("user.");
#endif
return a;
}
TQCString
TDEXAttr::getVisibleAttributeName(const TQCString& attr)
{
TQCString a(attr);
#if defined(Q_OS_LINUX)
a = a.remove(0, 5);
#endif
return a;
}
// kate: replace-tabs true; tab-width 4;

@ -0,0 +1,96 @@
/*******************************************************************************
Extended attributes support for TDE
Copyright © 2025 Philippe Mavridis <philippe.mavridis@yandex.com>
Based on xattr_p.h from KFileMetaData
Copyright © 2014 Raphael Kubo da Costa <rakuco@FreeBSD.org>
This program or library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*******************************************************************************/
#ifndef __TDEXATTR_H
#define __TDEXATTR_H
#include <kurl.h>
#include <tdeio/global.h>
#include <tqvaluelist.h>
/**
* Extended attributes support class for TDE.
*
* This class implements extended attributes support for TDE. Static methods are
* provided which can list, read and write user-defined extended attributes of
* local files.
*
* @author Philippe Mavridis <philippe.mavridis@yandex.com>
*/
class TDEIO_EXPORT TDEXAttr
{
public:
/**
* List all the extended attributes of a file.
* @param url the URL of the file
* @param list pointer to a TQCString list where the list of attributes will be stored
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
*/
static uint list(const KURL& url, TQValueList<TQCString> *list);
/**
* Read the value of an extended attribute.
* @param url the URL of the file
* @param attr attribute name
* @param value pointer to TQCString where the value of the attribute @p attr will be stored
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
*/
static uint read(const KURL& url, const TQCString& attribute, TQCString *value);
/**
* Modify the value of an extended attribute.
*
* @param url the URL of the file
* @param attr attribute name
* @param value new value of the attribute @p attr
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
*/
static uint write(const KURL& url, const TQCString& attribute, const TQCString& value);
/**
* Remove an extended attribute.
*
* @param url the URL of the file
* @param attr attribute name
* @return the status of the operation (0 for success, enum TDEIO::Error otherwise)
*/
static uint remove(const KURL& url, const TQCString& attribute);
/**
* Check if extended attributes are supported for the given URL.
*
* @param url the URL of the file
* @return true if extended attributes are supported, false otherwise
*/
static bool supported(const KURL& url);
private:
static TQCString getLocalAttributeName(const TQCString& attr);
static TQCString getVisibleAttributeName(const TQCString& attr);
static uint parseError(int _errno, int command);
};
#endif // __TDEXATTR_H
// kate: replace-tabs true; tab-width 2;

@ -94,6 +94,7 @@
#include <klargefile.h>
#include <tdeglobal.h>
#include <kmimetype.h>
#include <tdexattr.h>
using namespace TDEIO;
@ -360,7 +361,7 @@ write_all(int fd, const char *buf, size_t len)
return 0;
}
static bool
static bool
same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
{
if (src.st_ino == dest.st_ino &&
@ -626,7 +627,7 @@ void FileProtocol::copy( const KURL &src, const KURL &dest,
return;
}
if ( same_inode( buff_dest, buff_src) )
if ( same_inode( buff_dest, buff_src) )
{
error( TDEIO::ERR_IDENTICAL_FILES, dest.path() );
return;
@ -839,7 +840,7 @@ void FileProtocol::rename( const KURL &src, const KURL &dest,
return;
}
if ( same_inode( buff_dest, buff_src) )
if ( same_inode( buff_dest, buff_src) )
{
error( TDEIO::ERR_IDENTICAL_FILES, dest.path() );
return;
@ -955,6 +956,59 @@ void FileProtocol::del( const KURL& url, bool isfile)
finished();
}
void FileProtocol::listAttr( const KURL& url )
{
TQValueList<TQCString> attrs;
int result = TDEXAttr::list(url, &attrs);
if (result != 0)
{
error(result, url.path());
}
TQValueList<TQCString>::iterator it;
for (it = attrs.begin(); it != attrs.end(); ++it)
{
TQByteArray d;
TQDataStream s(d, IO_WriteOnly);
s << (*it);
data(d);
}
finished();
}
void FileProtocol::readAttr( const KURL& url, const TQCString& attr )
{
TQCString value;
int result = TDEXAttr::read(url, attr, &value);
if (result != 0)
{
error(result, url.path());
}
TQByteArray d;
TQDataStream s(d, IO_WriteOnly);
s << value;
data(d);
finished();
}
void FileProtocol::writeAttr( const KURL& url, const TQCString& attr, const TQCString& value )
{
int result = TDEXAttr::write(url, attr, value);
if (result != 0)
{
error(result, url.path());
}
finished();
}
void FileProtocol::removeAttr( const KURL& url, const TQCString& attr )
{
int result = TDEXAttr::remove(url, attr);
if (result != 0)
{
error(result, url.path());
}
finished();
}
TQString FileProtocol::getUserName( uid_t uid )
{
@ -990,8 +1044,6 @@ TQString FileProtocol::getGroupName( gid_t gid )
return *temp;
}
bool FileProtocol::createUDSEntry( const TQString & filename, const TQCString & path, UDSEntry & entry,
short int details, bool withACL )
{

@ -62,6 +62,10 @@ public:
virtual void mkdir( const KURL& url, int permissions );
virtual void chmod( const KURL& url, int permissions );
virtual void del( const KURL& url, bool isfile);
virtual void listAttr( const KURL& url );
virtual void readAttr( const KURL& url, const TQCString& attr );
virtual void writeAttr( const KURL& url, const TQCString& attr, const TQCString& value );
virtual void removeAttr( const KURL& url, const TQCString& attr );
/**
* Special commands supported by this slave:
@ -81,10 +85,10 @@ protected slots:
protected:
bool createUDSEntry( const TQString & filename, const TQCString & path, TDEIO::UDSEntry & entry,
bool createUDSEntry( const TQString & filename, const TQCString & path, TDEIO::UDSEntry & entry,
short int details, bool withACL );
int setACL( const char *path, mode_t perm, bool _directoryDefault );
TQString getUserName( uid_t uid );
TQString getGroupName( gid_t gid );

@ -10,6 +10,8 @@ makedir=true
deleting=true
linking=true
moving=true
readattr=true
writeattr=true
maxInstances=4
X-DocPath=tdeioslave/file/index.html
Class=:local

Loading…
Cancel
Save