Improve "info hash" handling

Define "torrent ID" concept, which is either a SHA1 hash for torrents of version 1,
or a SHA256 hash (truncated to SHA1 hash length) for torrents of version 2.
Add support for native libtorrent2 info hashes.
This commit is contained in:
Vladimir Golovnev (Glassez)
2021-03-05 12:43:58 +03:00
parent 4da4fb0676
commit 561b597031
34 changed files with 463 additions and 320 deletions

View File

@@ -103,7 +103,8 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent)
};
return {
{KEY_TORRENT_HASH, QString(torrent.hash().toString())},
// TODO: Add fields for real SHA1 and SHA256 hashes
{KEY_TORRENT_ID, QString(torrent.id().toString())},
{KEY_TORRENT_NAME, torrent.name()},
{KEY_TORRENT_MAGNET_URI, torrent.createMagnetURI()},
{KEY_TORRENT_SIZE, torrent.wantedSize()},

View File

@@ -36,7 +36,7 @@ namespace BitTorrent
}
// Torrent keys
const char KEY_TORRENT_HASH[] = "hash";
const char KEY_TORRENT_ID[] = "hash";
const char KEY_TORRENT_NAME[] = "name";
const char KEY_TORRENT_MAGNET_URI[] = "magnet_uri";
const char KEY_TORRENT_SIZE[] = "size";

View File

@@ -460,10 +460,10 @@ void SyncController::maindataAction()
QHash<QString, QStringList> trackers;
for (const BitTorrent::Torrent *torrent : asConst(session->torrents()))
{
const BitTorrent::InfoHash torrentHash = torrent->hash();
const BitTorrent::TorrentID torrentID = torrent->id();
QVariantMap map = serialize(*torrent);
map.remove(KEY_TORRENT_HASH);
map.remove(KEY_TORRENT_ID);
// Calculated last activity time can differ from actual value by up to 10 seconds (this is a libtorrent issue).
// So we don't need unnecessary updates of last activity time in response.
@@ -471,11 +471,11 @@ void SyncController::maindataAction()
if (iterTorrents != lastResponse.end())
{
const QVariantHash lastResponseTorrents = iterTorrents->toHash();
const auto iterHash = lastResponseTorrents.find(torrentHash.toString());
const auto iterID = lastResponseTorrents.find(torrentID.toString());
if (iterHash != lastResponseTorrents.end())
if (iterID != lastResponseTorrents.end())
{
const QVariantMap torrentData = iterHash->toMap();
const QVariantMap torrentData = iterID->toMap();
const auto iterLastActivity = torrentData.find(KEY_TORRENT_LAST_ACTIVITY_TIME);
if (iterLastActivity != torrentData.end())
@@ -488,9 +488,9 @@ void SyncController::maindataAction()
}
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers()))
trackers[tracker.url] << torrentHash.toString();
trackers[tracker.url] << torrentID.toString();
torrents[torrentHash.toString()] = map;
torrents[torrentID.toString()] = map;
}
data["torrents"] = torrents;
@@ -534,15 +534,15 @@ void SyncController::maindataAction()
}
// GET param:
// - hash (string): torrent hash
// - hash (string): torrent hash (ID)
// - rid (int): last response id
void SyncController::torrentPeersAction()
{
auto lastResponse = sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastResponse")).toMap();
auto lastAcceptedResponse = sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastAcceptedResponse")).toMap();
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const BitTorrent::Torrent *torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
const BitTorrent::Torrent *torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);

View File

@@ -120,18 +120,18 @@ namespace
using Utils::String::parseInt;
using Utils::String::parseDouble;
void applyToTorrents(const QStringList &hashes, const std::function<void (BitTorrent::Torrent *torrent)> &func)
void applyToTorrents(const QStringList &idList, const std::function<void (BitTorrent::Torrent *torrent)> &func)
{
if ((hashes.size() == 1) && (hashes[0] == QLatin1String("all")))
if ((idList.size() == 1) && (idList[0] == QLatin1String("all")))
{
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
func(torrent);
}
else
{
for (const QString &hashString : hashes)
for (const QString &idString : idList)
{
const auto hash = BitTorrent::InfoHash::fromString(hashString);
const auto hash = BitTorrent::TorrentID::fromString(idString);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (torrent)
func(torrent);
@@ -211,20 +211,20 @@ namespace
return {dht, pex, lsd};
}
QVector<BitTorrent::InfoHash> toInfoHashes(const QStringList &hashes)
QVector<BitTorrent::TorrentID> toTorrentIDs(const QStringList &idStrings)
{
QVector<BitTorrent::InfoHash> infoHashes;
infoHashes.reserve(hashes.size());
for (const QString &hash : hashes)
infoHashes << BitTorrent::InfoHash::fromString(hash);
return infoHashes;
QVector<BitTorrent::TorrentID> idList;
idList.reserve(idStrings.size());
for (const QString &hash : idStrings)
idList << BitTorrent::TorrentID::fromString(hash);
return idList;
}
}
// Returns all the torrents in JSON format.
// The return value is a JSON-formatted list of dictionaries.
// The dictionary keys are:
// - "hash": Torrent hash
// - "hash": Torrent hash (ID)
// - "name": Torrent name
// - "size": Torrent size
// - "progress": Torrent progress
@@ -260,11 +260,11 @@ void TorrentsController::infoAction()
int offset {params()["offset"].toInt()};
const QStringList hashes {params()["hashes"].split('|', QString::SkipEmptyParts)};
InfoHashSet hashSet;
TorrentIDSet idSet;
for (const QString &hash : hashes)
hashSet.insert(BitTorrent::InfoHash::fromString(hash));
idSet.insert(BitTorrent::TorrentID::fromString(hash));
const TorrentFilter torrentFilter(filter, (hashes.isEmpty() ? TorrentFilter::AnyHash : hashSet), category);
const TorrentFilter torrentFilter(filter, (hashes.isEmpty() ? TorrentFilter::AnyID : idSet), category);
QVariantList torrentList;
for (const BitTorrent::Torrent *torrent : asConst(BitTorrent::Session::instance()->torrents()))
{
@@ -374,8 +374,8 @@ void TorrentsController::propertiesAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -445,8 +445,8 @@ void TorrentsController::trackersAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -481,8 +481,8 @@ void TorrentsController::webseedsAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -512,8 +512,8 @@ void TorrentsController::filesAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -558,8 +558,8 @@ void TorrentsController::pieceHashesAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -580,8 +580,8 @@ void TorrentsController::pieceStatesAction()
{
requireParams({"hash"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -691,8 +691,8 @@ void TorrentsController::addTrackersAction()
{
requireParams({"hash", "urls"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -710,11 +710,11 @@ void TorrentsController::editTrackerAction()
{
requireParams({"hash", "origUrl", "newUrl"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
const QString origUrl = params()["origUrl"];
const QString newUrl = params()["newUrl"];
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -751,8 +751,8 @@ void TorrentsController::removeTrackersAction()
{
requireParams({"hash", "urls"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -804,7 +804,7 @@ void TorrentsController::addPeersAction()
return torrent->connectPeer(peer);
});
results[torrent->hash().toString()] = QJsonObject
results[torrent->id().toString()] = QJsonObject
{
{"added", peersAdded},
{"failed", (peers.size() - peersAdded)}
@@ -826,15 +826,15 @@ void TorrentsController::resumeAction()
{
requireParams({"hashes"});
const QStringList hashes = params()["hashes"].split('|');
applyToTorrents(hashes, [](BitTorrent::Torrent *const torrent) { torrent->resume(); });
const QStringList idStrings = params()["hashes"].split('|');
applyToTorrents(idStrings, [](BitTorrent::Torrent *const torrent) { torrent->resume(); });
}
void TorrentsController::filePrioAction()
{
requireParams({"hash", "id", "priority"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
bool ok = false;
const auto priority = static_cast<BitTorrent::DownloadPriority>(params()["priority"].toInt(&ok));
if (!ok)
@@ -843,7 +843,7 @@ void TorrentsController::filePrioAction()
if (!BitTorrent::isValidDownloadPriority(priority))
throw APIError(APIErrorType::BadParams, tr("Priority is not valid"));
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
if (!torrent->hasMetadata())
@@ -875,15 +875,15 @@ void TorrentsController::uploadLimitAction()
{
requireParams({"hashes"});
const QStringList hashes {params()["hashes"].split('|')};
const QStringList idList {params()["hashes"].split('|')};
QJsonObject map;
for (const QString &hash : hashes)
for (const QString &id : idList)
{
int limit = -1;
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::InfoHash::fromString(hash));
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::TorrentID::fromString(id));
if (torrent)
limit = torrent->uploadLimit();
map[hash] = limit;
map[id] = limit;
}
setResult(map);
@@ -893,15 +893,15 @@ void TorrentsController::downloadLimitAction()
{
requireParams({"hashes"});
const QStringList hashes {params()["hashes"].split('|')};
const QStringList idList {params()["hashes"].split('|')};
QJsonObject map;
for (const QString &hash : hashes)
for (const QString &id : idList)
{
int limit = -1;
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::InfoHash::fromString(hash));
const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::TorrentID::fromString(id));
if (torrent)
limit = torrent->downloadLimit();
map[hash] = limit;
map[id] = limit;
}
setResult(map);
@@ -992,7 +992,7 @@ void TorrentsController::deleteAction()
? DeleteTorrentAndFiles : DeleteTorrent;
applyToTorrents(hashes, [deleteOption](const BitTorrent::Torrent *torrent)
{
BitTorrent::Session::instance()->deleteTorrent(torrent->hash(), deleteOption);
BitTorrent::Session::instance()->deleteTorrent(torrent->id(), deleteOption);
});
}
@@ -1004,7 +1004,7 @@ void TorrentsController::increasePrioAction()
throw APIError(APIErrorType::Conflict, tr("Torrent queueing must be enabled"));
const QStringList hashes {params()["hashes"].split('|')};
BitTorrent::Session::instance()->increaseTorrentsQueuePos(toInfoHashes(hashes));
BitTorrent::Session::instance()->increaseTorrentsQueuePos(toTorrentIDs(hashes));
}
void TorrentsController::decreasePrioAction()
@@ -1015,7 +1015,7 @@ void TorrentsController::decreasePrioAction()
throw APIError(APIErrorType::Conflict, tr("Torrent queueing must be enabled"));
const QStringList hashes {params()["hashes"].split('|')};
BitTorrent::Session::instance()->decreaseTorrentsQueuePos(toInfoHashes(hashes));
BitTorrent::Session::instance()->decreaseTorrentsQueuePos(toTorrentIDs(hashes));
}
void TorrentsController::topPrioAction()
@@ -1026,7 +1026,7 @@ void TorrentsController::topPrioAction()
throw APIError(APIErrorType::Conflict, tr("Torrent queueing must be enabled"));
const QStringList hashes {params()["hashes"].split('|')};
BitTorrent::Session::instance()->topTorrentsQueuePos(toInfoHashes(hashes));
BitTorrent::Session::instance()->topTorrentsQueuePos(toTorrentIDs(hashes));
}
void TorrentsController::bottomPrioAction()
@@ -1037,7 +1037,7 @@ void TorrentsController::bottomPrioAction()
throw APIError(APIErrorType::Conflict, tr("Torrent queueing must be enabled"));
const QStringList hashes {params()["hashes"].split('|')};
BitTorrent::Session::instance()->bottomTorrentsQueuePos(toInfoHashes(hashes));
BitTorrent::Session::instance()->bottomTorrentsQueuePos(toTorrentIDs(hashes));
}
void TorrentsController::setLocationAction()
@@ -1070,13 +1070,13 @@ void TorrentsController::renameAction()
{
requireParams({"hash", "name"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
QString name = params()["name"].trimmed();
if (name.isEmpty())
throw APIError(APIErrorType::Conflict, tr("Incorrect torrent name"));
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -1257,8 +1257,8 @@ void TorrentsController::renameFileAction()
{
requireParams({"hash", "oldPath", "newPath"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);
@@ -1279,8 +1279,8 @@ void TorrentsController::renameFolderAction()
{
requireParams({"hash", "oldPath", "newPath"});
const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
const auto id = BitTorrent::TorrentID::fromString(params()["hash"]);
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(id);
if (!torrent)
throw APIError(APIErrorType::NotFound);