diff --git a/tdecore/kprotocolinfo_tdecore.cpp b/tdecore/kprotocolinfo_tdecore.cpp index 3d08cccd5..1ce9f4e3a 100644 --- a/tdecore/kprotocolinfo_tdecore.cpp +++ b/tdecore/kprotocolinfo_tdecore.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -71,6 +70,8 @@ KProtocolInfo::KProtocolInfo(const TQString &path) m_supportsDeleting = config.readBoolEntry( "deleting", false ); m_supportsLinking = config.readBoolEntry( "linking", false ); m_supportsMoving = config.readBoolEntry( "moving", false ); + m_supportsReadingAttrs = config.readBoolEntry( "readattr", false ); + m_supportsWritingAttrs = config.readBoolEntry( "writeattr", false ); m_canCopyFromFile = config.readBoolEntry( "copyFromFile", false ); m_canCopyToFile = config.readBoolEntry( "copyToFile", false ); d->canRenameFromFile = config.readBoolEntry( "renameFromFile", false ); @@ -159,7 +160,8 @@ KProtocolInfo::load( TQDataStream& _str) i_supportsMoving, i_determineMimetypeFromExtension, i_canCopyFromFile, i_canCopyToFile, i_showPreviews, i_uriMode, i_canRenameFromFile, i_canRenameToFile, - i_canDeleteRecursive, i_fileNameUsedForCopying; + i_canDeleteRecursive, i_fileNameUsedForCopying, + i_supportsReadingAttrs, i_supportsWritingAttrs; _str >> m_name >> m_exec >> m_listing >> m_defaultMimetype >> i_determineMimetypeFromExtension @@ -175,7 +177,8 @@ KProtocolInfo::load( TQDataStream& _str) >> d->extraFields >> i_showPreviews >> i_uriMode >> d->capabilities >> d->proxyProtocol >> i_canRenameFromFile >> i_canRenameToFile - >> i_canDeleteRecursive >> i_fileNameUsedForCopying; + >> i_canDeleteRecursive >> i_fileNameUsedForCopying + >> i_supportsReadingAttrs >> i_supportsWritingAttrs; m_inputType = (Type) i_inputType; m_outputType = (Type) i_outputType; @@ -188,6 +191,8 @@ KProtocolInfo::load( TQDataStream& _str) m_supportsDeleting = (i_supportsDeleting != 0); m_supportsLinking = (i_supportsLinking != 0); m_supportsMoving = (i_supportsMoving != 0); + m_supportsReadingAttrs = (i_supportsReadingAttrs != 0); + m_supportsWritingAttrs = (i_supportsWritingAttrs != 0); m_canCopyFromFile = (i_canCopyFromFile != 0); m_canCopyToFile = (i_canCopyToFile != 0); d->canRenameFromFile = (i_canRenameFromFile != 0); @@ -214,7 +219,8 @@ KProtocolInfo::save( TQDataStream& _str) i_supportsMoving, i_determineMimetypeFromExtension, i_canCopyFromFile, i_canCopyToFile, i_showPreviews, i_uriMode, i_canRenameFromFile, i_canRenameToFile, - i_canDeleteRecursive, i_fileNameUsedForCopying; + i_canDeleteRecursive, i_fileNameUsedForCopying, + i_supportsReadingAttrs, i_supportsWritingAttrs; i_inputType = (TQ_INT32) m_inputType; i_outputType = (TQ_INT32) m_outputType; @@ -227,6 +233,8 @@ KProtocolInfo::save( TQDataStream& _str) i_supportsDeleting = m_supportsDeleting ? 1 : 0; i_supportsLinking = m_supportsLinking ? 1 : 0; i_supportsMoving = m_supportsMoving ? 1 : 0; + i_supportsReadingAttrs = m_supportsReadingAttrs ? 1 : 0; + i_supportsWritingAttrs = m_supportsWritingAttrs ? 1 : 0; i_canCopyFromFile = m_canCopyFromFile ? 1 : 0; i_canCopyToFile = m_canCopyToFile ? 1 : 0; i_canRenameFromFile = d->canRenameFromFile ? 1 : 0; @@ -251,7 +259,8 @@ KProtocolInfo::save( TQDataStream& _str) << d->extraFields << i_showPreviews << i_uriMode << d->capabilities << d->proxyProtocol << i_canRenameFromFile << i_canRenameToFile - << i_canDeleteRecursive << i_fileNameUsedForCopying; + << i_canDeleteRecursive << i_fileNameUsedForCopying + << i_supportsReadingAttrs << i_supportsWritingAttrs; } diff --git a/tdeio/tdefile/kpropertiesdialog.cpp b/tdeio/tdefile/kpropertiesdialog.cpp index c8e324a2d..8026f0767 100644 --- a/tdeio/tdefile/kpropertiesdialog.cpp +++ b/tdeio/tdefile/kpropertiesdialog.cpp @@ -124,6 +124,9 @@ extern "C" { #include #include #include +#include +#include +#include #include "tdefilesharedlg.h" #include "kpropertiesdesktopbase.h" @@ -139,6 +142,17 @@ extern "C" { # include #endif +// This KDE3-era HACK ensures that TDEIO jobs are properly terminated in the +// applyChanges() slots. +void tqt_enter_modal( TQWidget *widget ); +void tqt_leave_modal( TQWidget *widget ); +#define WAIT_FOR_JOB \ + TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); \ + tqt_enter_modal(&dummy); \ + tqApp->enter_loop(); \ + tqt_leave_modal(&dummy); +#define JOB_DONE tqApp->exit_loop(); + static TQString nameFromFileName(TQString nameStr) { if ( nameStr.endsWith(".desktop") ) @@ -387,6 +401,7 @@ bool KPropertiesDialog::canDisplay( KFileItemList _items ) KBindingPropsPlugin::supports( _items ) || KURLPropsPlugin::supports( _items ) || KDevicePropsPlugin::supports( _items ) || + TDEAttrPropsPlugin::supports(_items) || KFileMetaPropsPlugin::supports( _items ) || KPreviewPropsPlugin::supports( _items ); } @@ -486,6 +501,12 @@ void KPropertiesDialog::insertPages() insertPlugin (p); } + if (TDEAttrPropsPlugin::supports(m_items)) + { + TDEAttrPropsPlugin *p = new TDEAttrPropsPlugin(this); + insertPlugin(p); + } + if ( KFileMetaPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this ); @@ -1324,10 +1345,6 @@ bool KFilePropsPlugin::supports( KFileItemList /*_items*/ ) return true; } -// Don't do this at home -void tqt_enter_modal( TQWidget *widget ); -void tqt_leave_modal( TQWidget *widget ); - void KFilePropsPlugin::applyChanges() { if ( d->dirSizeJob ) { @@ -1383,11 +1400,7 @@ void KFilePropsPlugin::applyChanges() TQ_SLOT( slotCopyFinished( TDEIO::Job * ) ) ); connect( job, TQ_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ), TQ_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) ); - // wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; return; } properties->updateUrl(properties->kurl()); @@ -1406,8 +1419,7 @@ void KFilePropsPlugin::slotCopyFinished( TDEIO::Job * job ) kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl; if (job) { - // allow apply() to return - tqApp->exit_loop(); + JOB_DONE; if ( job->error() ) { job->showErrorDialog( d->m_frame ); @@ -2555,11 +2567,7 @@ void KFilePermissionsPropsPlugin::applyChanges() connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); - // Wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; } if (dirs.count() > 0) { job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions, @@ -2571,11 +2579,7 @@ void KFilePermissionsPropsPlugin::applyChanges() connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); - // Wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; } } @@ -2584,8 +2588,7 @@ void KFilePermissionsPropsPlugin::slotChmodResult( TDEIO::Job * job ) kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl; if (job->error()) job->showErrorDialog( d->m_frame ); - // allow apply() to return - tqApp->exit_loop(); + JOB_DONE } @@ -2861,6 +2864,745 @@ void KBindingPropsPlugin::applyChanges() 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 m_entryWidgets, m_pendingWidgets; + TQCString m_namespace; + TQVBoxLayout *m_layout, *m_entriesLayout; + TQMap m_entries; + TQLabel *m_label; + TQStringList m_savedAttrs; +}; + +class TDEAttrPropsPlugin::TDEAttrPropsPluginPrivate +{ + public: + TDEAttrPropsPluginPrivate() {} + ~TDEAttrPropsPluginPrivate() {} + + TQFrame *m_frame, *m_tab; + KTabWidget *m_tabWidget; + TQMap 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( + "User attributes may be assigned to files and directories " + "for storing arbitrary additional information." + ); + updateTab("user", i18n("User"), tip); + +#if defined(Q_OS_LINUX) + tip = i18n( + "

System attributes are used by the kernel to store " + "system objects such as Access Control Lists." + "

Read and write access permissions to system attributes depend on " + "the policy implemented for each system attribute implemented by " + "filesystems in the kernel." + ); + updateTab("system", i18n("System"), tip); + + tip = i18n( + "

Security attributes are used by kernel security modules " + "such as SELinux, and also to implement file capabilities." + "

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." + ); + updateTab("security", i18n("Security"), tip); + + tip = i18n( + "

Trusted attributes 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." + ); + 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( + "

System attributes are protected such that appropriate " + "privilege is required to directly access or manipulate them." + ); + 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(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(w); + delEntry(entry); + } + + // From combobox lineedit + else if (qstrcmp(w->parentWidget()->name(), "tdeattrentry") == 0) + { + TDEAttrEntry *entry = static_cast(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(const_cast(TQObject::sender())); + Q_ASSERT(dlg); + KEditListBox *elb = static_cast(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("Are you sure you want to delete attribute %1?") + .arg(TQString::fromLocal8Bit(attr)); + } + else + { + msg = i18n("Are you sure you want to delete this unnamed attribute?"); + } + + int result = KMessageBox::warningYesNo( + properties, msg, i18n("Remove attribute?"), + KStdGuiItem::yes(), KStdGuiItem::no(), "ConfirmRemoveAttribute"); + del = (result == KMessageBox::Yes); + } + + if (del) + { + TDEAttrNamespaceTab *tab = static_cast(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(const_cast(sender)); + Q_ASSERT(button); + TDEAttrEntry *entry = static_cast(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(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 attrs = listJob->attributes(); + TQValueList::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(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(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(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::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 entries(tab->m_entries); + TQMap::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(const_cast(TQObject::sender())); + Q_ASSERT(dlg); + TQCheckBox *showSystemNs = static_cast(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 diff --git a/tdeio/tdefile/kpropertiesdialog.h b/tdeio/tdefile/kpropertiesdialog.h index 8fa1a5880..f30ba1494 100644 --- a/tdeio/tdefile/kpropertiesdialog.h +++ b/tdeio/tdefile/kpropertiesdialog.h @@ -63,7 +63,7 @@ namespace TDEIO { class Job; } * * This class must be created with (void)new KPropertiesDialog(...) * It will take care of deleting itself. - * + * * If you are looking for more flexibility, see KFileMetaInfo and * KFileMetaInfoWidget. */ @@ -82,9 +82,9 @@ public: static bool canDisplay( KFileItemList _items ); /** - * Brings up a Properties dialog, as shown above. + * Brings up a Properties dialog, as shown above. * This is the normal constructor for - * file-manager type applications, where you have a KFileItem instance + * file-manager type applications, where you have a KFileItem instance * to work with. Normally you will use this * method rather than the one below. * @@ -192,40 +192,40 @@ public: virtual ~KPropertiesDialog(); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p item points to a local file, native (non modal) property + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p item points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(KFileItem* item, TQWidget* parent = 0, + static bool showDialog(KFileItem* item, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p _url points to a local file, native (non modal) property + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p _url points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(const KURL& _url, TQWidget* parent = 0, + static bool showDialog(const KURL& _url, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p _items has one element and this element points - * to a local file, native (non modal) property dialog is displayed + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p _items has one element and this element points + * to a local file, native (non modal) property dialog is displayed * (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0, + static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** @@ -244,7 +244,7 @@ public: void insertPlugin (KPropsDlgPlugin *plugin); /** - * The URL of the file that has its properties being displayed. + * The URL of the file that has its properties being displayed. * This is only valid if the KPropertiesDialog was created/shown * for one file or URL. * @@ -323,7 +323,7 @@ public: * @since 3.1 */ void showFileSharingPage(); - + /** * Sets the file sharing page. * This page is shown when calling showFileSharingPage(). @@ -715,6 +715,52 @@ private: KBindingPropsPluginPrivate *d; }; +/** + * Properties plugin for extended attributes + * @internal + */ +class TDEIO_EXPORT TDEAttrPropsPlugin : public KPropsDlgPlugin +{ + TQ_OBJECT + + public: + TDEAttrPropsPlugin(KPropertiesDialog *_props); + virtual ~TDEAttrPropsPlugin(); + + virtual void applyChanges(); + static bool supports(KFileItemList _items); + + private slots: + void slotListJobResult(TDEIO::Job *); + void slotReadJobResult(TDEIO::Job *); + void slotWriteJobResult(TDEIO::Job *); + void slotRemoveJobResult(TDEIO::Job *); + void slotAddEntry(); + void 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 * @internal diff --git a/tdeio/tdeio/CMakeLists.txt b/tdeio/tdeio/CMakeLists.txt index 91828320a..6a895af94 100644 --- a/tdeio/tdeio/CMakeLists.txt +++ b/tdeio/tdeio/CMakeLists.txt @@ -46,7 +46,7 @@ install( FILES kfilterbase.h kfilterdev.h tdeemailsettings.h kscan.h kdatatool.h karchive.h tdefilefilter.h tdefilemetainfo.h renamedlgplugin.h kmimetyperesolver.h kdcopservicestarter.h - kremoteencoding.h kmimetypechooser.h kacl.h + kremoteencoding.h kmimetypechooser.h kacl.h tdexattr.h DESTINATION ${INCLUDE_INSTALL_DIR} ) install( FILES @@ -77,7 +77,7 @@ set( ${target}_SRCS kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp observer.cpp ../misc/uiserver.stub observer.skel tdeemailsettings.cpp kprotocolinfo.cpp renamedlg.cpp skipdlg.cpp kremoteencoding.cpp - kmimetypechooser.cpp + kmimetypechooser.cpp tdexattr.cpp ) tde_add_library( ${target} STATIC_PIC AUTOMOC diff --git a/tdeio/tdeio/global.cpp b/tdeio/tdeio/global.cpp index f026b3044..33051a4b8 100644 --- a/tdeio/tdeio/global.cpp +++ b/tdeio/tdeio/global.cpp @@ -426,7 +426,7 @@ TDEIO_EXPORT TQString TDEIO::buildErrorString(int errorCode, const TQString &err result = i18n( "Access to restricted port in POST denied."); break; case TDEIO::ERR_OFFLINE_MODE: - result = i18n( "Could not access %1.\nOffline mode active.").arg( errorText ); + result = i18n( "Could not access %1.\nOffline mode active.").arg( errorText ); break; default: result = i18n( "Unknown error code %1\n%2\nPlease send a full bug report at http://bugs.trinitydesktop.org." ).arg( errorCode ).arg( errorText ); @@ -466,6 +466,11 @@ TDEIO_EXPORT TQString TDEIO::unsupportedActionErrorString(const TQString &protoc return i18n("Creating folders is not supported with protocol %1.").arg(protocol); case CMD_CHMOD: return i18n("Changing the attributes of files is not supported with protocol %1.").arg(protocol); + case CMD_LISTATTR: + case CMD_READATTR: + case CMD_WRITEATTR: + case CMD_REMOVEATTR: + return i18n("Extended attributes are not supported with protocol %1.").arg(protocol); case CMD_SUBURL: return i18n("Using sub-URLs with %1 is not supported.").arg(protocol); case CMD_MULTI_GET: @@ -1361,10 +1366,10 @@ extern "C" void endvfsent( ); # endif #endif -#ifdef __CYGWIN__ -#define hasmntopt(var,opt) (0) -#endif - +#ifdef __CYGWIN__ +#define hasmntopt(var,opt) (0) +#endif + // There are (at least) four kind of APIs: // setmntent + getmntent + struct mntent (linux...) // getmntent + struct mnttab @@ -1949,7 +1954,7 @@ TQString TDEIO::findPathMountPoint(const TQString& filename) return get_mount_info(filename, isautofs, isslow, ismanual, fstype); #else //!Q_OS_UNIX return TQString::null; -#endif +#endif } bool TDEIO::manually_mounted(const TQString& filename) @@ -1961,7 +1966,7 @@ bool TDEIO::manually_mounted(const TQString& filename) return !mountPoint.isNull() && (ismanual == Right); #else //!Q_OS_UNIX return false; -#endif +#endif } bool TDEIO::probably_slow_mounted(const TQString& filename) @@ -1973,7 +1978,7 @@ bool TDEIO::probably_slow_mounted(const TQString& filename) return !mountPoint.isNull() && (isslow == Right); #else //!Q_OS_UNIX return false; -#endif +#endif } bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag) @@ -1995,7 +2000,7 @@ bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag) case CaseInsensitive: return isMsDos; } -#endif +#endif return false; } diff --git a/tdeio/tdeio/global.h b/tdeio/tdeio/global.h index 77e2da77b..c7ec9c911 100644 --- a/tdeio/tdeio/global.h +++ b/tdeio/tdeio/global.h @@ -164,7 +164,11 @@ namespace TDEIO CMD_RESUMEANSWER = 'T', // 84 CMD_CONFIG = 'U', // 85 CMD_MULTI_GET = 'V', // 86 - CMD_LOCALURL = 'W' // 87 + CMD_LOCALURL = 'W', // 87 + CMD_LISTATTR = 'X', // 88 + CMD_READATTR = 'Y', // 89 + CMD_WRITEATTR = 'Z', // 90 + CMD_REMOVEATTR = '[' // 91 // Add new ones here once a release is done, to avoid breaking binary compatibility. // Note that protocol-specific commands shouldn't be added here, but should use special. }; @@ -247,7 +251,7 @@ namespace TDEIO // the server in order to continue. ERR_POST_DENIED = 65, // Issued when trying to POST data to a certain Ports // see job.cpp - ERR_OFFLINE_MODE = 66 // Used when an app is in offline mode and a + ERR_OFFLINE_MODE = 66 // Used when an app is in offline mode and a // requested document is unavailable. }; @@ -350,7 +354,7 @@ namespace TDEIO /// @since 3.5 UDS_DEFAULT_ACL_STRING = 104 | UDS_STRING, - // available: 112, 120 + // available: 112, 120 /// Access permissions (part of the mode returned by stat) UDS_ACCESS = 128 | UDS_LONG, diff --git a/tdeio/tdeio/job.cpp b/tdeio/tdeio/job.cpp index 5bdea2fd8..a52dbe7ec 100644 --- a/tdeio/tdeio/job.cpp +++ b/tdeio/tdeio/job.cpp @@ -837,6 +837,120 @@ SimpleJob *TDEIO::unmount( const TQString& point, bool showProgressInfo ) return job; } +//////////// +class AttributeJob::AttributeJobPrivate +{ + public: + AttributeJobPrivate() {} + + // The extended attributes of a file, result of the LISTATTR command + TQValueList 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 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, 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 ) { TransferJob::virtual_hook( id, data ); } +void AttributeJob::virtual_hook( int id, void* data ) +{ TransferJob::virtual_hook( id, data ); } + void FileCopyJob::virtual_hook( int id, void* data ) { Job::virtual_hook( id, data ); } diff --git a/tdeio/tdeio/job.h b/tdeio/tdeio/job.h index 5484712bb..29b2758b4 100644 --- a/tdeio/tdeio/job.h +++ b/tdeio/tdeio/job.h @@ -127,6 +127,66 @@ namespace TDEIO { */ TDEIO_EXPORT SimpleJob *unmount( const TQString & point, bool showProgressInfo = true ); + /** + * List extended attributes. + * + * Currently only used by @p tdeio_file. + * + * @param url file URL + * @param showProgressInfo true to show progress information + * @return the job handling the operation. + * @since R14.1.5 + */ + TDEIO_EXPORT AttributeJob *listAttr( const KURL& url, 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 * diff --git a/tdeio/tdeio/jobclasses.h b/tdeio/tdeio/jobclasses.h index 5d04b7f5d..c2d494b6b 100644 --- a/tdeio/tdeio/jobclasses.h +++ b/tdeio/tdeio/jobclasses.h @@ -1241,6 +1241,79 @@ namespace TDEIO { class MimetypeJobPrivate* d; }; + /** + * An AttributeJob is a TransferJob that allows you to query and modify + * the extended attributes of an URL. Don't create directly, but use + * TDEIO::listAttr(), TDEIO::readAttr(), TDEIO::writeAttr() or + * TDEIO::removeAttr() instead. + * @see TDEIO::listAttr(), TDEIO::readAttr(), TDEIO::writeAttr(), TDEIO::removeAttr() + */ + class TDEIO_EXPORT AttributeJob : public TransferJob { + TQ_OBJECT + + public: + /** + * Do not create an AttributeJob directly. Use TDEIO::listAttr(), + * TDEIO::readAttr(), TDEIO::writeAttr() or TDEIO::removeAttr() instead. + * @param url the url to get + * @param command the command to issue + * @param packedArgs the arguments + * @param showProgressInfo true to show progress information to the user + */ + AttributeJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo); + ~AttributeJob(); + + /** + * This function contains the result of a successful listAttr() operation. + * If an error occured, an empty list is returned instead. + * @return list of extended attributes of the file + */ + const TQValueList 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. * @see TDEIO::file_copy() diff --git a/tdeio/tdeio/kprotocolinfo.cpp b/tdeio/tdeio/kprotocolinfo.cpp index 4332fc23b..4261e5f62 100644 --- a/tdeio/tdeio/kprotocolinfo.cpp +++ b/tdeio/tdeio/kprotocolinfo.cpp @@ -190,6 +190,24 @@ bool KProtocolInfo::supportsMoving( const KURL &url ) return prot->m_supportsMoving; } +bool KProtocolInfo::supportsReadingAttrs( const KURL &url ) +{ + KProtocolInfo::Ptr prot = findProtocol(url); + if ( !prot ) + return false; + + return prot->m_supportsReadingAttrs; +} + +bool KProtocolInfo::supportsWritingAttrs( const KURL &url ) +{ + KProtocolInfo::Ptr prot = findProtocol(url); + if ( !prot ) + return false; + + return prot->m_supportsWritingAttrs; +} + bool KProtocolInfo::canCopyFromFile( const KURL &url ) { KProtocolInfo::Ptr prot = findProtocol(url); diff --git a/tdeio/tdeio/kprotocolinfo.h b/tdeio/tdeio/kprotocolinfo.h index 000a88323..a6fea6954 100644 --- a/tdeio/tdeio/kprotocolinfo.h +++ b/tdeio/tdeio/kprotocolinfo.h @@ -343,6 +343,30 @@ public: */ static bool supportsMoving( const KURL &url ); + /** + * Returns whether the protocol supports reading attributes. + * + * This corresponds to the "readattr=" field in the protocol description file. + * Valid values for this field are "true" or "false" (default). + * + * @param url the url to check + * @return true if the protocol supports reading attributes + * @since R14.1.5 + */ + static bool supportsReadingAttrs( const KURL &url ); + + /** + * Returns whether the protocol supports reading attributes. + * + * This corresponds to the "writeattr=" field in the protocol description file. + * Valid values for this field are "true" or "false" (default). + * + * @param url the url to check + * @return true if the protocol supports reading attributes + * @since R14.1.5 + */ + static bool supportsWritingAttrs( const KURL &url ); + /** * Returns whether the protocol can copy files/objects directly from the * filesystem itself. If not, the application will read files from the @@ -661,6 +685,8 @@ protected: bool m_supportsDeleting; bool m_supportsLinking; bool m_supportsMoving; + bool m_supportsReadingAttrs; + bool m_supportsWritingAttrs; TQString m_defaultMimetype; bool m_determineMimetypeFromExtension; TQString m_icon; diff --git a/tdeio/tdeio/slavebase.cpp b/tdeio/tdeio/slavebase.cpp index b9e9a04a6..137e154c0 100644 --- a/tdeio/tdeio/slavebase.cpp +++ b/tdeio/tdeio/slavebase.cpp @@ -90,7 +90,7 @@ return false; } KEntryMap internalEntryMap() const { return KEntryMap(); } - void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup) + void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup) { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); } KEntry lookupData(const KEntryKey &_key) const @@ -800,6 +800,14 @@ void SlaveBase::mkdir(KURL const &, int) { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); } void SlaveBase::chmod(KURL const &, int) { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); } +void SlaveBase::listAttr(KURL const &, 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 &) { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); } void SlaveBase::multiGet(const TQByteArray &) @@ -1134,6 +1142,34 @@ void SlaveBase::dispatch( int command, const TQByteArray &data ) stream >> url >> i; chmod( url, i); 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: special( data ); break; diff --git a/tdeio/tdeio/slavebase.h b/tdeio/tdeio/slavebase.h index 05d3cbbed..0add0362b 100644 --- a/tdeio/tdeio/slavebase.h +++ b/tdeio/tdeio/slavebase.h @@ -461,6 +461,45 @@ public: */ 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. * If the slave returns an error ERR_UNSUPPORTED_ACTION, the job will diff --git a/tdeio/tdeio/tdexattr.cpp b/tdeio/tdeio/tdexattr.cpp new file mode 100644 index 000000000..6fd715f38 --- /dev/null +++ b/tdeio/tdeio/tdexattr.cpp @@ -0,0 +1,379 @@ +/******************************************************************************* + Extended attributes support for TDE + Copyright © 2025 Philippe Mavridis + + Based on xattr_p.h from KFileMetaData + Copyright © 2014 Raphael Kubo da Costa + + 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 +#include + +#if defined(Q_OS_LINUX) || defined(__GLIBC__) +# include +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) +# include +#endif + +#include + +#include + +#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 +splitLengthValue(TQByteArray data) +{ + uint pos = 0; + TQValueList 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 +splitZeroSeparator(TQByteArray data) +{ + uint pos = 0; + TQValueList 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 *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 entries = splitZeroSeparator(data); + TQCString nsPrefix = ns + "."; +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + const TQValueList entries = splitLengthValue(data); +#endif + + list->clear(); + + TQValueList::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 *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; \ No newline at end of file diff --git a/tdeio/tdeio/tdexattr.h b/tdeio/tdeio/tdexattr.h new file mode 100644 index 000000000..a4f1a3458 --- /dev/null +++ b/tdeio/tdeio/tdexattr.h @@ -0,0 +1,105 @@ +/******************************************************************************* + Extended attributes support for TDE + Copyright © 2025 Philippe Mavridis + + Based on xattr_p.h from KFileMetaData + Copyright © 2014 Raphael Kubo da Costa + + 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 +#include +#include + +/** + * 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 + */ + +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 *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; \ No newline at end of file diff --git a/tdeioslave/file/file.cpp b/tdeioslave/file/file.cpp index 162f8a848..a801d71ac 100644 --- a/tdeioslave/file/file.cpp +++ b/tdeioslave/file/file.cpp @@ -94,6 +94,7 @@ #include #include #include +#include using namespace TDEIO; @@ -360,7 +361,7 @@ write_all(int fd, const char *buf, size_t len) return 0; } -static bool +static bool same_inode(const KDE_struct_stat &src, const KDE_struct_stat &dest) { if (src.st_ino == dest.st_ino && @@ -626,7 +627,7 @@ void FileProtocol::copy( const KURL &src, const KURL &dest, return; } - if ( same_inode( buff_dest, buff_src) ) + if ( same_inode( buff_dest, buff_src) ) { error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); return; @@ -839,7 +840,7 @@ void FileProtocol::rename( const KURL &src, const KURL &dest, return; } - if ( same_inode( buff_dest, buff_src) ) + if ( same_inode( buff_dest, buff_src) ) { error( TDEIO::ERR_IDENTICAL_FILES, dest.path() ); return; @@ -955,6 +956,87 @@ void FileProtocol::del( const KURL& url, bool isfile) finished(); } +void FileProtocol::listAttr( const KURL& url, const TQCString &ns ) +{ + if (!TDEXAttr::readSupported(url, ns)) + { + error(TDEIO::ERR_ACCESS_DENIED, url.path()); + return; + } + + TQValueList attrs; + int result = TDEXAttr::list(url, ns, &attrs); + if (result != 0) + { + error(result, url.path()); + return; + } + TQValueList::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 ) { @@ -990,8 +1072,6 @@ TQString FileProtocol::getGroupName( gid_t gid ) return *temp; } - - bool FileProtocol::createUDSEntry( const TQString & filename, const TQCString & path, UDSEntry & entry, short int details, bool withACL ) { diff --git a/tdeioslave/file/file.h b/tdeioslave/file/file.h index 04a6ed225..962941ba9 100644 --- a/tdeioslave/file/file.h +++ b/tdeioslave/file/file.h @@ -62,6 +62,13 @@ public: virtual void mkdir( const KURL& url, int permissions ); virtual void chmod( const KURL& url, int permissions ); virtual void del( const KURL& url, bool isfile); + virtual void listAttr( const KURL& url, 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: @@ -81,10 +88,10 @@ protected slots: protected: - bool createUDSEntry( const TQString & filename, const TQCString & path, TDEIO::UDSEntry & entry, + bool createUDSEntry( const TQString & filename, const TQCString & path, TDEIO::UDSEntry & entry, short int details, bool withACL ); int setACL( const char *path, mode_t perm, bool _directoryDefault ); - + TQString getUserName( uid_t uid ); TQString getGroupName( gid_t gid ); diff --git a/tdeioslave/file/file.protocol b/tdeioslave/file/file.protocol index 14f17e033..26ed30a6e 100644 --- a/tdeioslave/file/file.protocol +++ b/tdeioslave/file/file.protocol @@ -10,6 +10,8 @@ makedir=true deleting=true linking=true moving=true +readattr=true +writeattr=true maxInstances=4 X-DocPath=tdeioslave/file/index.html Class=:local