diff --git a/src/core/bittorrent/session.cpp b/src/core/bittorrent/session.cpp index 9a237666c..5dec4a743 100644 --- a/src/core/bittorrent/session.cpp +++ b/src/core/bittorrent/session.cpp @@ -813,11 +813,11 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles) // Remove it from session if (deleteLocalFiles) { - QDir saveDir(torrent->actualSavePath()); - if ((saveDir != QDir(m_defaultSavePath)) && (saveDir != QDir(m_tempPath))) { - m_savePathsToRemove[torrent->hash()] = saveDir.absolutePath(); - qDebug() << "Save path to remove (async): " << saveDir.absolutePath(); - } + QString tmp = torrent->filePath(0); + tmp.truncate(tmp.indexOf("/")); // get the torrent root directory name + if (!tmp.isEmpty()) + m_savePathsToRemove[torrent->hash()] = torrent->actualSavePath() + tmp; + m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files); } else { @@ -2049,7 +2049,6 @@ void Session::handleAlert(libt::alert *a) handleMetadataReceivedAlert(static_cast(a)); dispatchTorrentAlert(a); break; - case libt::state_update_alert::alert_type: handleStateUpdateAlert(static_cast(a)); break; @@ -2065,6 +2064,9 @@ void Session::handleAlert(libt::alert *a) case libt::torrent_deleted_alert::alert_type: handleTorrentDeletedAlert(static_cast(a)); break; + case libt::torrent_delete_failed_alert::alert_type: + handleTorrentDeleteFailedAlert(static_cast(a)); + break; case libt::portmap_error_alert::alert_type: handlePortmapWarningAlert(static_cast(a)); break; @@ -2178,13 +2180,16 @@ void Session::handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p) void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p) { + m_savePathsToRemove.remove(p->info_hash); +} + +void Session::handleTorrentDeleteFailedAlert(libt::torrent_delete_failed_alert *p) +{ + // libtorrent won't delete the directory if it contains files not listed in the torrent, + // so we remove the directory ourselves if (m_savePathsToRemove.contains(p->info_hash)) { - qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); - const QString dirpath = m_savePathsToRemove.take(p->info_hash); - qDebug() << "Removing save path: " << dirpath << "..."; - bool ok = Utils::Fs::smartRemoveEmptyFolderTree(dirpath); - Q_UNUSED(ok); - qDebug() << "Folder was removed: " << ok; + QString path = m_savePathsToRemove.take(p->info_hash); + Utils::Fs::smartRemoveEmptyFolderTree(path); } } diff --git a/src/core/bittorrent/session.h b/src/core/bittorrent/session.h index b0d046a80..cf0a1ffd3 100644 --- a/src/core/bittorrent/session.h +++ b/src/core/bittorrent/session.h @@ -61,6 +61,7 @@ namespace libtorrent struct torrent_finished_alert; struct torrent_removed_alert; struct torrent_deleted_alert; + struct torrent_delete_failed_alert; struct torrent_paused_alert; struct torrent_resumed_alert; struct save_resume_data_alert; @@ -305,6 +306,7 @@ namespace BitTorrent void handleFileErrorAlert(libtorrent::file_error_alert *p); void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p); void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p); + void handleTorrentDeleteFailedAlert(libtorrent::torrent_delete_failed_alert *p); void handlePortmapWarningAlert(libtorrent::portmap_error_alert *p); void handlePortmapAlert(libtorrent::portmap_alert *p); void handlePeerBlockedAlert(libtorrent::peer_blocked_alert *p); diff --git a/src/core/utils/fs.cpp b/src/core/utils/fs.cpp index c6f277e37..053c614be 100644 --- a/src/core/utils/fs.cpp +++ b/src/core/utils/fs.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #ifdef Q_OS_MAC @@ -112,57 +113,51 @@ QString Utils::Fs::folderName(const QString& file_path) } /** - * Remove an empty folder tree. - * - * This function will also remove .DS_Store files on Mac OS and - * Thumbs.db on Windows. + * This function will first remove system cache files, e.g. `Thumbs.db`, + * `.DS_Store`. Then will try to remove the whole tree if the tree consist + * only of folders */ -bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& dir_path) +bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& path) { - qDebug() << Q_FUNC_INFO << dir_path; - if (dir_path.isEmpty()) + if (!QDir(path).exists()) return false; - QDir dir(dir_path); - if (!dir.exists()) - return true; + static const QStringList deleteFilesList = { + // Windows + "Thumbs.db", + "desktop.ini", + // Linux + ".directory", + // Mac OS + ".DS_Store" + }; - // Remove Files created by the OS -#if defined Q_OS_MAC - forceRemove(dir_path + QLatin1String("/.DS_Store")); -#elif defined Q_OS_WIN - forceRemove(dir_path + QLatin1String("/Thumbs.db")); -#endif + // travel from the deepest folder and remove anything unwanted on the way out. + QStringList dirList(path + "/"); // get all sub directories paths + QDirIterator iter(path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories); + while (iter.hasNext()) + dirList << iter.next() + "/"; + std::sort(dirList.begin(), dirList.end(), [](const QString &l, const QString &r) { return l.count("/") > r.count("/"); }); // sort descending by directory depth - QFileInfoList sub_files = dir.entryInfoList(); - foreach (const QFileInfo& info, sub_files) { - QString sub_name = info.fileName(); - if (sub_name == "." || sub_name == "..") - continue; - - QString sub_path = info.absoluteFilePath(); - qDebug() << Q_FUNC_INFO << "sub file: " << sub_path; - if (info.isDir()) { - if (!smartRemoveEmptyFolderTree(sub_path)) { - qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path; - return false; - } + for (const QString &p : dirList) { + // remove unwanted files + for (const QString &f : deleteFilesList) { + forceRemove(p + f); } - else { - if (info.isHidden()) { - qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path; - if (!forceRemove(sub_path)) { - qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path; - return false; - } - } - else { - qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path; - } + + // remove temp files on linux (file ends with '~'), e.g. `filename~` + QDir dir(p); + QStringList tmpFileList = dir.entryList(QDir::Files); + for (const QString &f : tmpFileList) { + if (f.endsWith("~")) + forceRemove(p + f); } + + // remove directory if empty + dir.rmdir(p); } - qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path; - return QDir().rmdir(dir_path); + + return QDir(path).exists(); } /** diff --git a/src/core/utils/fs.h b/src/core/utils/fs.h index 35d73ba1e..cb7db0228 100644 --- a/src/core/utils/fs.h +++ b/src/core/utils/fs.h @@ -55,7 +55,8 @@ namespace Utils bool sameFileNames(const QString& first, const QString& second); QString expandPath(const QString& path); QString expandPathAbs(const QString& path); - bool smartRemoveEmptyFolderTree(const QString& dir_path); + + bool smartRemoveEmptyFolderTree(const QString& path); bool forceRemove(const QString& file_path); void removeDirRecursive(const QString& dirName);