File indexing completed on 2025-02-02 03:49:25
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kio_trash.h" 0009 #include "../../utils_p.h" 0010 #include "kiotrashdebug.h" 0011 #include "transferjob.h" 0012 0013 #include <KDirNotify> 0014 #include <kio/jobuidelegateextension.h> 0015 0016 #include <KLocalizedString> 0017 0018 #include <QCoreApplication> 0019 #include <QDataStream> 0020 #include <QEventLoop> 0021 #include <QFile> 0022 #include <QJsonDocument> 0023 #include <QJsonObject> 0024 #include <QMimeDatabase> 0025 #include <QMimeType> 0026 0027 #include <grp.h> 0028 #include <pwd.h> 0029 #include <sys/stat.h> 0030 #include <sys/types.h> 0031 0032 // Pseudo plugin class to embed meta data 0033 class KIOPluginForMetaData : public QObject 0034 { 0035 Q_OBJECT 0036 Q_PLUGIN_METADATA(IID "org.kde.kio.worker.trash" FILE "trash.json") 0037 }; 0038 0039 extern "C" { 0040 int Q_DECL_EXPORT kdemain(int argc, char **argv) 0041 { 0042 // necessary to use other KIO workers 0043 QCoreApplication app(argc, argv); 0044 0045 KIO::setDefaultJobUiDelegateExtension(nullptr); 0046 // start the worker 0047 TrashProtocol worker(argv[1], argv[2], argv[3]); 0048 worker.dispatchLoop(); 0049 return 0; 0050 } 0051 } 0052 0053 static bool isTopLevelEntry(const QUrl &url) 0054 { 0055 const QString dir = url.adjusted(QUrl::RemoveFilename).path(); 0056 return dir.length() <= 1; 0057 } 0058 0059 TrashProtocol::TrashProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app) 0060 : WorkerBase(protocol, pool, app) 0061 { 0062 m_userId = getuid(); 0063 struct passwd *user = getpwuid(m_userId); 0064 if (user) { 0065 m_userName = QString::fromLatin1(user->pw_name); 0066 } 0067 m_groupId = getgid(); 0068 struct group *grp = getgrgid(m_groupId); 0069 if (grp) { 0070 m_groupName = QString::fromLatin1(grp->gr_name); 0071 } 0072 } 0073 0074 TrashProtocol::~TrashProtocol() 0075 { 0076 } 0077 0078 KIO::WorkerResult TrashProtocol::initImpl() 0079 { 0080 if (!impl.init()) { 0081 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0082 } 0083 0084 return KIO::WorkerResult::pass(); 0085 } 0086 0087 KIO::WorkerResult TrashProtocol::enterLoop() 0088 { 0089 int errorId = 0; 0090 QString errorText; 0091 0092 QEventLoop eventLoop; 0093 connect(this, &TrashProtocol::leaveModality, &eventLoop, [&](int _errorId, const QString &_errorText) { 0094 errorId = _errorId; 0095 errorText = _errorText; 0096 eventLoop.quit(); 0097 }); 0098 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 0099 0100 if (errorId != 0) { 0101 return KIO::WorkerResult::fail(errorId, errorText); 0102 } 0103 return KIO::WorkerResult::pass(); 0104 } 0105 0106 KIO::WorkerResult TrashProtocol::restore(const QUrl &trashURL) 0107 { 0108 int trashId; 0109 QString fileId; 0110 QString relativePath; 0111 bool ok = TrashImpl::parseURL(trashURL, trashId, fileId, relativePath); 0112 if (!ok) { 0113 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", trashURL.toString())); 0114 } 0115 TrashedFileInfo info; 0116 ok = impl.infoForFile(trashId, fileId, info); 0117 if (!ok) { 0118 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0119 } 0120 QUrl dest = QUrl::fromLocalFile(info.origPath); 0121 if (!relativePath.isEmpty()) { 0122 dest.setPath(Utils::concatPaths(dest.path(), relativePath)); 0123 } 0124 0125 // Check that the destination directory exists, to improve the error code in case it doesn't. 0126 const QString destDir = dest.adjusted(QUrl::RemoveFilename).path(); 0127 QT_STATBUF buff; 0128 0129 if (QT_LSTAT(QFile::encodeName(destDir).constData(), &buff) == -1) { 0130 return KIO::WorkerResult::fail( 0131 KIO::ERR_WORKER_DEFINED, 0132 i18n("The directory %1 does not exist anymore, so it is not possible to restore this item to its original location. " 0133 "You can either recreate that directory and use the restore operation again, or drag the item anywhere else to restore it.", 0134 destDir)); 0135 } 0136 0137 return copyOrMoveFromTrash(trashURL, dest, false /*overwrite*/, Move); 0138 } 0139 0140 KIO::WorkerResult TrashProtocol::rename(const QUrl &oldURL, const QUrl &newURL, KIO::JobFlags flags) 0141 { 0142 if (const auto initResult = initImpl(); !initResult.success()) { 0143 return initResult; 0144 } 0145 0146 qCDebug(KIO_TRASH) << "TrashProtocol::rename(): old=" << oldURL << " new=" << newURL << " overwrite=" << (flags & KIO::Overwrite); 0147 0148 if (oldURL.scheme() == QLatin1String("trash") && newURL.scheme() == QLatin1String("trash")) { 0149 if (!isTopLevelEntry(oldURL) || !isTopLevelEntry(newURL)) { 0150 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_RENAME, oldURL.toString()); 0151 } 0152 int oldTrashId; 0153 QString oldFileId; 0154 QString oldRelativePath; 0155 bool oldOk = TrashImpl::parseURL(oldURL, oldTrashId, oldFileId, oldRelativePath); 0156 if (!oldOk) { 0157 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", oldURL.toString())); 0158 } 0159 if (!oldRelativePath.isEmpty()) { 0160 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_RENAME, oldURL.toString()); 0161 } 0162 // Dolphin/KIO can't specify a trashid in the new URL so here path == filename 0163 // bool newOk = TrashImpl::parseURL(newURL, newTrashId, newFileId, newRelativePath); 0164 const QString newFileId = newURL.path().mid(1); 0165 if (newFileId.contains(QLatin1Char('/'))) { 0166 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_RENAME, oldURL.toString()); 0167 } 0168 bool ok = impl.moveInTrash(oldTrashId, oldFileId, newFileId); 0169 if (!ok) { 0170 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0171 } 0172 const QUrl finalUrl = TrashImpl::makeURL(oldTrashId, newFileId, QString()); 0173 org::kde::KDirNotify::emitFileRenamed(oldURL, finalUrl); 0174 return KIO::WorkerResult::pass(); 0175 } 0176 0177 if (oldURL.scheme() == QLatin1String("trash") && newURL.isLocalFile()) { 0178 return copyOrMoveFromTrash(oldURL, newURL, (flags & KIO::Overwrite), Move); 0179 } 0180 if (oldURL.isLocalFile() && newURL.scheme() == QLatin1String("trash")) { 0181 return copyOrMoveToTrash(oldURL, newURL, Move); 0182 } 0183 return KIO::WorkerResult::fail(KIO::ERR_UNSUPPORTED_ACTION, i18n("Invalid combination of protocols.")); 0184 } 0185 0186 KIO::WorkerResult TrashProtocol::copy(const QUrl &src, const QUrl &dest, int /*permissions*/, KIO::JobFlags flags) 0187 { 0188 if (const auto initResult = initImpl(); !initResult.success()) { 0189 return initResult; 0190 } 0191 0192 qCDebug(KIO_TRASH) << "TrashProtocol::copy(): " << src << " " << dest; 0193 0194 if (src.scheme() == QLatin1String("trash") && dest.scheme() == QLatin1String("trash")) { 0195 return KIO::WorkerResult::fail(KIO::ERR_UNSUPPORTED_ACTION, i18n("This file is already in the trash bin.")); 0196 } 0197 0198 if (src.scheme() == QLatin1String("trash") && dest.isLocalFile()) { 0199 return copyOrMoveFromTrash(src, dest, (flags & KIO::Overwrite), Copy); 0200 } 0201 if (src.isLocalFile() && dest.scheme() == QLatin1String("trash")) { 0202 return copyOrMoveToTrash(src, dest, Copy); 0203 } 0204 return KIO::WorkerResult::fail(KIO::ERR_UNSUPPORTED_ACTION, i18n("Invalid combination of protocols.")); 0205 } 0206 0207 KIO::WorkerResult TrashProtocol::copyOrMoveFromTrash(const QUrl &src, const QUrl &dest, bool overwrite, CopyOrMove action) 0208 { 0209 // Extracting (e.g. via dnd). Ignore original location stored in info file. 0210 int trashId; 0211 QString fileId; 0212 QString relativePath; 0213 bool ok = TrashImpl::parseURL(src, trashId, fileId, relativePath); 0214 if (!ok) { 0215 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", src.toString())); 0216 } 0217 const QString destPath = dest.path(); 0218 if (QFile::exists(destPath)) { 0219 if (overwrite) { 0220 ok = QFile::remove(destPath); 0221 Q_ASSERT(ok); // ### TODO 0222 } else { 0223 return KIO::WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0224 } 0225 } 0226 0227 if (action == Move) { 0228 qCDebug(KIO_TRASH) << "calling moveFromTrash(" << destPath << " " << trashId << " " << fileId << ")"; 0229 ok = impl.moveFromTrash(destPath, trashId, fileId, relativePath); 0230 } else { // Copy 0231 qCDebug(KIO_TRASH) << "calling copyFromTrash(" << destPath << " " << trashId << " " << fileId << ")"; 0232 ok = impl.copyFromTrash(destPath, trashId, fileId, relativePath); 0233 } 0234 if (!ok) { 0235 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0236 } 0237 0238 if (action == Move && relativePath.isEmpty()) { 0239 (void)impl.deleteInfo(trashId, fileId); 0240 } 0241 return KIO::WorkerResult::pass(); 0242 } 0243 0244 KIO::WorkerResult TrashProtocol::copyOrMoveToTrash(const QUrl &src, const QUrl &dest, CopyOrMove action) 0245 { 0246 qCDebug(KIO_TRASH) << "trashing a file" << src << dest; 0247 0248 // Trashing a file 0249 // We detect the case where this isn't normal trashing, but 0250 // e.g. if kwrite tries to save (moving tempfile over destination) 0251 if (isTopLevelEntry(dest) && src.fileName() == dest.fileName()) { // new toplevel entry 0252 const QString srcPath = src.path(); 0253 // In theory we should use TrashImpl::parseURL to give the right filename to createInfo, 0254 // in case the trash URL didn't contain the same filename as srcPath. 0255 // But this can only happen with copyAs/moveAs, not available in the GUI 0256 // for the trash (New/... or Rename from iconview/listview). 0257 int trashId; 0258 QString fileId; 0259 if (!impl.createInfo(srcPath, trashId, fileId)) { 0260 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0261 } 0262 bool ok; 0263 if (action == Move) { 0264 qCDebug(KIO_TRASH) << "calling moveToTrash(" << srcPath << " " << trashId << " " << fileId << ")"; 0265 ok = impl.moveToTrash(srcPath, trashId, fileId); 0266 } else { // Copy 0267 qCDebug(KIO_TRASH) << "calling copyToTrash(" << srcPath << " " << trashId << " " << fileId << ")"; 0268 ok = impl.copyToTrash(srcPath, trashId, fileId); 0269 } 0270 if (!ok) { 0271 (void)impl.deleteInfo(trashId, fileId); 0272 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0273 } 0274 // Inform caller of the final URL. Used by konq_undo. 0275 const QUrl url = impl.makeURL(trashId, fileId, QString()); 0276 setMetaData(QLatin1String("trashURL-") + srcPath, url.url()); 0277 return KIO::WorkerResult::pass(); 0278 } 0279 0280 qCDebug(KIO_TRASH) << "returning KIO::ERR_ACCESS_DENIED, it's not allowed to add a file to an existing trash directory"; 0281 // It's not allowed to add a file to an existing trash directory. 0282 return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED, dest.toString()); 0283 } 0284 0285 void TrashProtocol::createTopLevelDirEntry(KIO::UDSEntry &entry) 0286 { 0287 entry.reserve(entry.count() + 8); 0288 entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); 0289 entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, i18n("Trash")); 0290 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); 0291 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, 0700); 0292 entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); 0293 entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, impl.isEmpty() ? QStringLiteral("user-trash") : QStringLiteral("user-trash-full")); 0294 entry.fastInsert(KIO::UDSEntry::UDS_USER, m_userName); 0295 entry.fastInsert(KIO::UDSEntry::UDS_GROUP, m_groupName); 0296 entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_USER_ID, m_userId); 0297 entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_GROUP_ID, m_groupId); 0298 } 0299 0300 KIO::StatDetails TrashProtocol::getStatDetails() 0301 { 0302 const QString statDetails = metaData(QStringLiteral("details")); 0303 return statDetails.isEmpty() ? KIO::StatDefaultDetails : static_cast<KIO::StatDetails>(statDetails.toInt()); 0304 } 0305 0306 KIO::WorkerResult TrashProtocol::stat(const QUrl &url) 0307 { 0308 if (const auto initResult = initImpl(); !initResult.success()) { 0309 return initResult; 0310 } 0311 0312 const QString path = url.path(); 0313 if (path.isEmpty() || path == QLatin1String("/")) { 0314 // The root is "virtual" - it's not a single physical directory 0315 KIO::UDSEntry entry = impl.trashUDSEntry(getStatDetails()); 0316 createTopLevelDirEntry(entry); 0317 statEntry(entry); 0318 } else { 0319 int trashId; 0320 QString fileId; 0321 QString relativePath; 0322 0323 bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); 0324 0325 if (!ok) { 0326 // ######## do we still need this? 0327 qCDebug(KIO_TRASH) << url << " looks fishy, returning does-not-exist"; 0328 // A URL like trash:/file simply means that CopyJob is trying to see if 0329 // the destination exists already (it made up the URL by itself). 0330 // error( KIO::ERR_WORKER_DEFINED, i18n( "Malformed URL %1" ).arg( url.toString() ) ); 0331 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toString()); 0332 } 0333 0334 qCDebug(KIO_TRASH) << "parsed" << url << "got" << trashId << fileId << relativePath; 0335 0336 const QString filePath = impl.physicalPath(trashId, fileId, relativePath); 0337 if (filePath.isEmpty()) { 0338 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0339 } 0340 0341 // For a toplevel file, use the fileId as display name (to hide the trashId) 0342 // For a file in a subdir, use the fileName as is. 0343 QString fileDisplayName = relativePath.isEmpty() ? fileId : url.fileName(); 0344 0345 QUrl fileURL; 0346 if (url.path().length() > 1) { 0347 fileURL = url; 0348 } 0349 0350 KIO::UDSEntry entry; 0351 TrashedFileInfo info; 0352 ok = impl.infoForFile(trashId, fileId, info); 0353 if (ok) { 0354 ok = createUDSEntry(filePath, fileDisplayName, fileURL.fileName(), entry, info); 0355 } 0356 0357 if (!ok) { 0358 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_STAT, url.toString()); 0359 } 0360 0361 statEntry(entry); 0362 } 0363 return KIO::WorkerResult::pass(); 0364 } 0365 0366 KIO::WorkerResult TrashProtocol::del(const QUrl &url, bool /*isfile*/) 0367 { 0368 if (const auto initResult = initImpl(); !initResult.success()) { 0369 return initResult; 0370 } 0371 0372 int trashId; 0373 QString fileId; 0374 QString relativePath; 0375 0376 bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); 0377 if (!ok) { 0378 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", url.toString())); 0379 } 0380 0381 ok = relativePath.isEmpty(); 0382 if (!ok) { 0383 return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED, url.toString()); 0384 } 0385 0386 ok = impl.del(trashId, fileId); 0387 if (!ok) { 0388 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0389 } 0390 0391 return KIO::WorkerResult::pass(); 0392 } 0393 0394 KIO::WorkerResult TrashProtocol::listDir(const QUrl &url) 0395 { 0396 if (const auto initResult = initImpl(); !initResult.success()) { 0397 return initResult; 0398 } 0399 0400 qCDebug(KIO_TRASH) << "listdir: " << url; 0401 const QString path = url.path(); 0402 if (path.isEmpty() || path == QLatin1String("/")) { 0403 return listRoot(); 0404 } 0405 int trashId; 0406 QString fileId; 0407 QString relativePath; 0408 bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); 0409 if (!ok) { 0410 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", url.toString())); 0411 } 0412 // was: const QString physicalPath = impl.physicalPath( trashId, fileId, relativePath ); 0413 0414 // Get info for deleted directory - the date of deletion and orig path will be used 0415 // for all the items in it, and we need the physicalPath. 0416 TrashedFileInfo info; 0417 ok = impl.infoForFile(trashId, fileId, info); 0418 if (!ok || info.physicalPath.isEmpty()) { 0419 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0420 } 0421 if (!relativePath.isEmpty()) { 0422 info.physicalPath += QLatin1Char('/') + relativePath; 0423 } 0424 0425 // List subdir. Can't use kio_file here since we provide our own info... 0426 qCDebug(KIO_TRASH) << "listing " << info.physicalPath; 0427 const QStringList entryNames = impl.listDir(info.physicalPath); 0428 totalSize(entryNames.count()); 0429 KIO::UDSEntry entry; 0430 for (const QString &fileName : entryNames) { 0431 if (fileName == QLatin1String("..")) { 0432 continue; 0433 } 0434 const QString filePath = info.physicalPath + QLatin1Char('/') + fileName; 0435 // shouldn't be necessary 0436 // const QString url = TrashImpl::makeURL( trashId, fileId, relativePath + '/' + fileName ); 0437 entry.clear(); 0438 TrashedFileInfo infoForItem(info); 0439 infoForItem.origPath += QLatin1Char('/') + fileName; 0440 if (createUDSEntry(filePath, fileName, fileName, entry, infoForItem)) { 0441 listEntry(entry); 0442 } 0443 } 0444 entry.clear(); 0445 return KIO::WorkerResult::pass(); 0446 } 0447 0448 bool TrashProtocol::createUDSEntry(const QString &physicalPath, 0449 const QString &displayFileName, 0450 const QString &internalFileName, 0451 KIO::UDSEntry &entry, 0452 const TrashedFileInfo &info) 0453 { 0454 entry.reserve(14); 0455 QByteArray physicalPath_c = QFile::encodeName(physicalPath); 0456 QT_STATBUF buff; 0457 if (QT_LSTAT(physicalPath_c.constData(), &buff) == -1) { 0458 qCWarning(KIO_TRASH) << "couldn't stat " << physicalPath << ", relevant trashinfo file will be removed"; 0459 impl.deleteInfo(info.trashId, info.fileId); 0460 return false; 0461 } 0462 if (S_ISLNK(buff.st_mode)) { 0463 char buffer2[1000]; 0464 int n = ::readlink(physicalPath_c.constData(), buffer2, 999); 0465 if (n != -1) { 0466 buffer2[n] = 0; 0467 } 0468 0469 // this does not follow symlink on purpose 0470 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName(buffer2)); 0471 } 0472 0473 mode_t type = buff.st_mode & S_IFMT; // extract file type 0474 mode_t access = buff.st_mode & 07777; // extract permissions 0475 access &= 07555; // make it readonly, since it's in the trashcan 0476 Q_ASSERT(!internalFileName.isEmpty()); 0477 entry.fastInsert(KIO::UDSEntry::UDS_NAME, internalFileName); // internal filename, like "0-foo" 0478 entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, displayFileName); // user-visible filename, like "foo" 0479 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type); 0480 entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_PATH, physicalPath); 0481 // if ( !url.isEmpty() ) 0482 // entry.insert( KIO::UDSEntry::UDS_URL, url ); 0483 0484 QMimeDatabase db; 0485 QMimeType mt = db.mimeTypeForFile(physicalPath); 0486 if (mt.isValid()) { 0487 entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, mt.name()); 0488 } 0489 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access); 0490 entry.fastInsert(KIO::UDSEntry::UDS_SIZE, buff.st_size); 0491 entry.fastInsert(KIO::UDSEntry::UDS_USER, m_userName); // assumption 0492 entry.fastInsert(KIO::UDSEntry::UDS_GROUP, m_groupName); // assumption 0493 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime); 0494 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime); // ## or use it for deletion time? 0495 entry.fastInsert(KIO::UDSEntry::UDS_EXTRA, info.origPath); 0496 entry.fastInsert(KIO::UDSEntry::UDS_EXTRA + 1, info.deletionDate.toString(Qt::ISODate)); 0497 return true; 0498 } 0499 0500 KIO::WorkerResult TrashProtocol::listRoot() 0501 { 0502 if (const auto initResult = initImpl(); !initResult.success()) { 0503 return initResult; 0504 } 0505 0506 const TrashedFileInfoList lst = impl.list(); 0507 totalSize(lst.count()); 0508 KIO::UDSEntry entry; 0509 createTopLevelDirEntry(entry); 0510 listEntry(entry); 0511 for (const TrashedFileInfo &fileInfo : lst) { 0512 const QUrl url = TrashImpl::makeURL(fileInfo.trashId, fileInfo.fileId, QString()); 0513 entry.clear(); 0514 const QString fileDisplayName = fileInfo.fileId; 0515 0516 if (createUDSEntry(fileInfo.physicalPath, fileDisplayName, url.fileName(), entry, fileInfo)) { 0517 listEntry(entry); 0518 } 0519 } 0520 entry.clear(); 0521 return KIO::WorkerResult::pass(); 0522 } 0523 0524 KIO::WorkerResult TrashProtocol::special(const QByteArray &data) 0525 { 0526 if (const auto initResult = initImpl(); !initResult.success()) { 0527 return initResult; 0528 } 0529 0530 QDataStream stream(data); 0531 int cmd; 0532 stream >> cmd; 0533 0534 switch (cmd) { 0535 case 1: 0536 if (!impl.emptyTrash()) { 0537 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0538 } 0539 break; 0540 case 2: 0541 impl.migrateOldTrash(); 0542 break; 0543 case 3: { 0544 QUrl url; 0545 stream >> url; 0546 return restore(url); 0547 } 0548 case 4: { 0549 QJsonObject json; 0550 const auto map = impl.trashDirectories(); 0551 for (auto it = map.begin(); it != map.end(); ++it) { 0552 json[QString::number(it.key())] = it.value(); 0553 } 0554 setMetaData(QStringLiteral("TRASH_DIRECTORIES"), QString::fromLocal8Bit(QJsonDocument(json).toJson())); 0555 sendMetaData(); 0556 break; 0557 } 0558 default: 0559 qCWarning(KIO_TRASH) << "Unknown command in special(): " << cmd; 0560 return KIO::WorkerResult::fail(KIO::ERR_UNSUPPORTED_ACTION, QString::number(cmd)); 0561 } 0562 return KIO::WorkerResult::pass(); 0563 } 0564 0565 KIO::WorkerResult TrashProtocol::put(const QUrl &url, int /*permissions*/, KIO::JobFlags) 0566 { 0567 if (const auto initResult = initImpl(); !initResult.success()) { 0568 return initResult; 0569 } 0570 0571 qCDebug(KIO_TRASH) << "put: " << url; 0572 // create deleted file. We need to get the mtime and original location from metadata... 0573 // Maybe we can find the info file for url.fileName(), in case ::rename() was called first, and failed... 0574 return KIO::WorkerResult::fail(KIO::ERR_ACCESS_DENIED, url.toString()); 0575 } 0576 0577 KIO::WorkerResult TrashProtocol::get(const QUrl &url) 0578 { 0579 if (const auto initResult = initImpl(); !initResult.success()) { 0580 return initResult; 0581 } 0582 0583 qCDebug(KIO_TRASH) << "get() : " << url; 0584 if (!url.isValid()) { 0585 // qCDebug(KIO_TRASH) << kBacktrace(); 0586 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", url.url())); 0587 } 0588 if (url.path().length() <= 1) { 0589 return KIO::WorkerResult::fail(KIO::ERR_IS_DIRECTORY, url.toString()); 0590 } 0591 int trashId; 0592 QString fileId; 0593 QString relativePath; 0594 bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); 0595 if (!ok) { 0596 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Malformed URL %1", url.toString())); 0597 } 0598 const QString physicalPath = impl.physicalPath(trashId, fileId, relativePath); 0599 if (physicalPath.isEmpty()) { 0600 return KIO::WorkerResult::fail(impl.lastErrorCode(), impl.lastErrorMessage()); 0601 } 0602 0603 // Usually we run jobs in TrashImpl (for e.g. future kdedmodule) 0604 // But for this one we wouldn't use DCOP for every bit of data... 0605 QUrl fileURL = QUrl::fromLocalFile(physicalPath); 0606 KIO::TransferJob *job = KIO::get(fileURL, KIO::NoReload, KIO::HideProgressInfo); 0607 connect(job, &KIO::TransferJob::data, this, &TrashProtocol::slotData); 0608 connect(job, &KIO::TransferJob::mimeTypeFound, this, &TrashProtocol::slotMimetype); 0609 connect(job, &KJob::result, this, &TrashProtocol::jobFinished); 0610 return enterLoop(); 0611 } 0612 0613 void TrashProtocol::slotData(KIO::Job *, const QByteArray &arr) 0614 { 0615 data(arr); 0616 } 0617 0618 void TrashProtocol::slotMimetype(KIO::Job *, const QString &mt) 0619 { 0620 mimeType(mt); 0621 } 0622 0623 void TrashProtocol::jobFinished(KJob *job) 0624 { 0625 Q_EMIT leaveModality(job->error(), job->errorText()); 0626 } 0627 0628 KIO::WorkerResult TrashProtocol::fileSystemFreeSpace(const QUrl &url) 0629 { 0630 qCDebug(KIO_TRASH) << "fileSystemFreeSpace:" << url; 0631 0632 if (const auto initResult = initImpl(); !initResult.success()) { 0633 return initResult; 0634 } 0635 0636 TrashImpl::TrashSpaceInfo spaceInfo; 0637 if (!impl.trashSpaceInfo(url.path(), spaceInfo)) { 0638 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_STAT, url.toDisplayString()); 0639 } 0640 0641 setMetaData(QStringLiteral("total"), QString::number(spaceInfo.totalSize)); 0642 setMetaData(QStringLiteral("available"), QString::number(spaceInfo.availableSize)); 0643 0644 return KIO::WorkerResult::pass(); 0645 } 0646 0647 #include "kio_trash.moc" 0648 0649 #include "moc_kio_trash.cpp"