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