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 <tdestandarddirs.h>
#include <tdeglobal.h> #include <tdeglobal.h>
#include <tdeapplication.h> #include <tdeapplication.h>
#include <kdebug.h>
#include <ksimpleconfig.h> #include <ksimpleconfig.h>
#include <tdeconfig.h> #include <tdeconfig.h>
#include <kstringhandler.h> #include <kstringhandler.h>
@ -71,6 +70,8 @@ KProtocolInfo::KProtocolInfo(const TQString &path)
m_supportsDeleting = config.readBoolEntry( "deleting", false ); m_supportsDeleting = config.readBoolEntry( "deleting", false );
m_supportsLinking = config.readBoolEntry( "linking", false ); m_supportsLinking = config.readBoolEntry( "linking", false );
m_supportsMoving = config.readBoolEntry( "moving", 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_canCopyFromFile = config.readBoolEntry( "copyFromFile", false );
m_canCopyToFile = config.readBoolEntry( "copyToFile", false ); m_canCopyToFile = config.readBoolEntry( "copyToFile", false );
d->canRenameFromFile = config.readBoolEntry( "renameFromFile", false ); d->canRenameFromFile = config.readBoolEntry( "renameFromFile", false );
@ -159,7 +160,8 @@ KProtocolInfo::load( TQDataStream& _str)
i_supportsMoving, i_determineMimetypeFromExtension, i_supportsMoving, i_determineMimetypeFromExtension,
i_canCopyFromFile, i_canCopyToFile, i_showPreviews, i_canCopyFromFile, i_canCopyToFile, i_showPreviews,
i_uriMode, i_canRenameFromFile, i_canRenameToFile, 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 _str >> m_name >> m_exec >> m_listing >> m_defaultMimetype
>> i_determineMimetypeFromExtension >> i_determineMimetypeFromExtension
@ -175,7 +177,8 @@ KProtocolInfo::load( TQDataStream& _str)
>> d->extraFields >> i_showPreviews >> i_uriMode >> d->extraFields >> i_showPreviews >> i_uriMode
>> d->capabilities >> d->proxyProtocol >> d->capabilities >> d->proxyProtocol
>> i_canRenameFromFile >> i_canRenameToFile >> i_canRenameFromFile >> i_canRenameToFile
>> i_canDeleteRecursive >> i_fileNameUsedForCopying; >> i_canDeleteRecursive >> i_fileNameUsedForCopying
>> i_supportsReadingAttrs >> i_supportsWritingAttrs;
m_inputType = (Type) i_inputType; m_inputType = (Type) i_inputType;
m_outputType = (Type) i_outputType; m_outputType = (Type) i_outputType;
@ -188,6 +191,8 @@ KProtocolInfo::load( TQDataStream& _str)
m_supportsDeleting = (i_supportsDeleting != 0); m_supportsDeleting = (i_supportsDeleting != 0);
m_supportsLinking = (i_supportsLinking != 0); m_supportsLinking = (i_supportsLinking != 0);
m_supportsMoving = (i_supportsMoving != 0); m_supportsMoving = (i_supportsMoving != 0);
m_supportsReadingAttrs = (i_supportsReadingAttrs != 0);
m_supportsWritingAttrs = (i_supportsWritingAttrs != 0);
m_canCopyFromFile = (i_canCopyFromFile != 0); m_canCopyFromFile = (i_canCopyFromFile != 0);
m_canCopyToFile = (i_canCopyToFile != 0); m_canCopyToFile = (i_canCopyToFile != 0);
d->canRenameFromFile = (i_canRenameFromFile != 0); d->canRenameFromFile = (i_canRenameFromFile != 0);
@ -214,7 +219,8 @@ KProtocolInfo::save( TQDataStream& _str)
i_supportsMoving, i_determineMimetypeFromExtension, i_supportsMoving, i_determineMimetypeFromExtension,
i_canCopyFromFile, i_canCopyToFile, i_showPreviews, i_canCopyFromFile, i_canCopyToFile, i_showPreviews,
i_uriMode, i_canRenameFromFile, i_canRenameToFile, 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_inputType = (TQ_INT32) m_inputType;
i_outputType = (TQ_INT32) m_outputType; i_outputType = (TQ_INT32) m_outputType;
@ -227,6 +233,8 @@ KProtocolInfo::save( TQDataStream& _str)
i_supportsDeleting = m_supportsDeleting ? 1 : 0; i_supportsDeleting = m_supportsDeleting ? 1 : 0;
i_supportsLinking = m_supportsLinking ? 1 : 0; i_supportsLinking = m_supportsLinking ? 1 : 0;
i_supportsMoving = m_supportsMoving ? 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_canCopyFromFile = m_canCopyFromFile ? 1 : 0;
i_canCopyToFile = m_canCopyToFile ? 1 : 0; i_canCopyToFile = m_canCopyToFile ? 1 : 0;
i_canRenameFromFile = d->canRenameFromFile ? 1 : 0; i_canRenameFromFile = d->canRenameFromFile ? 1 : 0;
@ -251,7 +259,8 @@ KProtocolInfo::save( TQDataStream& _str)
<< d->extraFields << i_showPreviews << i_uriMode << d->extraFields << i_showPreviews << i_uriMode
<< d->capabilities << d->proxyProtocol << d->capabilities << d->proxyProtocol
<< i_canRenameFromFile << i_canRenameToFile << 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 <krun.h>
#include <tdelistview.h> #include <tdelistview.h>
#include <kacl.h> #include <kacl.h>
#include <kprotocolinfo.h>
#include "tdefilesharedlg.h" #include "tdefilesharedlg.h"
#include "kpropertiesdesktopbase.h" #include "kpropertiesdesktopbase.h"
@ -139,6 +140,17 @@ extern "C" {
# include <win32_utils.h> # include <win32_utils.h>
#endif #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) static TQString nameFromFileName(TQString nameStr)
{ {
if ( nameStr.endsWith(".desktop") ) if ( nameStr.endsWith(".desktop") )
@ -387,6 +399,7 @@ bool KPropertiesDialog::canDisplay( KFileItemList _items )
KBindingPropsPlugin::supports( _items ) || KBindingPropsPlugin::supports( _items ) ||
KURLPropsPlugin::supports( _items ) || KURLPropsPlugin::supports( _items ) ||
KDevicePropsPlugin::supports( _items ) || KDevicePropsPlugin::supports( _items ) ||
TDEAttrPropsPlugin::supports(_items) ||
KFileMetaPropsPlugin::supports( _items ) || KFileMetaPropsPlugin::supports( _items ) ||
KPreviewPropsPlugin::supports( _items ); KPreviewPropsPlugin::supports( _items );
} }
@ -486,6 +499,12 @@ void KPropertiesDialog::insertPages()
insertPlugin (p); insertPlugin (p);
} }
if (TDEAttrPropsPlugin::supports(m_items))
{
TDEAttrPropsPlugin *p = new TDEAttrPropsPlugin(this);
insertPlugin(p);
}
if ( KFileMetaPropsPlugin::supports( m_items ) ) if ( KFileMetaPropsPlugin::supports( m_items ) )
{ {
KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this ); KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
@ -1324,10 +1343,6 @@ bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
return true; return true;
} }
// Don't do this at home
void tqt_enter_modal( TQWidget *widget );
void tqt_leave_modal( TQWidget *widget );
void KFilePropsPlugin::applyChanges() void KFilePropsPlugin::applyChanges()
{ {
if ( d->dirSizeJob ) { if ( d->dirSizeJob ) {
@ -1383,11 +1398,7 @@ void KFilePropsPlugin::applyChanges()
TQ_SLOT( slotCopyFinished( TDEIO::Job * ) ) ); TQ_SLOT( slotCopyFinished( TDEIO::Job * ) ) );
connect( job, TQ_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ), connect( job, TQ_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ),
TQ_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) ); TQ_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) );
// wait for job WAIT_FOR_JOB;
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
return; return;
} }
properties->updateUrl(properties->kurl()); properties->updateUrl(properties->kurl());
@ -1406,8 +1417,7 @@ void KFilePropsPlugin::slotCopyFinished( TDEIO::Job * job )
kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl; kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
if (job) if (job)
{ {
// allow apply() to return JOB_DONE;
tqApp->exit_loop();
if ( job->error() ) if ( job->error() )
{ {
job->showErrorDialog( d->m_frame ); job->showErrorDialog( d->m_frame );
@ -2555,11 +2565,7 @@ void KFilePermissionsPropsPlugin::applyChanges()
connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) );
// Wait for job WAIT_FOR_JOB;
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
} }
if (dirs.count() > 0) { if (dirs.count() > 0) {
job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions, job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
@ -2571,11 +2577,7 @@ void KFilePermissionsPropsPlugin::applyChanges()
connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ),
TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) );
// Wait for job WAIT_FOR_JOB;
TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal));
tqt_enter_modal(&dummy);
tqApp->enter_loop();
tqt_leave_modal(&dummy);
} }
} }
@ -2584,8 +2586,7 @@ void KFilePermissionsPropsPlugin::slotChmodResult( TDEIO::Job * job )
kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl; kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
if (job->error()) if (job->error())
job->showErrorDialog( d->m_frame ); job->showErrorDialog( d->m_frame );
// allow apply() to return JOB_DONE
tqApp->exit_loop();
} }
@ -2861,6 +2862,450 @@ void KBindingPropsPlugin::applyChanges()
config.sync(); 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 * KDevicePropsPlugin

@ -63,7 +63,7 @@ namespace TDEIO { class Job; }
* *
* This class must be created with (void)new KPropertiesDialog(...) * This class must be created with (void)new KPropertiesDialog(...)
* It will take care of deleting itself. * It will take care of deleting itself.
* *
* If you are looking for more flexibility, see KFileMetaInfo and * If you are looking for more flexibility, see KFileMetaInfo and
* KFileMetaInfoWidget. * KFileMetaInfoWidget.
*/ */
@ -82,9 +82,9 @@ public:
static bool canDisplay( KFileItemList _items ); 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 * 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 * to work with. Normally you will use this
* method rather than the one below. * method rather than the one below.
* *
@ -192,40 +192,40 @@ public:
virtual ~KPropertiesDialog(); virtual ~KPropertiesDialog();
/** /**
* Immediately displays a Properties dialog using constructor with * Immediately displays a Properties dialog using constructor with
* the same parameters. * the same parameters.
* On MS Windows, if @p item points to a local file, native (non modal) property * 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). * dialog is displayed (@p parent and @p modal are ignored in this case).
* *
* @return true on succesfull dialog displaying (can be false on win32). * @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4 * @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); const char* name = 0, bool modal = false);
/** /**
* Immediately displays a Properties dialog using constructor with * Immediately displays a Properties dialog using constructor with
* the same parameters. * the same parameters.
* On MS Windows, if @p _url points to a local file, native (non modal) property * 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). * dialog is displayed (@p parent and @p modal are ignored in this case).
* *
* @return true on succesfull dialog displaying (can be false on win32). * @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4 * @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); const char* name = 0, bool modal = false);
/** /**
* Immediately displays a Properties dialog using constructor with * Immediately displays a Properties dialog using constructor with
* the same parameters. * the same parameters.
* On MS Windows, if @p _items has one element and this element points * On MS Windows, if @p _items has one element and this element points
* to a local file, native (non modal) property dialog is displayed * to a local file, native (non modal) property dialog is displayed
* (@p parent and @p modal are ignored in this case). * (@p parent and @p modal are ignored in this case).
* *
* @return true on succesfull dialog displaying (can be false on win32). * @return true on succesfull dialog displaying (can be false on win32).
* @since 3.4 * @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); const char* name = 0, bool modal = false);
/** /**
@ -244,7 +244,7 @@ public:
void insertPlugin (KPropsDlgPlugin *plugin); 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 * This is only valid if the KPropertiesDialog was created/shown
* for one file or URL. * for one file or URL.
* *
@ -323,7 +323,7 @@ public:
* @since 3.1 * @since 3.1
*/ */
void showFileSharingPage(); void showFileSharingPage();
/** /**
* Sets the file sharing page. * Sets the file sharing page.
* This page is shown when calling showFileSharingPage(). * This page is shown when calling showFileSharingPage().
@ -715,6 +715,43 @@ private:
KBindingPropsPluginPrivate *d; 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 * Properties plugin for device .desktop files
* @internal * @internal

@ -46,7 +46,7 @@ install( FILES
kfilterbase.h kfilterdev.h tdeemailsettings.h kscan.h kfilterbase.h kfilterdev.h tdeemailsettings.h kscan.h
kdatatool.h karchive.h tdefilefilter.h tdefilemetainfo.h kdatatool.h karchive.h tdefilefilter.h tdefilemetainfo.h
renamedlgplugin.h kmimetyperesolver.h kdcopservicestarter.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} ) DESTINATION ${INCLUDE_INSTALL_DIR} )
install( FILES install( FILES
@ -77,7 +77,7 @@ set( ${target}_SRCS
kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp
observer.cpp ../misc/uiserver.stub observer.skel tdeemailsettings.cpp observer.cpp ../misc/uiserver.stub observer.skel tdeemailsettings.cpp
kprotocolinfo.cpp renamedlg.cpp skipdlg.cpp kremoteencoding.cpp kprotocolinfo.cpp renamedlg.cpp skipdlg.cpp kremoteencoding.cpp
kmimetypechooser.cpp kmimetypechooser.cpp tdexattr.cpp
) )
tde_add_library( ${target} STATIC_PIC AUTOMOC 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."); result = i18n( "Access to restricted port in POST denied.");
break; break;
case TDEIO::ERR_OFFLINE_MODE: 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; break;
default: default:
result = i18n( "Unknown error code %1\n%2\nPlease send a full bug report at http://bugs.trinitydesktop.org." ).arg( errorCode ).arg( errorText ); 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); return i18n("Creating folders is not supported with protocol %1.").arg(protocol);
case CMD_CHMOD: case CMD_CHMOD:
return i18n("Changing the attributes of files is not supported with protocol %1.").arg(protocol); 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: case CMD_SUBURL:
return i18n("Using sub-URLs with %1 is not supported.").arg(protocol); return i18n("Using sub-URLs with %1 is not supported.").arg(protocol);
case CMD_MULTI_GET: case CMD_MULTI_GET:
@ -1361,10 +1366,10 @@ extern "C" void endvfsent( );
# endif # endif
#endif #endif
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#define hasmntopt(var,opt) (0) #define hasmntopt(var,opt) (0)
#endif #endif
// There are (at least) four kind of APIs: // There are (at least) four kind of APIs:
// setmntent + getmntent + struct mntent (linux...) // setmntent + getmntent + struct mntent (linux...)
// getmntent + struct mnttab // getmntent + struct mnttab
@ -1949,7 +1954,7 @@ TQString TDEIO::findPathMountPoint(const TQString& filename)
return get_mount_info(filename, isautofs, isslow, ismanual, fstype); return get_mount_info(filename, isautofs, isslow, ismanual, fstype);
#else //!Q_OS_UNIX #else //!Q_OS_UNIX
return TQString::null; return TQString::null;
#endif #endif
} }
bool TDEIO::manually_mounted(const TQString& filename) bool TDEIO::manually_mounted(const TQString& filename)
@ -1961,7 +1966,7 @@ bool TDEIO::manually_mounted(const TQString& filename)
return !mountPoint.isNull() && (ismanual == Right); return !mountPoint.isNull() && (ismanual == Right);
#else //!Q_OS_UNIX #else //!Q_OS_UNIX
return false; return false;
#endif #endif
} }
bool TDEIO::probably_slow_mounted(const TQString& filename) 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); return !mountPoint.isNull() && (isslow == Right);
#else //!Q_OS_UNIX #else //!Q_OS_UNIX
return false; return false;
#endif #endif
} }
bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag) bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag)
@ -1995,7 +2000,7 @@ bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag)
case CaseInsensitive: case CaseInsensitive:
return isMsDos; return isMsDos;
} }
#endif #endif
return false; return false;
} }

@ -164,7 +164,11 @@ namespace TDEIO
CMD_RESUMEANSWER = 'T', // 84 CMD_RESUMEANSWER = 'T', // 84
CMD_CONFIG = 'U', // 85 CMD_CONFIG = 'U', // 85
CMD_MULTI_GET = 'V', // 86 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. // 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. // 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. // the server in order to continue.
ERR_POST_DENIED = 65, // Issued when trying to POST data to a certain Ports ERR_POST_DENIED = 65, // Issued when trying to POST data to a certain Ports
// see job.cpp // 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. // requested document is unavailable.
}; };
@ -350,7 +354,7 @@ namespace TDEIO
/// @since 3.5 /// @since 3.5
UDS_DEFAULT_ACL_STRING = 104 | UDS_STRING, UDS_DEFAULT_ACL_STRING = 104 | UDS_STRING,
// available: 112, 120 // available: 112, 120
/// Access permissions (part of the mode returned by stat) /// Access permissions (part of the mode returned by stat)
UDS_ACCESS = 128 | UDS_LONG, UDS_ACCESS = 128 | UDS_LONG,

@ -837,6 +837,109 @@ SimpleJob *TDEIO::unmount( const TQString& point, bool showProgressInfo )
return job; 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, LocalURLJob::LocalURLJob( const KURL& url, int command,
const TQByteArray &packedArgs, bool showProgressInfo ) 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 ) void MimetypeJob::virtual_hook( int id, void* data )
{ TransferJob::virtual_hook( id, 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 ) void FileCopyJob::virtual_hook( int id, void* data )
{ Job::virtual_hook( id, data ); } { Job::virtual_hook( id, data ); }

@ -127,6 +127,58 @@ namespace TDEIO {
*/ */
TDEIO_EXPORT SimpleJob *unmount( const TQString & point, bool showProgressInfo = true ); 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 * Retrieve local URL if available
* *

@ -1241,6 +1241,72 @@ namespace TDEIO {
class MimetypeJobPrivate* d; 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. * The FileCopyJob copies data from one place to another.
* @see TDEIO::file_copy() * @see TDEIO::file_copy()

@ -190,6 +190,24 @@ bool KProtocolInfo::supportsMoving( const KURL &url )
return prot->m_supportsMoving; 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 ) bool KProtocolInfo::canCopyFromFile( const KURL &url )
{ {
KProtocolInfo::Ptr prot = findProtocol(url); KProtocolInfo::Ptr prot = findProtocol(url);

@ -343,6 +343,30 @@ public:
*/ */
static bool supportsMoving( const KURL &url ); 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 * Returns whether the protocol can copy files/objects directly from the
* filesystem itself. If not, the application will read files from the * filesystem itself. If not, the application will read files from the
@ -661,6 +685,8 @@ protected:
bool m_supportsDeleting; bool m_supportsDeleting;
bool m_supportsLinking; bool m_supportsLinking;
bool m_supportsMoving; bool m_supportsMoving;
bool m_supportsReadingAttrs;
bool m_supportsWritingAttrs;
TQString m_defaultMimetype; TQString m_defaultMimetype;
bool m_determineMimetypeFromExtension; bool m_determineMimetypeFromExtension;
TQString m_icon; TQString m_icon;

@ -90,7 +90,7 @@ return false; }
KEntryMap internalEntryMap() const { return KEntryMap(); } 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); } { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
KEntry lookupData(const KEntryKey &_key) const KEntry lookupData(const KEntryKey &_key) const
@ -800,6 +800,14 @@ void SlaveBase::mkdir(KURL const &, int)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); } { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); }
void SlaveBase::chmod(KURL const &, int) void SlaveBase::chmod(KURL const &, int)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); } { 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 &) void SlaveBase::setSubURL(KURL const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); } { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); }
void SlaveBase::multiGet(const TQByteArray &) void SlaveBase::multiGet(const TQByteArray &)
@ -1134,6 +1142,33 @@ void SlaveBase::dispatch( int command, const TQByteArray &data )
stream >> url >> i; stream >> url >> i;
chmod( url, i); chmod( url, i);
break; 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: case CMD_SPECIAL:
special( data ); special( data );
break; break;

@ -461,6 +461,38 @@ public:
*/ */
virtual void chmod( const KURL& url, int permissions ); 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. * Copy @p src into @p dest.
* If the slave returns an error ERR_UNSUPPORTED_ACTION, the job will * 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 <klargefile.h>
#include <tdeglobal.h> #include <tdeglobal.h>
#include <kmimetype.h> #include <kmimetype.h>
#include <tdexattr.h>
using namespace TDEIO; using namespace TDEIO;
@ -360,7 +361,7 @@ write_all(int fd, const char *buf, size_t len)
return 0; return 0;
} }
static bool static bool
same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest) same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest)
{ {
if (src.st_ino == dest.st_ino && if (src.st_ino == dest.st_ino &&
@ -626,7 +627,7 @@ void FileProtocol::copy( const KURL &src, const KURL &dest,
return; return;
} }
if ( same_inode( buff_dest, buff_src) ) if ( same_inode( buff_dest, buff_src) )
{ {
error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); error( TDEIO::ERR_IDENTICAL_FILES, dest.path() );
return; return;
@ -839,7 +840,7 @@ void FileProtocol::rename( const KURL &src, const KURL &dest,
return; return;
} }
if ( same_inode( buff_dest, buff_src) ) if ( same_inode( buff_dest, buff_src) )
{ {
error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); error( TDEIO::ERR_IDENTICAL_FILES, dest.path() );
return; return;
@ -955,6 +956,59 @@ void FileProtocol::del( const KURL& url, bool isfile)
finished(); 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 ) TQString FileProtocol::getUserName( uid_t uid )
{ {
@ -990,8 +1044,6 @@ TQString FileProtocol::getGroupName( gid_t gid )
return *temp; return *temp;
} }
bool FileProtocol::createUDSEntry( const TQString & filename, const TQCString & path, UDSEntry & entry, bool FileProtocol::createUDSEntry( const TQString & filename, const TQCString & path, UDSEntry & entry,
short int details, bool withACL ) short int details, bool withACL )
{ {

@ -62,6 +62,10 @@ public:
virtual void mkdir( const KURL& url, int permissions ); virtual void mkdir( const KURL& url, int permissions );
virtual void chmod( const KURL& url, int permissions ); virtual void chmod( const KURL& url, int permissions );
virtual void del( const KURL& url, bool isfile); 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: * Special commands supported by this slave:
@ -81,10 +85,10 @@ protected slots:
protected: 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 ); short int details, bool withACL );
int setACL( const char *path, mode_t perm, bool _directoryDefault ); int setACL( const char *path, mode_t perm, bool _directoryDefault );
TQString getUserName( uid_t uid ); TQString getUserName( uid_t uid );
TQString getGroupName( gid_t gid ); TQString getGroupName( gid_t gid );

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

Loading…
Cancel
Save