Warning, file /utilities/kio-stash/src/kioworker/filestash.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /***************************************************************************
0002  *   Copyright (C) 2016 by Arnav Dhamija <arnav.dhamija@gmail.com>         *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  *                                                                         *
0009  *   This program is distributed in the hope that it will be useful,       *
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0012  *   GNU General Public License for more details.                          *
0013  *                                                                         *
0014  *   You should have received a copy of the GNU General Public License     *
0015  *   along with this program; if not, write to the                         *
0016  *   Free Software Foundation, Inc.,                                       *
0017  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
0018  ***************************************************************************/
0019 
0020 #include "filestash.h"
0021 
0022 #include <QCoreApplication>
0023 #include <QDBusConnection>
0024 #include <QDBusMessage>
0025 #include <QDBusMetaType>
0026 #include <QDBusReply>
0027 #include <QDebug>
0028 #include <QDir>
0029 #include <QFile>
0030 #include <QMetaType>
0031 #include <QMimeDatabase>
0032 #include <QMimeType>
0033 #include <QUrl>
0034 
0035 #include <KConfigGroup>
0036 #include <KFileItem>
0037 #include <KIO/Job>
0038 #include <KIO/StatJob>
0039 #include <KLocalizedString>
0040 
0041 class KIOPluginForMetaData : public QObject
0042 {
0043     Q_OBJECT
0044     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.filestash" FILE "filestash.json")
0045 };
0046 
0047 extern "C" {
0048 int Q_DECL_EXPORT kdemain(int argc, char **argv)
0049 {
0050     QCoreApplication app(argc, argv);
0051     FileStash worker(argv[2], argv[3]);
0052     worker.dispatchLoop();
0053     return 0;
0054 }
0055 }
0056 
0057 FileStash::FileStash(const QByteArray &pool, const QByteArray &app, const QString &daemonService, const QString &daemonPath)
0058     : KIO::ForwardingWorkerBase("stash", pool, app)
0059     , m_daemonService(daemonService)
0060     , m_daemonPath(daemonPath)
0061 {
0062 }
0063 
0064 FileStash::~FileStash()
0065 {
0066 }
0067 
0068 bool FileStash::rewriteUrl(const QUrl &url, QUrl &newUrl)
0069 {
0070     if (url.scheme() != "file") {
0071         newUrl.setScheme("file");
0072         newUrl.setPath(url.path());
0073     } else {
0074         newUrl = url;
0075     }
0076     return true;
0077 }
0078 
0079 void FileStash::createTopLevelDirEntry(KIO::UDSEntry &entry)
0080 {
0081     entry.clear();
0082     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
0083     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0040000);
0084     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0700);
0085     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory"));
0086 }
0087 
0088 QStringList FileStash::setFileList(const QUrl &url)
0089 {
0090     QDBusMessage msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "fileList");
0091     msg << url.path();
0092     QDBusReply<QStringList> received = QDBusConnection::sessionBus().call(msg);
0093     return received.value();
0094 }
0095 
0096 QString FileStash::setFileInfo(const QUrl &url)
0097 {
0098     QDBusMessage msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "fileInfo");
0099     msg << url.path();
0100     QDBusReply<QString> received = QDBusConnection::sessionBus().call(msg);
0101     return received.value();
0102 }
0103 
0104 KIO::WorkerResult FileStash::stat(const QUrl &url)
0105 {
0106     KIO::UDSEntry entry;
0107     if (isRoot(url.path())) {
0108         createTopLevelDirEntry(entry);
0109     } else {
0110         QString fileInfo = setFileInfo(url);
0111         FileStash::dirList item = createDirListItem(fileInfo);
0112         if (!createUDSEntry(entry, item)) {
0113             return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
0114         }
0115     }
0116     statEntry(entry);
0117     return KIO::WorkerResult::pass();
0118 }
0119 
0120 bool FileStash::statUrl(const QUrl &url, KIO::UDSEntry &entry)
0121 {
0122     KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo);
0123     bool ok = statJob->exec();
0124     if (ok) {
0125         entry = statJob->statResult();
0126     }
0127     return ok;
0128 }
0129 
0130 bool FileStash::createUDSEntry(KIO::UDSEntry &entry, const FileStash::dirList &fileItem)
0131 {
0132     QMimeType fileMimetype;
0133     QMimeDatabase mimeDatabase;
0134     QString stringFilePath = fileItem.filePath;
0135 
0136     switch (fileItem.type) {
0137     case NodeType::DirectoryNode:
0138         entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0040000);
0139         entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QString("inode/directory"));
0140         entry.fastInsert(KIO::UDSEntry::UDS_NAME, QUrl(stringFilePath).fileName());
0141         entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, QUrl(stringFilePath).fileName());
0142         break;
0143     case NodeType::InvalidNode:
0144         return false;
0145     default:
0146         QByteArray physicalPath_c = QFile::encodeName(fileItem.source);
0147         QT_STATBUF buff;
0148         QT_LSTAT(physicalPath_c, &buff);
0149         mode_t access = buff.st_mode & 07777;
0150 
0151         QFileInfo entryInfo;
0152         entryInfo = QFileInfo(fileItem.source);
0153         fileMimetype = mimeDatabase.mimeTypeForFile(fileItem.source);
0154         entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, QUrl::fromLocalFile(fileItem.source).toString());
0155         entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, fileMimetype.name());
0156         entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, QUrl(stringFilePath).fileName());
0157         entry.fastInsert(KIO::UDSEntry::UDS_NAME, QUrl(stringFilePath).fileName());
0158         entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access);
0159         entry.fastInsert(KIO::UDSEntry::UDS_SIZE, entryInfo.size());
0160         entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
0161         entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
0162 
0163         if (fileItem.type == NodeType::FileNode) {
0164             entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000);
0165         } else if (fileItem.type == NodeType::SymlinkNode) {
0166             entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0120000);
0167         } else {
0168             return false;
0169         }
0170     }
0171     return true;
0172 }
0173 
0174 FileStash::dirList FileStash::createDirListItem(const QString &fileInfo)
0175 {
0176     QStringList strings = fileInfo.split("::", Qt::KeepEmptyParts);
0177     FileStash::dirList item;
0178     if (strings.at(0) == "dir") {
0179         item.type = FileStash::NodeType::DirectoryNode;
0180     } else if (strings.at(0) == "file") {
0181         item.type = FileStash::NodeType::FileNode;
0182     } else if (strings.at(0) == "symlink") {
0183         item.type = FileStash::NodeType::SymlinkNode;
0184     } else if (strings.at(0) == "invalid") {
0185         item.type = FileStash::NodeType::InvalidNode;
0186     }
0187     item.filePath = strings.at(1);
0188     item.source = strings.at(2);
0189     return item;
0190 }
0191 
0192 KIO::WorkerResult FileStash::listDir(const QUrl &url)
0193 {
0194     QStringList fileList = setFileList(url);
0195     if (!fileList.size()) {
0196         return KIO::WorkerResult::pass();
0197     }
0198     FileStash::dirList item;
0199     KIO::UDSEntry entry;
0200     if (isRoot(url.path())) {
0201         createTopLevelDirEntry(entry);
0202         listEntry(entry);
0203     }
0204     if (fileList.at(0) == "error::error::InvalidNode") {
0205         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("The file either does not exist or has not been stashed yet."));
0206     }
0207     for (auto it = fileList.begin(); it != fileList.end(); ++it) {
0208         entry.clear();
0209         item = createDirListItem(*it);
0210         if (createUDSEntry(entry, item)) {
0211             listEntry(entry);
0212         } else {
0213             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("The UDS Entry could not be created."));
0214         }
0215     }
0216     return KIO::WorkerResult::pass();
0217 }
0218 
0219 KIO::WorkerResult FileStash::mkdir(const QUrl &url, int permissions)
0220 {
0221     Q_UNUSED(permissions)
0222 
0223     QDBusMessage replyMessage;
0224     QDBusMessage msg;
0225     msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "addPath");
0226     QString destinationPath = url.path();
0227     msg << "" << destinationPath << NodeType::DirectoryNode;
0228     replyMessage = QDBusConnection::sessionBus().call(msg);
0229     if (replyMessage.type() == QDBusMessage::ErrorMessage) {
0230         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not create a directory"));
0231     }
0232     return KIO::WorkerResult::pass();
0233 }
0234 
0235 bool FileStash::copyFileToStash(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
0236 {
0237     Q_UNUSED(flags)
0238 
0239     NodeType fileType;
0240     QFileInfo fileInfo = QFileInfo(src.path());
0241     if (fileInfo.isFile()) {
0242         fileType = NodeType::FileNode;
0243     } else if (fileInfo.isSymLink()) {
0244         fileType = NodeType::SymlinkNode;
0245     } else if (fileInfo.isDir()) { // if I'm not wrong, this can never happen, but we should handle it anyway
0246         fileType = NodeType::DirectoryNode;
0247     } else {
0248         return false;
0249     }
0250 
0251     QDBusMessage replyMessage;
0252     QDBusMessage msg;
0253     msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "addPath");
0254     QString destinationPath = dest.path();
0255 
0256     msg << src.path() << destinationPath << (int)fileType;
0257     replyMessage = QDBusConnection::sessionBus().call(msg);
0258     if (replyMessage.type() != QDBusMessage::ErrorMessage) {
0259         return true;
0260     } else {
0261         return false;
0262     }
0263 }
0264 
0265 bool FileStash::copyStashToFile(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
0266 {
0267     const QString destInfo = setFileInfo(src);
0268     const FileStash::dirList fileItem = createDirListItem(destInfo);
0269 
0270     if (fileItem.type != NodeType::DirectoryNode) {
0271         QByteArray physicalPath_c = QFile::encodeName(fileItem.source);
0272         QT_STATBUF buff;
0273         QT_LSTAT(physicalPath_c, &buff);
0274         mode_t access = buff.st_mode & 07777;
0275         const auto result = KIO::ForwardingWorkerBase::copy(QUrl::fromLocalFile(fileItem.source), dest, access, flags);
0276         return result.success();
0277     }
0278     return false;
0279 }
0280 
0281 bool FileStash::copyStashToStash(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
0282 {
0283     Q_UNUSED(flags)
0284 
0285     KIO::UDSEntry entry;
0286 
0287     statUrl(src, entry);
0288     KFileItem fileItem(entry, src);
0289 
0290     const dirList item = createDirListItem(setFileInfo(src));
0291     NodeType fileType;
0292     if (fileItem.isFile()) {
0293         fileType = NodeType::FileNode;
0294     } else if (fileItem.isLink()) {
0295         fileType = NodeType::SymlinkNode;
0296     } else if (fileItem.isDir()) {
0297         fileType = NodeType::DirectoryNode;
0298     } else {
0299         return false;
0300     }
0301 
0302     QDBusMessage replyMessage;
0303     QDBusMessage msg;
0304     msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "addPath");
0305     msg << item.source << dest.path() << fileType;
0306     replyMessage = QDBusConnection::sessionBus().call(msg);
0307     if (replyMessage.type() != QDBusMessage::ErrorMessage) {
0308         return true;
0309     } else {
0310         return false;
0311     }
0312 }
0313 
0314 KIO::WorkerResult FileStash::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags)
0315 {
0316     KIO::UDSEntry entry;
0317     statUrl(src, entry);
0318     KFileItem item(entry, src);
0319     QUrl newDestPath;
0320     newDestPath = QUrl(dest.adjusted(QUrl::RemoveFilename).toString() + item.name());
0321 
0322     if (src.scheme() != "stash" && dest.scheme() == "stash") {
0323         if (copyFileToStash(src, newDestPath, flags)) {
0324             return KIO::WorkerResult::pass();
0325         }
0326 
0327         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not copy."));
0328     }
0329     if (src.scheme() == "stash" && dest.scheme() != "stash") {
0330         if (copyStashToFile(src, newDestPath, flags)) {
0331             return KIO::WorkerResult::pass();
0332         }
0333         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not copy."));
0334     }
0335     if (src.scheme() == "stash" && dest.scheme() == "stash") {
0336         if (copyStashToStash(src, newDestPath, flags)) {
0337             return KIO::WorkerResult::pass();
0338         }
0339         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not copy."));
0340     }
0341     if (dest.scheme() == "mtp") {
0342         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Copying to mtp workers is still under development!"));
0343     }
0344     return KIO::ForwardingWorkerBase::copy(item.targetUrl(), newDestPath, permissions, flags);
0345 }
0346 
0347 KIO::WorkerResult FileStash::del(const QUrl &url, bool isFile)
0348 {
0349     Q_UNUSED(isFile)
0350 
0351     if (deletePath(url)) {
0352         return KIO::WorkerResult::pass();
0353     }
0354     return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, QString("Could not reach the stash daemon"));
0355 }
0356 
0357 bool FileStash::deletePath(const QUrl &url)
0358 {
0359     NodeType fileType;
0360 
0361     QDBusMessage replyMessage;
0362     QDBusMessage msg;
0363     msg = QDBusMessage::createMethodCall(m_daemonService, m_daemonPath, "", "removePath");
0364 
0365     if (isRoot(url.adjusted(QUrl::RemoveFilename).toString())) {
0366         msg << url.fileName();
0367     } else {
0368         msg << url.path();
0369     }
0370 
0371     replyMessage = QDBusConnection::sessionBus().call(msg);
0372     if (replyMessage.type() != QDBusMessage::ErrorMessage) {
0373         return true;
0374     } else {
0375         return false;
0376     }
0377 }
0378 
0379 KIO::WorkerResult FileStash::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
0380 {
0381     if (src.scheme() == "stash" && dest.scheme() == "stash") {
0382         if (copyStashToStash(src, dest, flags)) {
0383             if (deletePath(src)) {
0384                 return KIO::WorkerResult::pass();
0385             }
0386         }
0387         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not rename."));
0388     }
0389     if (src.scheme() == "file" && dest.scheme() == "stash") {
0390         if (copyFileToStash(src, dest, flags)) {
0391             return KIO::WorkerResult::pass();
0392         }
0393         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not rename."));
0394     }
0395     if (src.scheme() == "stash" && dest.scheme() == "file") {
0396         if (copyStashToFile(src, dest, flags)) {
0397             if (deletePath(src)) {
0398                 return KIO::WorkerResult::pass();
0399             }
0400         }
0401         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Could not rename."));
0402     }
0403     return KIO::WorkerResult::fail();
0404 }
0405 
0406 bool FileStash::isRoot(const QString &string)
0407 {
0408     if (string.isEmpty() || string == "/") {
0409         return true;
0410     }
0411     return false;
0412 }
0413 
0414 #include "filestash.moc"