diff --git a/src/app/application.cpp b/src/app/application.cpp index eb7db1634..0ac15cb4f 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -339,7 +339,7 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const program.replace("%C", QString::number(torrent->filesCount())); program.replace("%Z", QString::number(torrent->totalSize())); program.replace("%T", torrent->currentTracker()); - program.replace("%I", torrent->hash()); + program.replace("%I", torrent->hash().toString()); Logger *logger = Logger::instance(); logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program)); diff --git a/src/base/bittorrent/infohash.cpp b/src/base/bittorrent/infohash.cpp index 3a2c0371c..f7ea61a8c 100644 --- a/src/base/bittorrent/infohash.cpp +++ b/src/base/bittorrent/infohash.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015, 2021 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,28 +36,13 @@ using namespace BitTorrent; const int InfoHashTypeId = qRegisterMetaType(); InfoHash::InfoHash(const lt::sha1_hash &nativeHash) - : m_valid(true) - , m_nativeHash(nativeHash) + : m_valid {true} + , m_nativeHash {nativeHash} { const QByteArray raw = QByteArray::fromRawData(nativeHash.data(), length()); m_hashString = QString::fromLatin1(raw.toHex()); } -InfoHash::InfoHash(const QString &hashString) - : m_valid(false) -{ - if (hashString.size() != (length() * 2)) - return; - - const QByteArray raw = QByteArray::fromHex(hashString.toLatin1()); - if (raw.size() != length()) // QByteArray::fromHex() will skip over invalid characters - return; - - m_valid = true; - m_hashString = hashString; - m_nativeHash.assign(raw.constData()); -} - bool InfoHash::isValid() const { return m_valid; @@ -68,7 +53,24 @@ InfoHash::operator lt::sha1_hash() const return m_nativeHash; } -InfoHash::operator QString() const +InfoHash InfoHash::fromString(const QString &hashString) +{ + if (hashString.size() != (length() * 2)) + return {}; + + const QByteArray raw = QByteArray::fromHex(hashString.toLatin1()); + if (raw.size() != length()) // QByteArray::fromHex() will skip over invalid characters + return {}; + + InfoHash result; + result.m_valid = true; + result.m_hashString = hashString; + result.m_nativeHash.assign(raw.constData()); + + return result; +} + +QString InfoHash::toString() const { return m_hashString; } diff --git a/src/base/bittorrent/infohash.h b/src/base/bittorrent/infohash.h index 81c35f5dd..1e2ec3b7f 100644 --- a/src/base/bittorrent/infohash.h +++ b/src/base/bittorrent/infohash.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015, 2021 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,7 +40,6 @@ namespace BitTorrent public: InfoHash() = default; InfoHash(const lt::sha1_hash &nativeHash); - InfoHash(const QString &hashString); InfoHash(const InfoHash &other) = default; static constexpr int length() @@ -51,7 +50,9 @@ namespace BitTorrent bool isValid() const; operator lt::sha1_hash() const; - operator QString() const; + + static InfoHash fromString(const QString &hashString); + QString toString() const; private: bool m_valid = false; diff --git a/src/base/bittorrent/magneturi.cpp b/src/base/bittorrent/magneturi.cpp index 4800cb41a..7bc44a242 100644 --- a/src/base/bittorrent/magneturi.cpp +++ b/src/base/bittorrent/magneturi.cpp @@ -40,7 +40,7 @@ namespace { - bool isBitTorrentInfoHash(const QString &string) + bool isSHA1Hash(const QString &string) { // There are 2 representations for BitTorrent info hash: // 1. 40 chars hex-encoded string @@ -65,7 +65,7 @@ MagnetUri::MagnetUri(const QString &source) { if (source.isEmpty()) return; - if (isBitTorrentInfoHash(source)) + if (isSHA1Hash(source)) m_url = QLatin1String("magnet:?xt=urn:btih:") + source; lt::error_code ec; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index a6d0f6acc..dd13629ea 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -1802,7 +1802,7 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio TorrentImpl *const torrent = m_torrents.take(hash); if (!torrent) return false; - qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash())); + qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash().toString())); emit torrentAboutToBeRemoved(torrent); // Remove it from session @@ -1856,8 +1856,8 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio } // Remove it from torrent resume directory - const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->hash()); - const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->hash()); + const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->hash().toString()); + const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->hash().toString()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QMetaObject::invokeMethod(m_resumeDataSavingManager, [this, resumedataFile, metadataFile]() { @@ -2254,7 +2254,7 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri) if (m_downloadedMetadata.contains(hash)) return false; qDebug("Adding torrent to preload metadata..."); - qDebug(" -> Hash: %s", qUtf8Printable(hash)); + qDebug(" -> Hash: %s", qUtf8Printable(hash.toString())); qDebug(" -> Name: %s", qUtf8Printable(name)); lt::add_torrent_params p = magnetUri.addTorrentParams(); @@ -2270,7 +2270,7 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri) p.max_connections = maxConnectionsPerTorrent(); p.max_uploads = maxUploadsPerTorrent(); - const QString savePath = Utils::Fs::tempPath() + static_cast(hash); + const QString savePath = Utils::Fs::tempPath() + hash.toString(); p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); // Forced start @@ -2303,7 +2303,7 @@ void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder fold ((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty())); const QString validName = Utils::Fs::toValidFileSystemName(torrent->name()); - const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->hash()); + const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->hash().toString()); QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName); const QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename); const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory()); @@ -2376,7 +2376,7 @@ void Session::saveTorrentsQueue() const // We require actual (non-cached) queue position here! const int queuePos = static_cast>(torrent->nativeHandle().queue_position()); if (queuePos >= 0) - queue[queuePos] = torrent->hash(); + queue[queuePos] = torrent->hash().toString(); } QByteArray data; @@ -3856,7 +3856,7 @@ void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent) { // Save metadata const QDir resumeDataDir {m_resumeFolderPath}; - const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; + const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash().toString())}; try { torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName)); @@ -3936,7 +3936,7 @@ void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const std // Separated thread is used for the blocking IO which results in slow processing of many torrents. // Copying lt::entry objects around isn't cheap. - const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash()); + const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash().toString()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QMetaObject::invokeMethod(m_resumeDataSavingManager , [this, filename, data]() { m_resumeDataSavingManager->save(filename, data); }); @@ -4014,7 +4014,7 @@ void Session::moveTorrentStorage(const MoveStorageJob &job) const { const InfoHash infoHash = job.torrentHandle.info_hash(); const TorrentImpl *torrent = m_torrents.value(infoHash); - const QString torrentName = (torrent ? torrent->name() : QString {infoHash}); + const QString torrentName = (torrent ? torrent->name() : infoHash.toString()); LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path)); job.torrentHandle.move_storage(job.path.toUtf8().constData() @@ -4587,7 +4587,7 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle) { // Backup torrent file const QDir resumeDataDir {m_resumeFolderPath}; - const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; + const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->hash().toString())}; try { torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName)); @@ -4925,7 +4925,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p) const InfoHash infoHash = currentJob.torrentHandle.info_hash(); TorrentImpl *torrent = m_torrents.value(infoHash); - const QString torrentName = (torrent ? torrent->name() : QString {infoHash}); + const QString torrentName = (torrent ? torrent->name() : infoHash.toString()); LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath)); handleMoveTorrentStorageJobFinished(); @@ -4940,7 +4940,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert const InfoHash infoHash = currentJob.torrentHandle.info_hash(); TorrentImpl *torrent = m_torrents.value(infoHash); - const QString torrentName = (torrent ? torrent->name() : QString {infoHash}); + const QString torrentName = (torrent ? torrent->name() : infoHash.toString()); const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path); const QString errorMessage = QString::fromStdString(p->message()); LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.") diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index dc86751b8..ed29b1f05 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -185,7 +185,7 @@ QString TorrentImpl::name() const if (!name.isEmpty()) return name; - return m_hash; + return m_hash.toString(); } QDateTime TorrentImpl::creationDate() const diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index 212d50616..f4cfeae43 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -295,7 +295,7 @@ void Tracker::processAnnounceRequest() if (infoHashIter == queryParams.end()) throw TrackerError("Missing \"info_hash\" parameter"); - const InfoHash infoHash(infoHashIter->toHex()); + const auto infoHash = InfoHash::fromString(infoHashIter->toHex()); if (!infoHash.isValid()) throw TrackerError("Invalid \"info_hash\" parameter"); diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index a90c227da..f98361577 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -296,7 +296,7 @@ bool AddNewTorrentDialog::loadTorrentImpl() return false; } - m_ui->labelHashData->setText(infoHash); + m_ui->labelHashData->setText(infoHash.toString()); setupTreeview(); TMMChanged(m_ui->comboTTM->currentIndex()); return true; @@ -348,7 +348,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) BitTorrent::Session::instance()->downloadMetadata(magnetUri); setMetadataProgressIndicator(true, tr("Retrieving metadata...")); - m_ui->labelHashData->setText(infoHash); + m_ui->labelHashData->setText(infoHash.toString()); m_magnetURI = magnetUri; return true; diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index cdb68da86..b74e4312c 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -313,7 +313,7 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::Torrent *const torrent) // Save path updateSavePath(m_torrent); // Hash - m_ui->labelHashVal->setText(m_torrent->hash()); + m_ui->labelHashVal->setText(m_torrent->hash().toString()); m_propListModel->model()->clear(); if (m_torrent->hasMetadata()) { diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index c5662fb44..df1d3b638 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -496,7 +496,7 @@ void TransferListWidget::copySelectedHashes() const { QStringList torrentHashes; for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents())) - torrentHashes << torrent->hash(); + torrentHashes << torrent->hash().toString(); qApp->clipboard()->setText(torrentHashes.join('\n')); } diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index 9b987f7bd..d39115091 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -103,7 +103,7 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) }; return { - {KEY_TORRENT_HASH, QString(torrent.hash())}, + {KEY_TORRENT_HASH, QString(torrent.hash().toString())}, {KEY_TORRENT_NAME, torrent.name()}, {KEY_TORRENT_MAGNET_URI, torrent.createMagnetURI()}, {KEY_TORRENT_SIZE, torrent.wantedSize()}, diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index b8c24b39c..55df82a38 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -471,7 +471,7 @@ void SyncController::maindataAction() if (iterTorrents != lastResponse.end()) { const QVariantHash lastResponseTorrents = iterTorrents->toHash(); - const auto iterHash = lastResponseTorrents.find(torrentHash); + const auto iterHash = lastResponseTorrents.find(torrentHash.toString()); if (iterHash != lastResponseTorrents.end()) { @@ -488,9 +488,9 @@ void SyncController::maindataAction() } for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) - trackers[tracker.url()] << torrentHash; + trackers[tracker.url()] << torrentHash.toString(); - torrents[torrentHash] = map; + torrents[torrentHash.toString()] = map; } data["torrents"] = torrents; @@ -541,7 +541,7 @@ void SyncController::torrentPeersAction() auto lastResponse = sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastResponse")).toMap(); auto lastAcceptedResponse = sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastAcceptedResponse")).toMap(); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); const BitTorrent::Torrent *torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index e8149dd5b..b06dd7654 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -127,8 +127,9 @@ namespace } else { - for (const QString &hash : hashes) + for (const QString &hashString : hashes) { + const auto hash = BitTorrent::InfoHash::fromString(hashString); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (torrent) func(torrent); @@ -213,7 +214,7 @@ namespace QVector infoHashes; infoHashes.reserve(hashes.size()); for (const QString &hash : hashes) - infoHashes << hash; + infoHashes << BitTorrent::InfoHash::fromString(hash); return infoHashes; } } @@ -259,7 +260,7 @@ void TorrentsController::infoAction() InfoHashSet hashSet; for (const QString &hash : hashes) - hashSet.insert(BitTorrent::InfoHash {hash}); + hashSet.insert(BitTorrent::InfoHash::fromString(hash)); const TorrentFilter torrentFilter(filter, (hashes.isEmpty() ? TorrentFilter::AnyHash : hashSet), category); QVariantList torrentList; @@ -371,7 +372,7 @@ void TorrentsController::propertiesAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -442,7 +443,7 @@ void TorrentsController::trackersAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -478,7 +479,7 @@ void TorrentsController::webseedsAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -509,7 +510,7 @@ void TorrentsController::filesAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -555,7 +556,7 @@ void TorrentsController::pieceHashesAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -577,7 +578,7 @@ void TorrentsController::pieceStatesAction() { requireParams({"hash"}); - const QString hash {params()["hash"]}; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -683,7 +684,7 @@ void TorrentsController::addTrackersAction() { requireParams({"hash", "urls"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -702,7 +703,7 @@ void TorrentsController::editTrackerAction() { requireParams({"hash", "origUrl", "newUrl"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); const QString origUrl = params()["origUrl"]; const QString newUrl = params()["newUrl"]; @@ -745,7 +746,7 @@ void TorrentsController::removeTrackersAction() { requireParams({"hash", "urls"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -798,7 +799,7 @@ void TorrentsController::addPeersAction() return torrent->connectPeer(peer); }); - results[torrent->hash()] = QJsonObject + results[torrent->hash().toString()] = QJsonObject { {"added", peersAdded}, {"failed", (peers.size() - peersAdded)} @@ -828,7 +829,7 @@ void TorrentsController::filePrioAction() { requireParams({"hash", "id", "priority"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); bool ok = false; const auto priority = static_cast(params()["priority"].toInt(&ok)); if (!ok) @@ -874,7 +875,7 @@ void TorrentsController::uploadLimitAction() for (const QString &hash : hashes) { int limit = -1; - const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::InfoHash::fromString(hash)); if (torrent) limit = torrent->uploadLimit(); map[hash] = limit; @@ -892,7 +893,7 @@ void TorrentsController::downloadLimitAction() for (const QString &hash : hashes) { int limit = -1; - const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + const BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(BitTorrent::InfoHash::fromString(hash)); if (torrent) limit = torrent->downloadLimit(); map[hash] = limit; @@ -1064,7 +1065,7 @@ void TorrentsController::renameAction() { requireParams({"hash", "name"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); QString name = params()["name"].trimmed(); if (name.isEmpty()) @@ -1251,7 +1252,7 @@ void TorrentsController::renameFileAction() { requireParams({"hash", "oldPath", "newPath"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound); @@ -1273,7 +1274,7 @@ void TorrentsController::renameFolderAction() { requireParams({"hash", "oldPath", "newPath"}); - const QString hash = params()["hash"]; + const auto hash = BitTorrent::InfoHash::fromString(params()["hash"]); BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) throw APIError(APIErrorType::NotFound);