diff --git a/src/core/job.cpp b/src/core/job.cpp --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -263,6 +263,88 @@ return d_func()->m_outgoingMetaData; } +bool Job::isPrivilegeExecutionEnabled() const +{ + return d_func()->m_privilegeExecutionEnabled; +} + +bool Job::wasConfirmationAsked() const +{ + return d_func()->m_confirmationAsked; +} + +bool JobPrivate::tryAskPrivilegeOpConfirmation() +{ + if (m_confirmationAsked) { + return true; + } + + if (m_parentJob) { + if (!m_parentJob->wasConfirmationAsked()) { + bool confirmed = m_parentJob->d_func()->tryAskPrivilegeOpConfirmation(); + if (confirmed) { + m_confirmationAsked = true; + } + return confirmed; + } else { + return true; + } + } + + switch (m_operationType) { + case ChangeAttr: + m_caption = i18n("Change Attribute"); + m_message = i18n("Root privileges are required to change file attributes. " + "Do you want to continue?"); + break; + case Copy: + m_caption = i18n("Copy Files"); + m_message = i18n("Root privileges are required to complete the copy operation. " + "Do you want to continue?"); + break; + case Delete: + m_caption = i18n("Delete Files"); + m_message = i18n("Root privileges are required to complete the delete operation." + "However, doing so may damage your system. Do you want to continue?"); + break; + case MkDir: + m_caption = i18n("Create Folder"); + m_message = i18n("Root privileges are required to create this folder. " + "Do you want to continue?"); + break; + case Move: + m_caption = i18n("Move Items"); + m_message = i18n("Root privileges are required to complete the move operation. " + "Do you want to continue?"); + break; + case Rename: + m_caption = i18n("Rename"); + m_message = i18n("Root privileges are required to complete renaming. " + "Do you want to continue?"); + break; + case Symlink: + m_caption = i18n("Create Symlink"); + m_message = i18n("Root privileges are required to create a symlink. " + "Do you want to continue?"); + break; + case Transfer: + m_caption = i18n("Transfer data"); + m_message = i18n("Root privileges are required to complete transferring data. " + "Do you want to continue?"); + default: + break; + } + + if (!m_uiDelegateExtension) { + return false; + } + + int status = m_uiDelegateExtension->requestMessageBox(JobUiDelegateExtension::WarningContinueCancel, + m_message, m_caption, i18n("Continue"), i18n("Cancel")); + m_confirmationAsked = true; + return status == 5; +} + ////////////////////////// class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate diff --git a/src/core/job_base.h b/src/core/job_base.h --- a/src/core/job_base.h +++ b/src/core/job_base.h @@ -214,6 +214,21 @@ */ QString queryMetaData(const QString &key); + /** + * Check for privilege execution support. + * + * @since 5.37 + */ + bool isPrivilegeExecutionEnabled() const; + + /** + * Check if confirmation has been asked. + * + * @since 5.37 + */ + bool wasConfirmationAsked() const; + + protected: Q_SIGNALS: @@ -301,7 +316,15 @@ * This is used by KIO::rename(), KIO::put(), KIO::file_copy(), KIO::file_move(), KIO::symlink(). * Otherwise the operation will fail with ERR_FILE_ALREADY_EXIST or ERR_DIR_ALREADY_EXIST. */ - Overwrite = 4 + Overwrite = 4, + /** + * When set, notifies the slave that the application(job) wants the file operation to be + * performed as root user if there occurs a failure due to insufficient privilege. + * + * @since 5.37 + **/ + PrivilegeExecution = 8 + }; Q_DECLARE_FLAGS(JobFlags, JobFlag) Q_DECLARE_OPERATORS_FOR_FLAGS(JobFlags) diff --git a/src/core/job_p.h b/src/core/job_p.h --- a/src/core/job_p.h +++ b/src/core/job_p.h @@ -47,7 +47,8 @@ public: JobPrivate() : m_parentJob(nullptr), m_extraFlags(0), - m_uiDelegateExtension(KIO::defaultJobUiDelegateExtension()) + m_uiDelegateExtension(KIO::defaultJobUiDelegateExtension()), + m_privilegeExecutionEnabled(false), m_confirmationAsked(false) { } @@ -74,6 +75,24 @@ JobUiDelegateExtension *m_uiDelegateExtension; Job *q_ptr; + enum FileOperationType { + ChangeAttr, // chmod(), chown(), setModificationTime() + Copy, + Delete, + MkDir, + Move, + Rename, + Symlink, + Transfer, // put() and get() + Other // if other file operation set message, caption inside the job. + }; + + bool m_privilegeExecutionEnabled; + bool m_confirmationAsked; + QString m_caption, m_message; + FileOperationType m_operationType; + bool tryAskPrivilegeOpConfirmation(); + void slotSpeed(KJob *job, unsigned long speed); static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest); @@ -173,6 +192,13 @@ void _k_slotSlaveInfoMessage(const QString &s); /** + * Called when privilegeExecData() is emmited by the slave. + * + * @since 5.37 + */ + void slotPrivilegeExecData(const QByteArray &data); + + /** * @internal * Called by the scheduler when a slave gets to * work on this job. diff --git a/src/core/simplejob.h b/src/core/simplejob.h --- a/src/core/simplejob.h +++ b/src/core/simplejob.h @@ -146,6 +146,7 @@ Q_PRIVATE_SLOT(d_func(), void slotSpeed(unsigned long speed)) Q_PRIVATE_SLOT(d_func(), void slotTotalSize(KIO::filesize_t data_size)) Q_PRIVATE_SLOT(d_func(), void _k_slotSlaveInfoMessage(const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotPrivilegeExecData(const QByteArray &)) Q_DECLARE_PRIVATE(SimpleJob) }; diff --git a/src/core/simplejob.cpp b/src/core/simplejob.cpp --- a/src/core/simplejob.cpp +++ b/src/core/simplejob.cpp @@ -148,6 +148,9 @@ q->connect(slave, SIGNAL(finished()), SLOT(slotFinished())); + q->connect(slave, SIGNAL(privilegeExecData(QByteArray)), + SLOT(slotPrivilegeExecData(QByteArray))); + if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job q->connect(slave, SIGNAL(totalSize(KIO::filesize_t)), SLOT(slotTotalSize(KIO::filesize_t))); @@ -337,6 +340,24 @@ Q_UNUSED(redirectionURL); } +void SimpleJobPrivate::slotPrivilegeExecData(const QByteArray &data) +{ + QByteArray reply; + if (data == QStringLiteral("CanElevatePrivilege")) { + if (m_privilegeExecutionEnabled + || (m_parentJob && m_parentJob->isPrivilegeExecutionEnabled())) { + reply = QByteArray("ElevatePrivilege"); + } + } else if (data == QStringLiteral("AskConfirmation")) { + bool confirmed = tryAskPrivilegeOpConfirmation(); + if (confirmed) { + reply = QByteArray("ActionConfirmed"); + } + } + + m_slave->send(MSG_PRIVILEGE_EXEC, reply); +} + ////////// SimpleJob *KIO::rmdir(const QUrl &url) {