From 8887afe2600d8e035bf0eb0cb5397e5409da874b Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 18 Aug 2026 09:29:01 -0400 Subject: [PATCH] implemented remove torrent and torrent-remove RPC --- libi2pd_client/Torrents.cpp | 60 +++++++++++++++++++++++++++++++++- libi2pd_client/Torrents.h | 2 ++ libi2pd_client/TorrentsRPC.cpp | 27 +++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index 0a1d185c..399401c9 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -1474,7 +1474,7 @@ namespace torrents if (completed) torrent->SetComplete (); } auto resumeFilePath = torrent->GetFullPath (); resumeFilePath += ".resume"; - if (std::filesystem::exists (resumeFilePath )) + if (std::filesystem::exists (resumeFilePath)) { if (!torrent->IsComplete ()) { @@ -1644,6 +1644,64 @@ namespace torrents return 0; } + bool TorrentsTunnel::RemoveTorrent (int id, bool deleteFiles) + { + std::shared_ptr torrent; + { + std::lock_guard l(m_TorrentsMutex); + auto it = m_TorrentsByID.find (id); + if (it == m_TorrentsByID.end ()) return false; + torrent = it->second.lock (); + m_TorrentsByID.erase (it); + if (!torrent) return false; + m_Torrents.erase (torrent->GetInfoHash ()); + } + boost::asio::post (GetService (), [this, torrent, deleteFiles]() + { + RemoveTorrent (torrent, deleteFiles); + }); + return true; + } + + void TorrentsTunnel::RemoveTorrent (std::shared_ptr torrent, bool deleteFiles) + { + if (!torrent) return; + auto connections = GetTorrentConnections (torrent); + // close connections + for (auto it: connections) + it->Close (); + if (deleteFiles) + boost::asio::post (GetDiskIOService (), [torrent]() + { + auto fullPath = torrent->GetFullPath (); + auto torrentFilePath = fullPath; torrentFilePath += ".torrent"; + std::error_code ec; + std::filesystem::remove (torrentFilePath, ec); + if (ec) + LogPrint (eLogError, "Torrents: Can't delete ", torrentFilePath); + auto resumeFilePath = fullPath; resumeFilePath += ".resume"; + if (std::filesystem::exists (resumeFilePath)) + { + std::filesystem::remove (resumeFilePath, ec); + if (ec) + LogPrint (eLogError, "Torrents: Can't delete ", resumeFilePath); + } + if (torrent->IsComplete () || !torrent->GetFiles ().empty ()) + { + std::filesystem::remove_all (fullPath, ec); + if (ec) + LogPrint (eLogError, "Torrents: Can't delete ", fullPath); + } + else + { + auto partFilePath = fullPath; partFilePath += ".part"; + std::filesystem::remove (partFilePath, ec); + if (ec) + LogPrint (eLogError, "Torrents: Can't delete ", partFilePath); + } + }); + } + void TorrentsTunnel::Accept () { auto localDestination = GetLocalDestination (); diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index a7391a9e..8fcead3c 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -329,6 +329,7 @@ namespace torrents std::shared_ptr FindTorrentByID (int id) const; std::vector GetTorrentIDs () const; std::pair, int> AddTorrent (std::string_view torrentFileContent); // (tunnel, id) + bool RemoveTorrent (int id, bool deleteFiles); std::list > GetTorrentConnections (std::shared_ptr torrent); const char* GetName() const override { return m_Name.c_str (); } @@ -340,6 +341,7 @@ namespace torrents void ReadTorrentFile (const std::filesystem::path& torrentFilePath); void InitTorrentFiles (std::shared_ptr torrent); int InsertTorrent (std::shared_ptr torrent); // returns id > 0 if success and 0 if failed + void RemoveTorrent (std::shared_ptr torrent, bool deleteFiles); bool CreateAndReserveFile (const std::filesystem::path& filePath, size_t reserve); void CompleteTorrent (std::shared_ptr torrent); void RequestTracker (std::shared_ptr torrent, std::string_view event = ""); diff --git a/libi2pd_client/TorrentsRPC.cpp b/libi2pd_client/TorrentsRPC.cpp index e1862c64..e730d3c5 100644 --- a/libi2pd_client/TorrentsRPC.cpp +++ b/libi2pd_client/TorrentsRPC.cpp @@ -45,6 +45,7 @@ namespace torrents std::string ErrorResponse (JSONRPCErrorCode errorCode, int64_t id, std::string_view message); std::string HandleTorrrentAdd (boost::json::object&& jsonRequest); + std::string HandleTorrrentRemove (boost::json::object&& jsonRequest); std::string HandleTorrrentGet (boost::json::object&& jsonRequest); private: @@ -90,6 +91,8 @@ namespace torrents auto method = jsonRequest.at ("method").as_string (); if (method == "torrent-add") return HandleTorrrentAdd (std::move (jsonRequest)); + else if (method == "torrent-remove") + return HandleTorrrentRemove (std::move (jsonRequest)); else if (method == "torrent-get") return HandleTorrrentGet (std::move (jsonRequest)); else @@ -137,6 +140,30 @@ namespace torrents return SuccessResponse (jsonRequest.at ("tag").as_int64 (), std::move (response)); } + std::string JSONRPCHandler::HandleTorrrentRemove (boost::json::object&& jsonRequest) + { + auto arguments = jsonRequest.at ("arguments").as_object (); + bool deleteFiles = false; + if (arguments.contains ("delete_local_data")) // for transmission >= 4.1 + deleteFiles = arguments.at ("delete_local_data").as_bool (); + else if (arguments.contains ("delete-local-data")) // for transmission < 4.1 + deleteFiles = arguments.at ("delete-local-data").as_bool (); + std::vector torrentIds; + if (arguments.contains ("ids")) + { + auto ids = arguments.at ("ids"); + if (ids.is_array ()) + for (const auto& it: ids.as_array ()) + torrentIds.push_back (it.as_int64 ()); + else + torrentIds.push_back (ids.as_int64 ()); + } + for (auto id: torrentIds) + m_Tunnel->RemoveTorrent (id, deleteFiles); + boost::json::object response; // always empty + return SuccessResponse (jsonRequest.at ("tag").as_int64 (), std::move (response)); + } + std::string JSONRPCHandler::HandleTorrrentGet (boost::json::object&& jsonRequest) { auto arguments = jsonRequest.at ("arguments").as_object ();