diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index bd124178..4c6b7aa2 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -373,7 +373,6 @@ namespace torrents return len; } - size_t Torrent::ParseInfo (std::string_view buf) { size_t len = ParseDictionary (buf, [this](std::string_view key, std::string_view buf)->size_t @@ -423,6 +422,12 @@ namespace torrents return len; } + std::string Torrent::CreateTorrentFileContent () const + { + if (m_Info.empty ()) return ""; + return CreateDictionary ( {{ "info", std::string_view ((const char *)m_Info.data (), m_Info.size ()) }} ); + } + bool Torrent::IsSafeName (std::string_view name) { if (name.empty () || name == "." || name == "..") return false; @@ -1279,7 +1284,7 @@ namespace torrents void PeerConnection::HandleBitfieldMsg (const uint8_t * buf, size_t len) { - if (!m_Torrent) return; + if (!m_Torrent || !m_Torrent->GetLength ()) return; // we are magnet and don't have torrent info yet m_IsInterested = false; size_t numPieces = m_Torrent->GetNumPieces (); m_RemoteBitfield.resize (numPieces); @@ -1640,6 +1645,9 @@ namespace torrents } return 0; }); + if (!m_Torrent->GetLength () && m_RemoteMetadataSize) // torrent is magnet and peer supports BEP9 + // request first piece of info + SendExtendedMsg (EXTENSION_MSGID_UT_METADATA, CreateDictionary ({{ "msg_type", CreateInteger (0) }, { "piece", CreateInteger (0) }})); } else { @@ -1748,11 +1756,10 @@ namespace torrents uint8_t digest[SHA_DIGEST_LENGTH]; SHA1 (m_RemoteMetadata.data (), m_RemoteMetadata.size (), digest); if (memcmp (m_Torrent->GetInfoHash ().data (), digest, SHA_DIGEST_LENGTH)) + GetTorrentsTunnel ()->UpdateTorrentInfo (m_Torrent, std::string_view ((const char *)m_RemoteMetadata.data (), m_RemoteMetadata.size ())); + else LogPrint (eLogError, "Torrents: ut_metadata info doesn't match infoHash"); - // TODO: update torrent - // cleanup - std::vector tmp; - m_RemoteMetadata.swap (tmp); + Close (); // we need to reconnect to receive bitfield } } break; diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 92729482..99f54ccf 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -162,6 +162,8 @@ namespace torrents Torrent (std::string_view buf); Torrent (const InfoHash& infoHash); + size_t ParseInfo (std::string_view buf); + std::string CreateTorrentFileContent () const; void ParseTrackerResponse (size_t trackerID, std::string_view buf); void HandleDatagramTrackerResponse (size_t trackerID, uint32_t interval, const uint8_t * hashes, size_t hashesLen, int numSeeders, int numLeechers); @@ -230,7 +232,6 @@ namespace torrents Torrent (); size_t ParsePieces (std::string_view buf); - size_t ParseInfo (std::string_view buf); size_t ParsePeers (size_t trackerID, std::string_view buf); size_t ParseFiles (std::string_view buf); static bool IsSafeName (std::string_view name); // a name from a torrent file before it becomes a path diff --git a/libi2pd_client/TorrentsTunnel.cpp b/libi2pd_client/TorrentsTunnel.cpp index f127bbf7..fbcca43e 100644 --- a/libi2pd_client/TorrentsTunnel.cpp +++ b/libi2pd_client/TorrentsTunnel.cpp @@ -134,6 +134,20 @@ namespace torrents } } + void TorrentsTunnel::SaveTorrentFile (std::shared_ptr torrent) + { + if (!torrent) return; + auto torrentFilePath = torrent->GetFullPath (); torrentFilePath += ".torrent"; + std::ofstream f(torrentFilePath, std::ofstream::binary); + if (f) + { + auto content = torrent->CreateTorrentFileContent (); + f.write (content.data (), content.size ()); + } + else + LogPrint (eLogError, "TorrentsTunnel: Can't open ", torrentFilePath); + } + void TorrentsTunnel::InitTorrentFiles (std::shared_ptr torrent) { if (!torrent) return; @@ -315,15 +329,20 @@ namespace torrents if (m_Torrents.find (torrent->GetInfoHash ()) == m_Torrents.end ()) { torrent->SetFullPath (m_TorrentsDir/std::filesystem::path (torrent->GetName ())); - { - auto torrentFilePath = torrent->GetFullPath (); torrentFilePath += ".torrent"; - std::ofstream f(torrentFilePath, std::ofstream::binary); - if (f) - f.write (torrentFileContent.data (), torrentFileContent.size ()); - else - return { torrent, 0 }; - } - InitTorrentFiles (torrent); + std::string content (torrentFileContent); + boost::asio::post (GetDiskIOService (), [this, torrent, content]() + { + auto torrentFilePath = torrent->GetFullPath (); torrentFilePath += ".torrent"; + { + // TODO: replace by SaveTorrentFile + std::ofstream f(torrentFilePath, std::ofstream::binary); + if (f) + f.write (content.data (), content.size ()); + else + LogPrint (eLogError, "TorrentsTunnel: Can't open ", torrentFilePath); + } + InitTorrentFiles (torrent); + }); return { torrent, InsertTorrent (torrent) }; } return { torrent, 0 }; @@ -502,6 +521,18 @@ namespace torrents return true; } + void TorrentsTunnel::UpdateTorrentInfo (std::shared_ptr torrent, std::string_view info) + { + if (!torrent) return; + torrent->ParseInfo (info); + torrent->SetFullPath (m_TorrentsDir/std::filesystem::path (torrent->GetName ())); + boost::asio::post (GetDiskIOService (), [this, torrent]() + { + SaveTorrentFile (torrent); + InitTorrentFiles (torrent); + }); + } + void TorrentsTunnel::Accept () { auto localDestination = GetLocalDestination (); diff --git a/libi2pd_client/TorrentsTunnel.h b/libi2pd_client/TorrentsTunnel.h index 6c0eb874..1c4a942b 100644 --- a/libi2pd_client/TorrentsTunnel.h +++ b/libi2pd_client/TorrentsTunnel.h @@ -100,6 +100,7 @@ namespace torrents std::vector GetTorrentIDs () const; std::pair, int> AddTorrent (std::string_view torrentFileContent); // retrun (torrent, id) std::pair, int> AddMagnet (std::string_view magnet); // return (torrent, id) + void UpdateTorrentInfo (std::shared_ptr torrent, std::string_view info); // magnet bool RemoveTorrent (int id, bool deleteFiles); bool StopTorrent (int id); bool StartTorrent (int id); @@ -112,6 +113,7 @@ namespace torrents void Accept (); void ReadTorrentFile (const std::filesystem::path& torrentFilePath); + void SaveTorrentFile (std::shared_ptr torrent); 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);