diff --git a/src/core/slavebase.h b/src/core/slavebase.h --- a/src/core/slavebase.h +++ b/src/core/slavebase.h @@ -935,6 +935,18 @@ */ int waitForHostInfo(QHostInfo &info); + /** + * Used by slave to send data to job when file operation is being performed + * with elevated privileges. It is similar to data() but has different name + * so as not to mess up existing connections. + */ + void privilegeExecData(const QByteArray &data); + + /** + * Reads the reply sent for privilegeExecData() signal into @p buffer. + */ + int readPrivilegeExecData(QByteArray &buffer); + protected: /** * Name of the protocol supported by this slave diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -1457,3 +1457,13 @@ return result; } + +void SlaveBase::privilegeExecData(const QByteArray &data) +{ + send(MSG_PRIVILEGE_EXEC, data); +} + +int SlaveBase::readPrivilegeExecData(QByteArray &buffer) +{ + return waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer); +} diff --git a/src/core/slaveinterface.h b/src/core/slaveinterface.h --- a/src/core/slaveinterface.h +++ b/src/core/slaveinterface.h @@ -84,8 +84,9 @@ MSG_DEL_AUTH_KEY, ///< @deprecated MSG_OPENED, MSG_WRITTEN, - MSG_HOST_INFO_REQ + MSG_HOST_INFO_REQ, // add new ones here once a release is done, to avoid breaking binary compatibility + MSG_PRIVILEGE_EXEC }; /** @@ -144,6 +145,8 @@ void open(); void written(KIO::filesize_t); + void privilegeExecData(const QByteArray &); + /////////// // Info sent by the slave ////////// diff --git a/src/core/slaveinterface.cpp b/src/core/slaveinterface.cpp --- a/src/core/slaveinterface.cpp +++ b/src/core/slaveinterface.cpp @@ -319,6 +319,9 @@ HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo))); break; } + case MSG_PRIVILEGE_EXEC: + emit privilegeExecData(rawdata); + break; default: qCWarning(KIO_CORE) << "Slave sends unknown command (" << _cmd << "), dropping slave"; return false; diff --git a/src/ioslaves/file/file.h b/src/ioslaves/file/file.h --- a/src/ioslaves/file/file.h +++ b/src/ioslaves/file/file.h @@ -100,6 +100,21 @@ void fileSystemFreeSpace(const QUrl &url); // KF6 TODO: Turn into virtual method in SlaveBase + enum ActionType { + CHMOD = 1, + CHOWN, + DEL, + MKDIR, + OPEN, + OPENDIR, + RENAME, + RMDIR, + SYMLINK, + UTIME, + }; + virtual bool execWithElevatedPrivilege(int error, ActionType action, const QVariant &arg1, + const QVariant &arg2 = QVariant(), + const QVariant &arg3 = QVariant()); private: mutable QHash mUsercache; mutable QHash mGroupcache; diff --git a/src/ioslaves/file/file_unix.cpp b/src/ioslaves/file/file_unix.cpp --- a/src/ioslaves/file/file_unix.cpp +++ b/src/ioslaves/file/file_unix.cpp @@ -39,6 +39,8 @@ #include #include +#include + //sendfile has different semantics in different platforms #if defined HAVE_SENDFILE && defined Q_OS_LINUX #define USE_SENDFILE 1 @@ -650,3 +652,54 @@ finished(); } + +bool FileProtocol::execWithElevatedPrivilege(int error, ActionType action, const QVariant &arg1, + const QVariant &arg2, const QVariant &arg3) +{ + if (error != EACCES) { + return false; + } + + QByteArray jobReply; + privilegeExecData("CanElevatePrivilege"); + readPrivilegeExecData(jobReply); + if(jobReply != QStringLiteral("ElevatePrivilege")) { + return false; + } + + privilegeExecData("AskConfirmation"); + readPrivilegeExecData(jobReply); + if (jobReply != QStringLiteral("ActionConfirmed")) { + return false; + } + + + QByteArray helperArgs; + QDataStream out(&helperArgs, QIODevice::WriteOnly); + out << action << arg1 << arg2 << arg3; + + QVariantMap argv; + argv.insert(QStringLiteral("arguments"), helperArgs); + + KAuth::Action execAction(QStringLiteral("org.kde.kio.file.exec")); + execAction.setHelperId(QStringLiteral("org.kde.kio.file")); + execAction.setArguments(argv); + + // if we are unit testing let's pretend to execute the action. + if (metaData(QStringLiteral("UnitTesting")) == QLatin1String("true")) { + QStringList testData; + testData += execAction.name(); + testData += QString::number(execAction.isValid()); + testData += execAction.helperId(); + testData += QString::number(execAction.status()); + setMetaData(QStringLiteral("TestData"), testData.join(QStringLiteral(","))); + return true; + } + + auto reply = execAction.execute(); + if (reply->exec()) { + return true; + } + + return false; +} diff --git a/src/ioslaves/file/file_win.cpp b/src/ioslaves/file/file_win.cpp --- a/src/ioslaves/file/file_win.cpp +++ b/src/ioslaves/file/file_win.cpp @@ -374,3 +374,9 @@ finished(); } + +bool FileProtocol::execWithElevatedPrivilege(int, ActionType, const QVariant &, + const QVariant &, const QVariant &) +{ + return false; +}