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 3 weeks ago
parent 2e76346c68
commit a6ea4bf998
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,9 @@ extern "C" {
#include <krun.h> #include <krun.h>
#include <tdelistview.h> #include <tdelistview.h>
#include <kacl.h> #include <kacl.h>
#include <kprotocolinfo.h>
#include <ktabwidget.h>
#include <tdeaccel.h>
#include "tdefilesharedlg.h" #include "tdefilesharedlg.h"
#include "kpropertiesdesktopbase.h" #include "kpropertiesdesktopbase.h"
@ -139,6 +142,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 +401,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 +501,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 +1345,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 +1400,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 +1419,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 +2567,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 +2579,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 +2588,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 +2864,745 @@ void KBindingPropsPlugin::applyChanges()
config.sync(); config.sync();
} }
/* ----------------------------------------------------
*
* TDEAttrPropsPlugin
*
* -------------------------------------------------- */
class TDEAttrPropsPlugin::TDEAttrEntry : public TQHBox
{
friend class TDEAttrPropsPlugin;
public:
TDEAttrEntry(TQWidget *parent)
: TQHBox(parent, "tdeattrentry")
{
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->setFocusPolicy(TQWidget::NoFocus);
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::TDEAttrNamespaceTab : public TQWidget
{
friend class TDEAttrPropsPlugin;
public:
TDEAttrNamespaceTab(TQWidget *parent, TQCString ns)
: TQWidget(parent), m_namespace(ns), m_queryPending(true)
{
m_layout = new TQVBoxLayout(this);
m_entriesLayout = new TQVBoxLayout;
m_label = new TQLabel(i18n("Querying attributes..."), this);
m_layout->setSpacing(KDialog::spacingHint());
m_layout->setMargin(KDialog::marginHint());
m_layout->addWidget(m_label);
m_layout->addLayout(m_entriesLayout);
m_layout->addStretch();
// Read saved attributes
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
TQString key = "SavedAttributes_" + ns;
m_savedAttrs = config->readListEntry(key);
m_entryWidgets.setAutoDelete(true);
}
private:
bool m_queryPending;
TQPtrList<TDEAttrEntry> m_entryWidgets, m_pendingWidgets;
TQCString m_namespace;
TQVBoxLayout *m_layout, *m_entriesLayout;
TQMap<TQCString, TQCString> m_entries;
TQLabel *m_label;
TQStringList m_savedAttrs;
};
class TDEAttrPropsPlugin::TDEAttrPropsPluginPrivate
{
public:
TDEAttrPropsPluginPrivate() {}
~TDEAttrPropsPluginPrivate() {}
TQFrame *m_frame, *m_tab;
KTabWidget *m_tabWidget;
TQMap<TQCString, TDEAttrNamespaceTab*> m_tabs;
TQHBox *m_buttonsBox;
TQPushButton *m_newEntry, *m_editSavedAttrs, *m_preferences;
bool m_showSystemNs;
TDEAccel *m_accel;
};
TDEAttrPropsPlugin::TDEAttrPropsPlugin(KPropertiesDialog *_props)
: KPropsDlgPlugin(_props)
{
// Initialise tabs
d = new TDEAttrPropsPluginPrivate;
d->m_frame = properties->addPage(i18n("A&ttributes"));
d->m_tabWidget = new KTabWidget(d->m_frame);
d->m_tabWidget->setTabShape(TQTabWidget::Triangular);
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
d->m_showSystemNs = config->readBoolEntry("ShowSystemNs");
TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateTabs()));
// Initialise button box
d->m_buttonsBox = new TQHBox(d->m_frame);
d->m_buttonsBox->setSpacing(KDialog::spacingHint());
d->m_buttonsBox->setMargin(KDialog::marginHint());
d->m_newEntry = new TQPushButton(
SmallIcon("add"),
i18n("Add attribute"),
d->m_buttonsBox
);
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
d->m_newEntry->setEnabled(false);
}
d->m_editSavedAttrs = new TQPushButton(
SmallIcon("document-save"),
i18n("Common attributes..."),
d->m_buttonsBox
);
d->m_preferences = new TQPushButton(
SmallIcon("configure"),
i18n("Preferences..."),
d->m_buttonsBox
);
// Initialise shortcuts
d->m_accel = new TDEAccel(d->m_frame);
d->m_accel->insert(
"add", i18n("Add attribute"),
i18n("Add a new attribute entry to the current namespace"),
TDEShortcut("Insert"), this, TQ_SLOT(slotAddEntry())
);
d->m_accel->insert(
"del", i18n("Remove attribute"),
i18n("Remove currently edited attribute entry"),
TDEShortcut("Alt+Delete"), this, TQ_SLOT(slotDelEntry())
);
// Create module layout
TQVBoxLayout *layout = new TQVBoxLayout(d->m_frame);
layout->addWidget(d->m_tabWidget);
layout->addWidget(d->m_buttonsBox);
// Connect signals
connect(d->m_newEntry, TQ_SIGNAL(clicked()), TQ_SLOT(slotAddEntry()));
connect(d->m_editSavedAttrs, TQ_SIGNAL(clicked()), TQ_SLOT(slotEditSavedAttrs()));
connect(d->m_preferences, TQ_SIGNAL(clicked()), TQ_SLOT(slotPreferences()));
connect(this, TQ_SIGNAL(changed()), TQ_SLOT(slotCheckNoAttrs()));
connect(d->m_tabWidget, TQ_SIGNAL(currentChanged(TQWidget*)),
TQ_SLOT(slotTabChanged()));
}
TDEAttrPropsPlugin::~TDEAttrPropsPlugin()
{
delete d;
}
void
TDEAttrPropsPlugin::slotUpdateTabs()
{
TQString tip;
tip = i18n(
"<qt><b>User attributes</b> may be assigned to files and directories "
"for storing arbitrary additional information.</qt>"
);
updateTab("user", i18n("User"), tip);
#if defined(Q_OS_LINUX)
tip = i18n(
"<qt><p><b>System attributes</b> are used by the kernel to store "
"system objects such as Access Control Lists."
"<p>Read and write access permissions to system attributes depend on "
"the policy implemented for each system attribute implemented by "
"filesystems in the kernel.</qt>"
);
updateTab("system", i18n("System"), tip);
tip = i18n(
"<qt><p><b>Security attributes</b> are used by kernel security modules "
"such as SELinux, and also to implement file capabilities."
"<p>Read and write access permissions to security attributes depend on "
"the policy implemented for each security attribute by the security "
"module. When no security module is loaded, all processes have read "
"access to extended security attributes, and write access is limited "
"to processes that have the CAP_SYS_ADMIN capability.</qt>"
);
updateTab("security", i18n("Security"), tip);
tip = i18n(
"<qt><p><b>Trusted attributes</b> are visible and accessible only to "
"processes that have the CAP_SYS_ADMIN capability. Attributes in this "
"class are used to implement mechanisms in user space (i.e., outside "
"the kernel) which keep information in extended attributes to which "
"ordinary processes should not have access.</qt>"
);
updateTab("trusted", i18n("Trusted"), tip);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
// From extattr(9) (FIXME: possibly find a more informative source?)
tip = i18n(
"<qt><p><b>System attributes</b> are protected such that appropriate "
"privilege is required to directly access or manipulate them.</qt>"
);
updateTab("system", i18n("System"), tip);
#endif
d->m_tabWidget->setTabBarHidden(d->m_tabWidget->count() == 1);
if (d->m_tabWidget->count() == 1) slotTabChanged();
}
void
TDEAttrPropsPlugin::updateTab(TQCString ns, TQString label, TQString tip)
{
if (d->m_tabs.contains(ns))
{
if (ns != "user" && !d->m_showSystemNs)
{
d->m_tabWidget->removePage(d->m_tabs[ns]);
d->m_tabs.remove(ns);
}
return;
}
else
{
if (ns != "user" && !d->m_showSystemNs)
{
return;
}
TDEAttrNamespaceTab *tab = new TDEAttrNamespaceTab(d->m_tabWidget, ns);
d->m_tabWidget->addTab(tab, label);
d->m_tabWidget->setTabToolTip(tab, tip);
TQWhatsThis::add(tab, tip);
d->m_tabs[ns] = tab;
}
}
TDEAttrPropsPlugin::TDEAttrNamespaceTab*
TDEAttrPropsPlugin::currentNamespaceTab()
{
return static_cast<TDEAttrNamespaceTab*>(d->m_tabWidget->currentPage());
}
void TDEAttrPropsPlugin::slotTabChanged()
{
TDEAttrNamespaceTab *tab = currentNamespaceTab();
if (!tab->m_queryPending)
{
return;
}
slotCheckNoAttrs();
// Query attributes
KURL url = properties->kurl();
TDEIO::AttributeJob *listJob = TDEIO::listAttr(url, tab->m_namespace, true);
connect(listJob, TQ_SIGNAL(result(TDEIO::Job*)),
TQ_SLOT(slotListJobResult(TDEIO::Job*)));
}
TDEAttrPropsPlugin::TDEAttrEntry *TDEAttrPropsPlugin::addEntry(TDEAttrNamespaceTab *tab)
{
if (!tab) tab = currentNamespaceTab();
Q_ASSERT(tab);
TDEAttrEntry *entryWidget = new TDEAttrEntry(tab);
tab->m_entriesLayout->addWidget(entryWidget);
tab->m_entryWidgets.append(entryWidget);
entryWidget->m_combo->insertStringList(tab->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(slotDelButtonPressed()));
}
void TDEAttrPropsPlugin::slotAddEntry()
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
TDEAttrEntry *entryWidget = addEntry();
connectEntry(entryWidget);
entryWidget->m_combo->lineEdit()->setFocus();
emit changed();
}
void TDEAttrPropsPlugin::slotDelEntry()
{
TDEAttrNamespaceTab *tab = currentNamespaceTab();
if (tab->focusWidget() && tab->focusWidget()->parentWidget())
{
// From value lineedit
TQWidget *w = tab->focusWidget()->parentWidget();
if (qstrcmp(w->name(), "tdeattrentry") == 0)
{
TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(w);
delEntry(entry);
}
// From combobox lineedit
else if (qstrcmp(w->parentWidget()->name(), "tdeattrentry") == 0)
{
TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(w->parentWidget());
delEntry(entry);
}
}
}
void TDEAttrPropsPlugin::slotEditSavedAttrs()
{
TDEAttrNamespaceTab *tab = currentNamespaceTab();
TQString caption = i18n("Common attributes [namespace: %1]")
.arg(tab->m_namespace);
KDialogBase *dlg = new KDialogBase(
properties,
"TDE Saved Attributes",
true,
caption,
KDialogBase::Ok | KDialogBase::Cancel,
KDialogBase::Ok
);
KEditListBox *elb = new KEditListBox(dlg);
elb->setTitle(caption);
elb->insertStringList(currentNamespaceTab()->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);
TDEAttrNamespaceTab *tab = currentNamespaceTab();
tab->m_savedAttrs = elb->items();
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
TQString key = "SavedAttributes_" + tab->m_namespace;
config->writeEntry(key, tab->m_savedAttrs);
config->sync();
dlg->deleteLater();
// Update comboboxes immediately
TDEAttrEntry *entry;
for (entry = tab->m_entryWidgets.first(); entry;
entry = tab->m_entryWidgets.next())
{
TQString currentText = entry->m_combo->currentText();
entry->m_combo->clear();
entry->m_combo->insertStringList(tab->m_savedAttrs);
entry->m_combo->setCurrentText(currentText);
}
}
void
TDEAttrPropsPlugin::delEntry(TDEAttrPropsPlugin::TDEAttrEntry *entry)
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
TQCString attr = entry->key();
bool del = false;
if (attr.isEmpty() && entry->value().isEmpty())
{
del = true;
}
else
{
TQString msg;
if (!attr.isEmpty())
{
msg = i18n("<qt>Are you sure you want to delete attribute <b>%1</b>?</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?"),
KStdGuiItem::yes(), KStdGuiItem::no(), "ConfirmRemoveAttribute");
del = (result == KMessageBox::Yes);
}
if (del)
{
TDEAttrNamespaceTab *tab = static_cast<TDEAttrNamespaceTab*>(entry->parentWidget());
Q_ASSERT(tab);
tab->m_entryWidgets.remove(entry);
if (tab->m_entryWidgets.count() > 0)
{
tab->m_entryWidgets.last()->m_combo->setFocus();
}
else
{
tab->setFocus();
}
emit changed();
}
}
void TDEAttrPropsPlugin::slotDelButtonPressed()
{
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);
delEntry(entry);
}
void TDEAttrPropsPlugin::slotCheckNoAttrs()
{
TDEAttrNamespaceTab *tab = currentNamespaceTab();
if (tab->m_entryWidgets.count() == 0)
{
tab->m_label->show();
tab->m_label->setText(i18n("No attributes or insufficient permissions."));
}
else tab->m_label->hide();
}
void TDEAttrPropsPlugin::slotListJobResult(TDEIO::Job *job)
{
TDEIO::AttributeJob *listJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(listJob);
const TQCString& ns = listJob->attributeNs();
TDEAttrNamespaceTab *tab = d->m_tabs[ns];
Q_ASSERT(tab);
if (job->error())
{
job->showErrorDialog();
tab->m_label->setText(i18n("Error reading attributes information!"));
}
else
{
TQValueList<TQCString> attrs = listJob->attributes();
TQValueList<TQCString>::iterator it;
for (it = attrs.begin(); it != attrs.end(); ++it)
{
TQCString attr(*it);
#if defined(Q_OS_LINUX)
if (attr.left(ns.length() + 1) == ns + ".")
{
attr = attr.mid(ns.length() + 1);
}
#endif
TDEAttrEntry *entryWidget = addEntry(tab);
entryWidget->setKey(attr);
tab->m_pendingWidgets.append(entryWidget);
TDEIO::AttributeJob *readJob = TDEIO::readAttr(properties->kurl(),
ns, attr, false);
connect(readJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotReadJobResult(TDEIO::Job*)));
}
slotCheckNoAttrs();
}
}
void TDEAttrPropsPlugin::slotReadJobResult(TDEIO::Job *job)
{
TDEIO::AttributeJob *readJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(readJob);
if (job->error())
{
job->showErrorDialog();
}
else
{
const TQCString& ns = readJob->attributeNs();
TDEAttrNamespaceTab *tab = d->m_tabs[ns];
Q_ASSERT(tab);
tab->m_queryPending = false;
const TQCString& attr = readJob->attribute(), val = readJob->value();
TDEAttrEntry *entryWidget;
for (entryWidget = tab->m_pendingWidgets.first(); entryWidget;
entryWidget = tab->m_pendingWidgets.next())
{
if (entryWidget->key() == attr)
{
entryWidget->setValue(val);
tab->m_pendingWidgets.remove(entryWidget);
tab->m_entries[attr] = val;
if (KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
entryWidget->setReadOnly(false);
connectEntry(entryWidget);
}
tab->m_pendingWidgets.remove(entryWidget);
tab->m_entries[attr] = val;
return;
}
}
kdWarning() << "attribute widget not found: " << ns << "." << attr << endl;
}
}
void TDEAttrPropsPlugin::slotWriteJobResult(TDEIO::Job *job)
{
TDEIO::AttributeJob *writeJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(writeJob);
if (job->error())
{
job->showErrorDialog();
}
else
{
TDEAttrNamespaceTab *tab = d->m_tabs[writeJob->attributeNs()];
Q_ASSERT(tab);
tab->m_entries[writeJob->attribute()] = writeJob->value();
}
JOB_DONE;
}
void TDEAttrPropsPlugin::slotRemoveJobResult(TDEIO::Job *job)
{
TDEIO::AttributeJob *removeJob = static_cast<TDEIO::AttributeJob*>(job);
Q_ASSERT(removeJob);
if (job->error())
{
job->showErrorDialog();
}
else
{
TDEAttrNamespaceTab *tab = d->m_tabs[removeJob->attributeNs()];
Q_ASSERT(tab);
tab->m_entries.remove(removeJob->attribute());
}
JOB_DONE;
}
void TDEAttrPropsPlugin::applyChanges()
{
if (!KProtocolInfo::supportsWritingAttrs(properties->kurl()))
{
return;
}
TDEIO::AttributeJob *writeJob;
TQMap<TQCString, TDEAttrNamespaceTab*>::Iterator it;
for (it = d->m_tabs.begin(); it != d->m_tabs.end(); ++it)
{
TDEAttrNamespaceTab *tab = (*it);
// First do a validation
TQStringList existingKeys;
TDEAttrEntry *entry;
for (entry = tab->m_entryWidgets.first(); entry;
entry = tab->m_entryWidgets.next())
{
if (entry->key().isEmpty() && !entry->value().isEmpty())
{
KMessageBox::sorry(properties,
i18n("There is an attribute without a name."));
properties->abortApplying();
return;
}
else 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 = tab->m_entryWidgets.first(); entry;
entry = tab->m_entryWidgets.next())
{
// Ignore empty fields
if (entry->key().isEmpty()) continue;
// Ignore if the attribute value is unchanged
if (tab->m_entries.contains(entry->key()) &&
tab->m_entries[entry->key()] == entry->value())
{
continue;
}
writeJob = TDEIO::writeAttr(properties->kurl(), tab->m_namespace,
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(tab->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(),
tab->m_namespace,
it.key(), true);
connect(rmJob, TQ_SIGNAL(result(TDEIO::Job*)),
this, TQ_SLOT(slotRemoveJobResult(TDEIO::Job*)));
WAIT_FOR_JOB;
}
}
}
}
void TDEAttrPropsPlugin::slotPreferences()
{
KDialogBase *dlg = new KDialogBase(
properties,
"TDE Attribute Properties Plugin Preferences",
true,
i18n("Preferences"),
KDialogBase::Ok | KDialogBase::Cancel,
KDialogBase::Ok
);
connect(dlg, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotApplyPreferences()));
TQVBox *vbox = dlg->makeVBoxMainWidget();
TQCheckBox *showSystemNs = new TQCheckBox(
i18n("Show &system attributes"),
vbox,
"showSystemNs"
);
showSystemNs->setChecked(d->m_showSystemNs);
dlg->exec();
}
void TDEAttrPropsPlugin::slotApplyPreferences()
{
KDialogBase *dlg = static_cast<KDialogBase*>(const_cast<TQObject*>(TQObject::sender()));
Q_ASSERT(dlg);
TQCheckBox *showSystemNs = static_cast<TQCheckBox*>(dlg->child("showSystemNs", "TQCheckBox"));
Q_ASSERT(showSystemNs);
d->m_showSystemNs = showSystemNs->isChecked();
TDEConfig *config = new TDEConfig("kdeglobals");
config->setGroup("TDE Extended Attributes");
config->writeEntry("ShowSystemNs", d->m_showSystemNs);
config->sync();
TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateTabs()));
}
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,52 @@ 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 slotDelEntry();
void slotDelButtonPressed();
void slotEditSavedAttrs();
void slotApplySavedAttrs();
void slotCheckNoAttrs();
void slotTabChanged();
void slotPreferences();
void slotApplyPreferences();
void slotUpdateTabs();
private:
class TDEAttrEntry;
class TDEAttrNamespaceTab;
class TDEAttrPropsPluginPrivate;
TDEAttrPropsPluginPrivate *d;
protected:
void updateTab(TQCString ns, TQString label, TQString tip);
TDEAttrNamespaceTab* currentNamespaceTab();
void updateComboBoxes();
TDEAttrEntry *addEntry(TDEAttrNamespaceTab *tab = nullptr);
void delEntry(TDEAttrPropsPlugin::TDEAttrEntry *entry);
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,120 @@ 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 namespace
TQCString m_namespace;
// 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;
stream >> d->m_namespace;
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::attributeNs() { return d->m_namespace; }
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, const TQCString& ns,
bool showProgressInfo)
{
TDEIO_ARGS << url << ns;
return new AttributeJob(url, CMD_LISTATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::readAttr(const KURL& url, const TQCString& ns,
const TQCString& attr, bool showProgressInfo)
{
TDEIO_ARGS << url << ns << attr;
return new AttributeJob(url, CMD_READATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::writeAttr(const KURL& url, const TQCString& ns,
const TQCString& attr, const TQCString& val,
bool showProgressInfo)
{
TDEIO_ARGS << url << ns << attr << val;
return new AttributeJob(url, CMD_WRITEATTR, packedArgs, showProgressInfo);
}
AttributeJob *TDEIO::removeAttr(const KURL& url, const TQCString& ns,
const TQCString& attr, bool showProgressInfo)
{
TDEIO_ARGS << url << ns << 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 +4939,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,66 @@ 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, const TQCString &ns,
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 &ns,
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 &ns,
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 &ns,
const TQCString& attr,
bool showProgressInfo = true );
/** /**
* Retrieve local URL if available * Retrieve local URL if available
* *

@ -1241,6 +1241,79 @@ 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 namespace that this job is related to.
* Useful if you request multiple attribute values but have one handler
* slot.
* @return namespace as passed to listAttr(), readAttr(), writeAttr() or removeAttr()
*/
TQCString attributeNs();
/** 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 &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTATTR)); }
void SlaveBase::readAttr(KURL const &, TQCString const &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_READATTR)); }
void SlaveBase::writeAttr(KURL const &, TQCString const &, TQCString const &, TQCString const &)
{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_WRITEATTR)); }
void SlaveBase::removeAttr(KURL const &, TQCString 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,34 @@ 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:
{
TQCString ns;
stream >> url >> ns;
listAttr(url, ns);
break;
}
case CMD_READATTR:
{
TQCString ns, attr;
stream >> url >> ns >> attr;
readAttr(url, ns, attr);
break;
}
case CMD_WRITEATTR:
{
TQCString ns, attr, val;
stream >> url >> ns >> attr >> val;
writeAttr(url, ns, attr, val);
break;
}
case CMD_REMOVEATTR:
{
TQCString ns, attr;
stream >> url >> ns >> attr;
removeAttr(url, ns, attr);
break;
}
case CMD_SPECIAL: case CMD_SPECIAL:
special( data ); special( data );
break; break;

@ -461,6 +461,45 @@ 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
* @param ns attribute namespace (usually "user")
* @since R14.1.5
*/
virtual void listAttr(const KURL& url, const TQCString& ns);
/**
* Reads extended attribute @p attr of @p path
* @param url file URL
* @param ns attribute namespace (usually "user")
* @param attr attribute name
* @since R14.1.5
*/
virtual void readAttr(const KURL& url, const TQCString& ns,
const TQCString& attr);
/**
* Sets the value of extended attribute @p attr of @p path to @p val
* @param url file URL
* @param ns attribute namespace (usually "user")
* @param attr attribute name
* @param val attribute value
* @since R14.1.5
*/
virtual void writeAttr(const KURL& url, const TQCString& ns,
const TQCString& attr, const TQCString& val);
/**
* Removes extended attribute @p attr from @p path
* @param url file URL
* @param ns attribute namespace (usually "user")
* @param attr attribute name
* @since R14.1.5
*/
virtual void removeAttr(const KURL& url, const TQCString& ns,
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,379 @@
/*******************************************************************************
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 <kdebug.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) // FIXME untested
static TQValueList<TQCString>
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;
}
#define GET_EXTATTR_NS(ns) \
int extattrNS = \
ns == "user" ? EXTATTR_NAMESPACE_USER : \
ns == "system" ? EXTATTR_NAMESPACE_SYSTEM : -1; \
if (extattrNS == -1) return TDEIO::ERR_UNSUPPORTED_ACTION
#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, const TQCString &ns, 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)
GET_EXTATTR_NS(ns);
const ssize_t size = extattr_list_file(encodedPath, extattrNS, nullptr, 0);
#endif
if (size == 0)
{
return 0;
}
else if (size == -1)
{
kdWarning() << "(" << errno << ") error while listing attributes of "
<< url << endl;
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)
GET_EXTATTR_NS(ns);
const ssize_t r = extattr_list_file(encodedPath, extattrNS, data.data(), data.size());
#endif
if (r == 0) {
list->clear();
return 0;
}
if (r == -1 && errno != ERANGE)
{
kdWarning() << "(" << errno << ") error while listing attributes of "
<< url << endl;
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);
TQCString nsPrefix = ns + ".";
#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(nsPrefix.length()) != nsPrefix)
{
continue;
}
#endif
*list << attribute;
}
return 0;
}
}
uint
TDEXAttr::read(const KURL& url, const TQCString &ns, const TQCString& attribute, TQCString *value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_getxattr))
const ssize_t size = getxattr(encodedPath, ns + "." + attribute, nullptr, 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
GET_EXTATTR_NS(ns);
const ssize_t size = extattr_get_file(encodedPath, extattrNS, attribute, NULL, 0);
#endif
if (size == -1)
{
kdWarning() << "(" << errno << ") error while reading attribute "
<< ns << "." << attribute << " of " << url << endl;
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, ns + "." + attribute,
data.data(), data.size());
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
GET_EXTATTR_NS(ns);
const ssize_t r = extattr_get_file(encodedPath, extattrNS, attribute,
data.data(), data.size());
#endif
if (r == -1 && errno != ERANGE)
{
if (errno != ENODATA)
{
kdWarning() << "(" << errno << ") error while getting value of attribute "
<< ns << "." << attribute << " of " << url << endl;
}
*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 &ns, const TQCString& attribute, const TQCString& value)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_setxattr))
r = setxattr(encodedPath, ns + "." + attribute, value.data(), value.size(), 0);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
GET_EXTATTR_NS(ns);
r = extattr_set_file(encodedPath, extattrNS, attribute, value.data(), value.size());
#endif
if (r == -1)
{
kdWarning() << "(" << errno << ") error while writing attribute "
<< ns << "." << attribute << " of " << url << endl;
}
return r == -1 ? parseError(errno, TDEIO::CMD_WRITEATTR) : 0;
}
uint
TDEXAttr::remove(const KURL& url, const TQCString &ns, const TQCString& attribute)
{
CHECK_URL(url);
GET_ENCODED_PATH(url);
int r;
#if defined(Q_OS_LINUX) || (defined(__GLIBC__) && !defined(__stub_removexattr))
r = removexattr(encodedPath, ns + "." + attribute);
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
GET_EXTATTR_NS(ns);
r = extattr_delete_file (encodedPath, extattrNS, attribute);
#else
return TDEIO::ERR_UNSUPPORTED_ACTION;
#endif
if (r == -1)
{
kdWarning() << "(" << errno << ") error while removing attribute "
<< ns << "." << attribute << " of " << url << endl;
}
return r == -1 ? parseError(errno, TDEIO::CMD_REMOVEATTR) : 0;
}
bool
TDEXAttr::readSupported(const KURL& url, const TQCString &ns)
{
return read(url, ns, "x-tde-test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION;
}
bool
TDEXAttr::writeSupported(const KURL& url, const TQCString &ns)
{
return write(url, ns, "x-tde-test", nullptr) != TDEIO::ERR_UNSUPPORTED_ACTION
&& remove(url, ns, "x-tde-test") != TDEIO::ERR_UNSUPPORTED_ACTION;
}
#else // stubs for unsupported platforms
uint
TDEXAttr::list(const KURL& url, const TQCString &ns, TQValueList<TQCString> *list)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::read(const KURL& url, const TQCString &ns, const TQCString& attr, TQCString *value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
uint
TDEXAttr::write(const KURL& url, const TQCString &ns, const TQCString& attr, const TQCString& value)
{
return TDEIO::ERR_UNSUPPORTED_ACTION;
}
TDEIO::Error
TDEXAttr::remove(const KURL& url, const TQCString &ns, 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;
}
}
}
// kate: replace-tabs true; tab-width 4;

@ -0,0 +1,105 @@
/*******************************************************************************
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, const TQCString &ns, 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 &ns, 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 &ns, 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 &ns, const TQCString& attribute);
/**
* Check if reading extended attributes is supported for the given URL
* and namespace.
*
* @param url the URL of the file
* @return true if extended attributes are supported, false otherwise
*/
static bool readSupported(const KURL& url, const TQCString &ns);
/**
* Check if writing extended attributes is supported for the given URL
* and namespace.
*
* @param url the URL of the file
* @return true if extended attributes are supported, false otherwise
*/
static bool writeSupported(const KURL& url, const TQCString &ns);
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,87 @@ void FileProtocol::del( const KURL& url, bool isfile)
finished(); finished();
} }
void FileProtocol::listAttr( const KURL& url, const TQCString &ns )
{
if (!TDEXAttr::readSupported(url, ns))
{
error(TDEIO::ERR_ACCESS_DENIED, url.path());
return;
}
TQValueList<TQCString> attrs;
int result = TDEXAttr::list(url, ns, &attrs);
if (result != 0)
{
error(result, url.path());
return;
}
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 &ns, const TQCString& attr )
{
if (!TDEXAttr::readSupported(url, ns))
{
error(TDEIO::ERR_ACCESS_DENIED, url.path());
return;
}
TQCString value;
int result = TDEXAttr::read(url, ns, attr, &value);
if (result != 0)
{
error(result, url.path());
}
else
{
TQByteArray d;
TQDataStream s(d, IO_WriteOnly);
s << value;
data(d);
finished();
}
}
void FileProtocol::writeAttr( const KURL& url, const TQCString &ns, const TQCString& attr, const TQCString& value )
{
if (!TDEXAttr::writeSupported(url, ns))
{
error(TDEIO::ERR_WRITE_ACCESS_DENIED, url.path());
return;
}
int result = TDEXAttr::write(url, ns, attr, value);
if (result != 0)
{
error(result, url.path());
}
else finished();
}
void FileProtocol::removeAttr( const KURL& url, const TQCString &ns, const TQCString& attr )
{
if (!TDEXAttr::writeSupported(url, ns))
{
error(TDEIO::ERR_WRITE_ACCESS_DENIED, url.path());
return;
}
int result = TDEXAttr::remove(url, ns, attr);
if (result != 0)
{
error(result, url.path());
}
finished();
}
TQString FileProtocol::getUserName( uid_t uid ) TQString FileProtocol::getUserName( uid_t uid )
{ {
@ -990,8 +1072,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,13 @@ 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, const TQCString &ns );
virtual void readAttr( const KURL& url, const TQCString &ns,
const TQCString& attr );
virtual void writeAttr( const KURL& url, const TQCString &ns,
const TQCString& attr, const TQCString& value );
virtual void removeAttr( const KURL& url, const TQCString &ns,
const TQCString& attr );
/** /**
* Special commands supported by this slave: * Special commands supported by this slave:
@ -81,10 +88,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