Files
transmission/qt/Torrent.cc
Mike Gelfand 20119f006c Fixup recent Qt client changes (#1107)
* Add EDIT_DATE torrent property declaration (Qt client)

Switch to static assertion to help avoid similar issues in the future.

* Only declare std::hash<QString> for Qt < 5.14

* Pass main window as context when connecting lambdas to torrents model signals (Qt client)

This helps to automatically disconnect from signals on main window
destruction. If not done, use after free is possible since main window is
destroyed before torrents model.

Fixes: #1106
2020-01-14 23:28:34 +02:00

1117 lines
28 KiB
C++

/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#include <cassert>
#include <iostream>
#include <QApplication>
#include <QString>
#include <QUrl>
#include <QVariant>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_new0, tr_strdup */
#include <libtransmission/variant.h>
#include "Application.h"
#include "Prefs.h"
#include "Torrent.h"
#include "Utils.h"
struct Property
{
int id;
tr_quark key;
int type;
};
Property constexpr myProperties[] =
{
{ Torrent::UPLOAD_SPEED, TR_KEY_rateUpload, QVariant::ULongLong } /* Bps */,
{ Torrent::DOWNLOAD_SPEED, TR_KEY_rateDownload, QVariant::ULongLong }, /* Bps */
{ Torrent::DOWNLOAD_DIR, TR_KEY_downloadDir, QVariant::String },
{ Torrent::ACTIVITY, TR_KEY_status, QVariant::Int },
{ Torrent::NAME, TR_KEY_name, QVariant::String },
{ Torrent::ERROR, TR_KEY_error, QVariant::Int },
{ Torrent::ERROR_STRING, TR_KEY_errorString, QVariant::String },
{ Torrent::SIZE_WHEN_DONE, TR_KEY_sizeWhenDone, QVariant::ULongLong },
{ Torrent::LEFT_UNTIL_DONE, TR_KEY_leftUntilDone, QVariant::ULongLong },
{ Torrent::HAVE_UNCHECKED, TR_KEY_haveUnchecked, QVariant::ULongLong },
{ Torrent::HAVE_VERIFIED, TR_KEY_haveValid, QVariant::ULongLong },
{ Torrent::DESIRED_AVAILABLE, TR_KEY_desiredAvailable, QVariant::ULongLong },
{ Torrent::TOTAL_SIZE, TR_KEY_totalSize, QVariant::ULongLong },
{ Torrent::PIECE_SIZE, TR_KEY_pieceSize, QVariant::ULongLong },
{ Torrent::PIECE_COUNT, TR_KEY_pieceCount, QVariant::Int },
{ Torrent::PEERS_GETTING_FROM_US, TR_KEY_peersGettingFromUs, QVariant::Int },
{ Torrent::PEERS_SENDING_TO_US, TR_KEY_peersSendingToUs, QVariant::Int },
{ Torrent::WEBSEEDS_SENDING_TO_US, TR_KEY_webseedsSendingToUs, QVariant::Int },
{ Torrent::PERCENT_DONE, TR_KEY_percentDone, QVariant::Double },
{ Torrent::METADATA_PERCENT_DONE, TR_KEY_metadataPercentComplete, QVariant::Double },
{ Torrent::PERCENT_VERIFIED, TR_KEY_recheckProgress, QVariant::Double },
{ Torrent::DATE_ACTIVITY, TR_KEY_activityDate, QVariant::DateTime },
{ Torrent::DATE_ADDED, TR_KEY_addedDate, QVariant::DateTime },
{ Torrent::DATE_STARTED, TR_KEY_startDate, QVariant::DateTime },
{ Torrent::DATE_CREATED, TR_KEY_dateCreated, QVariant::DateTime },
{ Torrent::PEERS_CONNECTED, TR_KEY_peersConnected, QVariant::Int },
{ Torrent::ETA, TR_KEY_eta, QVariant::Int },
{ Torrent::DOWNLOADED_EVER, TR_KEY_downloadedEver, QVariant::ULongLong },
{ Torrent::UPLOADED_EVER, TR_KEY_uploadedEver, QVariant::ULongLong },
{ Torrent::FAILED_EVER, TR_KEY_corruptEver, QVariant::ULongLong },
{ Torrent::TRACKERSTATS, TR_KEY_trackerStats, CustomVariantType::TrackerStatsList },
{ Torrent::MIME_ICON, TR_KEY_NONE, QVariant::Icon },
{ Torrent::SEED_RATIO_LIMIT, TR_KEY_seedRatioLimit, QVariant::Double },
{ Torrent::SEED_RATIO_MODE, TR_KEY_seedRatioMode, QVariant::Int },
{ Torrent::SEED_IDLE_LIMIT, TR_KEY_seedIdleLimit, QVariant::Int },
{ Torrent::SEED_IDLE_MODE, TR_KEY_seedIdleMode, QVariant::Int },
{ Torrent::DOWN_LIMIT, TR_KEY_downloadLimit, QVariant::Int }, /* KB/s */
{ Torrent::DOWN_LIMITED, TR_KEY_downloadLimited, QVariant::Bool },
{ Torrent::UP_LIMIT, TR_KEY_uploadLimit, QVariant::Int }, /* KB/s */
{ Torrent::UP_LIMITED, TR_KEY_uploadLimited, QVariant::Bool },
{ Torrent::HONORS_SESSION_LIMITS, TR_KEY_honorsSessionLimits, QVariant::Bool },
{ Torrent::PEER_LIMIT, TR_KEY_peer_limit, QVariant::Int },
{ Torrent::HASH_STRING, TR_KEY_hashString, QVariant::String },
{ Torrent::IS_FINISHED, TR_KEY_isFinished, QVariant::Bool },
{ Torrent::IS_PRIVATE, TR_KEY_isPrivate, QVariant::Bool },
{ Torrent::IS_STALLED, TR_KEY_isStalled, QVariant::Bool },
{ Torrent::COMMENT, TR_KEY_comment, QVariant::String },
{ Torrent::CREATOR, TR_KEY_creator, QVariant::String },
{ Torrent::MANUAL_ANNOUNCE_TIME, TR_KEY_manualAnnounceTime, QVariant::DateTime },
{ Torrent::PEERS, TR_KEY_peers, CustomVariantType::PeerList },
{ Torrent::BANDWIDTH_PRIORITY, TR_KEY_bandwidthPriority, QVariant::Int },
{ Torrent::QUEUE_POSITION, TR_KEY_queuePosition, QVariant::Int },
{ Torrent::EDIT_DATE, TR_KEY_editDate, QVariant::Int },
};
/***
****
***/
// unchanging fields needed by the main window
Torrent::KeyList const Torrent::mainInfoKeys{
TR_KEY_addedDate,
TR_KEY_downloadDir,
TR_KEY_hashString,
TR_KEY_id, // must be in every req
TR_KEY_name,
TR_KEY_totalSize,
TR_KEY_trackers,
};
// changing fields needed by the main window
Torrent::KeyList const Torrent::mainStatKeys{
TR_KEY_downloadedEver,
TR_KEY_error,
TR_KEY_errorString,
TR_KEY_eta,
TR_KEY_haveUnchecked,
TR_KEY_haveValid,
TR_KEY_id, // must be in every req
TR_KEY_isFinished,
TR_KEY_leftUntilDone,
TR_KEY_manualAnnounceTime,
TR_KEY_metadataPercentComplete,
TR_KEY_peersConnected,
TR_KEY_peersGettingFromUs,
TR_KEY_peersSendingToUs,
TR_KEY_percentDone,
TR_KEY_queuePosition,
TR_KEY_rateDownload,
TR_KEY_rateUpload,
TR_KEY_recheckProgress,
TR_KEY_seedRatioLimit,
TR_KEY_seedRatioMode,
TR_KEY_sizeWhenDone,
TR_KEY_status,
TR_KEY_uploadedEver,
TR_KEY_webseedsSendingToUs
};
Torrent::KeyList const Torrent::allMainKeys = Torrent::mainInfoKeys + Torrent::mainStatKeys;
// unchanging fields needed by the details dialog
Torrent::KeyList const Torrent::detailInfoKeys{
TR_KEY_comment,
TR_KEY_creator,
TR_KEY_dateCreated,
TR_KEY_files,
TR_KEY_id, // must be in every req
TR_KEY_isPrivate,
TR_KEY_pieceCount,
TR_KEY_pieceSize,
TR_KEY_trackers
};
// changing fields needed by the details dialog
Torrent::KeyList const Torrent::detailStatKeys{
TR_KEY_activityDate,
TR_KEY_bandwidthPriority,
TR_KEY_corruptEver,
TR_KEY_desiredAvailable,
TR_KEY_downloadedEver,
TR_KEY_downloadLimit,
TR_KEY_downloadLimited,
TR_KEY_fileStats,
TR_KEY_honorsSessionLimits,
TR_KEY_id, // must be in every req
TR_KEY_peer_limit,
TR_KEY_peers,
TR_KEY_seedIdleLimit,
TR_KEY_seedIdleMode,
TR_KEY_startDate,
TR_KEY_trackerStats,
TR_KEY_uploadLimit,
TR_KEY_uploadLimited
};
/***
****
***/
Torrent::Torrent(Prefs const& prefs, int id) :
myId(id),
myPrefs(prefs)
{
static_assert(TR_N_ELEMENTS(myProperties) == PROPERTY_COUNT);
static_assert(([] () constexpr
{
int i = 0;
for (auto const& property : myProperties)
{
if (property.id != i)
{
return false;
}
++i;
}
return true;
})());
setIcon(MIME_ICON, Utils::getFileIcon());
}
/***
****
***/
bool Torrent::setInt(int i, int value)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Int);
if (myValues[i].isNull() || myValues[i].toInt() != value)
{
myValues[i].setValue(value);
changed = true;
}
return changed;
}
bool Torrent::setBool(int i, bool value)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Bool);
if (myValues[i].isNull() || myValues[i].toBool() != value)
{
myValues[i].setValue(value);
changed = true;
}
return changed;
}
bool Torrent::setDouble(int i, double value)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Double);
if (myValues[i] != value)
{
myValues[i].setValue(value);
changed = true;
}
return changed;
}
bool Torrent::setTime(int i, time_t value)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::DateTime);
auto& oldval = myValues[i];
auto const newval = qlonglong(value);
if (oldval != newval)
{
oldval = newval;
changed = true;
}
return changed;
}
bool Torrent::setSize(int i, qulonglong value)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::ULongLong);
if (myValues[i].isNull() || myValues[i].toULongLong() != value)
{
myValues[i].setValue(value);
changed = true;
}
return changed;
}
bool Torrent::setString(int i, char const* value, size_t len)
{
bool changed = false;
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::String);
auto& oldval = myValues[i];
auto const newval = QString::fromUtf8(value, len);
if (oldval != newval)
{
oldval = newval;
changed = true;
}
return changed;
}
bool Torrent::setIcon(int i, QIcon const& value)
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Icon);
myValues[i].setValue(value);
return true;
}
int Torrent::getInt(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Int);
// assert(!myValues[i].isNull());
return myValues[i].toInt();
}
time_t Torrent::getTime(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::DateTime);
// assert((i == DATE_ADDED) || !myValues[i].isNull());
return time_t(myValues[i].toLongLong());
}
bool Torrent::getBool(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Bool);
// assert(!myValues[i].isNull());
return myValues[i].toBool();
}
qulonglong Torrent::getSize(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::ULongLong);
// assert(!myValues[i].isNull());
return myValues[i].toULongLong();
}
double Torrent::getDouble(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Double);
// assert(!myValues[i].isNull());
return myValues[i].toDouble();
}
QString Torrent::getString(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::String);
// assert((i == HASH_STRING) || !myValues[i].isNull());
return myValues[i].toString();
}
QIcon Torrent::getIcon(int i) const
{
assert(0 <= i && i < PROPERTY_COUNT);
assert(myProperties[i].type == QVariant::Icon);
// assert(!myValues[i].isNull());
return myValues[i].value<QIcon>();
}
/***
****
***/
bool Torrent::getSeedRatio(double& ratio) const
{
bool isLimited;
switch (seedRatioMode())
{
case TR_RATIOLIMIT_SINGLE:
isLimited = true;
ratio = seedRatioLimit();
break;
case TR_RATIOLIMIT_GLOBAL:
if ((isLimited = myPrefs.getBool(Prefs::RATIO_ENABLED)))
{
ratio = myPrefs.getDouble(Prefs::RATIO);
}
break;
default: // TR_RATIOLIMIT_UNLIMITED:
isLimited = false;
break;
}
return isLimited;
}
bool Torrent::hasFileSubstring(QString const& substr) const
{
for (TorrentFile const& file : myFiles)
{
if (file.filename.contains(substr, Qt::CaseInsensitive))
{
return true;
}
}
return false;
}
bool Torrent::hasTrackerSubstring(QString const& substr) const
{
for (auto const& s : trackers())
{
if (s.contains(substr, Qt::CaseInsensitive))
{
return true;
}
}
return false;
}
int Torrent::compareSeedRatio(Torrent const& that) const
{
double a;
double b;
bool const has_a = getSeedRatio(a);
bool const has_b = that.getSeedRatio(b);
if (!has_a && !has_b)
{
return 0;
}
if (!has_a || !has_b)
{
return has_a ? -1 : 1;
}
if (a < b)
{
return -1;
}
if (a > b)
{
return 1;
}
return 0;
}
int Torrent::compareRatio(Torrent const& that) const
{
double const a = ratio();
double const b = that.ratio();
if (static_cast<int>(a) == TR_RATIO_INF && static_cast<int>(b) == TR_RATIO_INF)
{
return 0;
}
if (static_cast<int>(a) == TR_RATIO_INF)
{
return 1;
}
if (static_cast<int>(b) == TR_RATIO_INF)
{
return -1;
}
if (a < b)
{
return -1;
}
if (a > b)
{
return 1;
}
return 0;
}
int Torrent::compareETA(Torrent const& that) const
{
bool const haveA(hasETA());
bool const haveB(that.hasETA());
if (haveA && haveB)
{
return getETA() - that.getETA();
}
if (haveA)
{
return 1;
}
if (haveB)
{
return -1;
}
return 0;
}
int Torrent::compareTracker(Torrent const& that) const
{
Q_UNUSED(that)
// FIXME
return 0;
}
/***
****
***/
void Torrent::updateMimeIcon()
{
FileList const& files(myFiles);
QIcon icon;
if (files.size() > 1)
{
icon = Utils::getFolderIcon();
}
else if (files.size() == 1)
{
icon = Utils::guessMimeIcon(files.at(0).filename);
}
else
{
icon = Utils::guessMimeIcon(name());
}
setIcon(MIME_ICON, icon);
}
/***
****
***/
bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n)
{
static bool lookup_initialized = false;
static int key_to_property_index[TR_N_KEYS];
bool changed = false;
if (!lookup_initialized)
{
lookup_initialized = true;
for (int i = 0; i < TR_N_KEYS; ++i)
{
key_to_property_index[i] = -1;
}
for (int i = 0; i < PROPERTY_COUNT; i++)
{
key_to_property_index[myProperties[i].key] = i;
}
}
for (size_t pos = 0; pos < n; ++pos)
{
tr_quark key = keys[pos];
tr_variant* child = values[pos];
int const property_index = key_to_property_index[key];
if (property_index == -1) // we're not interested in this one
{
continue;
}
assert(myProperties[property_index].key == key);
switch (myProperties[property_index].type)
{
case QVariant::Int:
{
int64_t val;
if (tr_variantGetInt(child, &val))
{
changed |= setInt(property_index, val);
}
break;
}
case QVariant::Bool:
{
bool val;
if (tr_variantGetBool(child, &val))
{
changed |= setBool(property_index, val);
}
break;
}
case QVariant::String:
{
char const* val;
size_t len;
if (tr_variantGetStr(child, &val, &len))
{
bool const field_changed = setString(property_index, val, len);
changed |= field_changed;
if (field_changed && key == TR_KEY_name)
{
updateMimeIcon();
}
}
break;
}
case QVariant::ULongLong:
{
int64_t val;
if (tr_variantGetInt(child, &val))
{
changed |= setSize(property_index, val);
}
break;
}
case QVariant::Double:
{
double val;
if (tr_variantGetReal(child, &val))
{
changed |= setDouble(property_index, val);
}
break;
}
case QVariant::DateTime:
{
int64_t val;
if (tr_variantGetInt(child, &val) && val &&
setTime(property_index, time_t(val)))
{
changed = true;
if (key == TR_KEY_editDate)
{
// FIXME
// emit torrentEdited(*this);
}
}
break;
}
case CustomVariantType::PeerList:
// handled below
break;
default:
std::cerr << __FILE__ << ':' << __LINE__ << "unhandled type: " << tr_quark_get_string(key, nullptr) << std::endl;
assert(false && "unhandled type");
}
}
auto it = std::find(keys, keys + n, TR_KEY_files);
if (it != keys + n)
{
tr_variant* files = values[std::distance(keys, it)];
char const* str;
int64_t intVal;
int i = 0;
tr_variant* child;
myFiles.clear();
myFiles.reserve(tr_variantListSize(files));
while ((child = tr_variantListChild(files, i)) != nullptr)
{
TorrentFile file;
size_t len;
file.index = i++;
if (tr_variantDictFindStr(child, TR_KEY_name, &str, &len))
{
file.filename = QString::fromUtf8(str, len);
}
if (tr_variantDictFindInt(child, TR_KEY_length, &intVal))
{
file.size = intVal;
}
myFiles.append(file);
}
updateMimeIcon();
changed = true;
}
it = std::find(keys, keys + n, TR_KEY_fileStats);
if (it != keys + n)
{
tr_variant* files = values[std::distance(keys, it)];
int const n = tr_variantListSize(files);
for (int i = 0; i < n && i < myFiles.size(); ++i)
{
int64_t intVal;
bool boolVal;
tr_variant* child = tr_variantListChild(files, i);
TorrentFile& file(myFiles[i]);
if (tr_variantDictFindInt(child, TR_KEY_bytesCompleted, &intVal))
{
file.have = intVal;
}
if (tr_variantDictFindBool(child, TR_KEY_wanted, &boolVal))
{
file.wanted = boolVal;
}
if (tr_variantDictFindInt(child, TR_KEY_priority, &intVal))
{
file.priority = intVal;
}
}
changed = true;
}
it = std::find(keys, keys + n, TR_KEY_trackers);
if (it != keys + n)
{
tr_variant* v = values[std::distance(keys, it)];
// build the new tracker list
QStringList trackers;
trackers.reserve(tr_variantListSize(v));
tr_variant* child;
int i = 0;
while ((child = tr_variantListChild(v, i++)) != nullptr)
{
char const* str;
size_t len;
if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len))
{
trackers.append(QString::fromUtf8(str, len));
}
}
// update the trackers
if (trackers_ != trackers)
{
QStringList displayNames;
displayNames.reserve(trackers.size());
for (auto const& tracker : trackers)
{
auto const url = QUrl(tracker);
auto const key = qApp->faviconCache().add(url);
displayNames.append(FaviconCache::getDisplayName(key));
}
displayNames.removeDuplicates();
trackers_.swap(trackers);
trackerDisplayNames_.swap(displayNames);
changed = true;
}
}
it = std::find(keys, keys + n, TR_KEY_trackerStats);
if (it != keys + n)
{
tr_variant* trackerStats = values[std::distance(keys, it)];
tr_variant* child;
TrackerStatsList trackerStatsList;
int childNum = 0;
while ((child = tr_variantListChild(trackerStats, childNum++)) != nullptr)
{
bool b;
int64_t i;
size_t len;
char const* str;
TrackerStat trackerStat;
if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len))
{
trackerStat.announce = QString::fromUtf8(str, len);
}
if (tr_variantDictFindInt(child, TR_KEY_announceState, &i))
{
trackerStat.announceState = i;
}
if (tr_variantDictFindInt(child, TR_KEY_downloadCount, &i))
{
trackerStat.downloadCount = i;
}
if (tr_variantDictFindBool(child, TR_KEY_hasAnnounced, &b))
{
trackerStat.hasAnnounced = b;
}
if (tr_variantDictFindBool(child, TR_KEY_hasScraped, &b))
{
trackerStat.hasScraped = b;
}
if (tr_variantDictFindStr(child, TR_KEY_host, &str, &len))
{
trackerStat.host = QString::fromUtf8(str, len);
}
if (tr_variantDictFindInt(child, TR_KEY_id, &i))
{
trackerStat.id = i;
}
if (tr_variantDictFindBool(child, TR_KEY_isBackup, &b))
{
trackerStat.isBackup = b;
}
if (tr_variantDictFindInt(child, TR_KEY_lastAnnouncePeerCount, &i))
{
trackerStat.lastAnnouncePeerCount = i;
}
if (tr_variantDictFindStr(child, TR_KEY_lastAnnounceResult, &str, &len))
{
trackerStat.lastAnnounceResult = QString::fromUtf8(str, len);
}
if (tr_variantDictFindInt(child, TR_KEY_lastAnnounceStartTime, &i))
{
trackerStat.lastAnnounceStartTime = i;
}
if (tr_variantDictFindBool(child, TR_KEY_lastAnnounceSucceeded, &b))
{
trackerStat.lastAnnounceSucceeded = b;
}
if (tr_variantDictFindInt(child, TR_KEY_lastAnnounceTime, &i))
{
trackerStat.lastAnnounceTime = i;
}
if (tr_variantDictFindBool(child, TR_KEY_lastAnnounceTimedOut, &b))
{
trackerStat.lastAnnounceTimedOut = b;
}
if (tr_variantDictFindStr(child, TR_KEY_lastScrapeResult, &str, &len))
{
trackerStat.lastScrapeResult = QString::fromUtf8(str, len);
}
if (tr_variantDictFindInt(child, TR_KEY_lastScrapeStartTime, &i))
{
trackerStat.lastScrapeStartTime = i;
}
if (tr_variantDictFindBool(child, TR_KEY_lastScrapeSucceeded, &b))
{
trackerStat.lastScrapeSucceeded = b;
}
if (tr_variantDictFindInt(child, TR_KEY_lastScrapeTime, &i))
{
trackerStat.lastScrapeTime = i;
}
if (tr_variantDictFindBool(child, TR_KEY_lastScrapeTimedOut, &b))
{
trackerStat.lastScrapeTimedOut = b;
}
if (tr_variantDictFindInt(child, TR_KEY_leecherCount, &i))
{
trackerStat.leecherCount = i;
}
if (tr_variantDictFindInt(child, TR_KEY_nextAnnounceTime, &i))
{
trackerStat.nextAnnounceTime = i;
}
if (tr_variantDictFindInt(child, TR_KEY_nextScrapeTime, &i))
{
trackerStat.nextScrapeTime = i;
}
if (tr_variantDictFindInt(child, TR_KEY_scrapeState, &i))
{
trackerStat.scrapeState = i;
}
if (tr_variantDictFindInt(child, TR_KEY_seederCount, &i))
{
trackerStat.seederCount = i;
}
if (tr_variantDictFindInt(child, TR_KEY_tier, &i))
{
trackerStat.tier = i;
}
trackerStatsList << trackerStat;
}
myValues[TRACKERSTATS].setValue(trackerStatsList);
changed = true;
}
it = std::find(keys, keys + n, TR_KEY_peers);
if (it != keys + n)
{
tr_variant* peers = values[std::distance(keys, it)];
tr_variant* child;
PeerList peerList;
int childNum = 0;
while ((child = tr_variantListChild(peers, childNum++)) != nullptr)
{
double d;
bool b;
int64_t i;
size_t len;
char const* str;
Peer peer;
if (tr_variantDictFindStr(child, TR_KEY_address, &str, &len))
{
peer.address = QString::fromUtf8(str, len);
}
if (tr_variantDictFindStr(child, TR_KEY_clientName, &str, &len))
{
peer.clientName = QString::fromUtf8(str, len);
}
if (tr_variantDictFindBool(child, TR_KEY_clientIsChoked, &b))
{
peer.clientIsChoked = b;
}
if (tr_variantDictFindBool(child, TR_KEY_clientIsInterested, &b))
{
peer.clientIsInterested = b;
}
if (tr_variantDictFindStr(child, TR_KEY_flagStr, &str, &len))
{
peer.flagStr = QString::fromUtf8(str, len);
}
if (tr_variantDictFindBool(child, TR_KEY_isDownloadingFrom, &b))
{
peer.isDownloadingFrom = b;
}
if (tr_variantDictFindBool(child, TR_KEY_isEncrypted, &b))
{
peer.isEncrypted = b;
}
if (tr_variantDictFindBool(child, TR_KEY_isIncoming, &b))
{
peer.isIncoming = b;
}
if (tr_variantDictFindBool(child, TR_KEY_isUploadingTo, &b))
{
peer.isUploadingTo = b;
}
if (tr_variantDictFindBool(child, TR_KEY_peerIsChoked, &b))
{
peer.peerIsChoked = b;
}
if (tr_variantDictFindBool(child, TR_KEY_peerIsInterested, &b))
{
peer.peerIsInterested = b;
}
if (tr_variantDictFindInt(child, TR_KEY_port, &i))
{
peer.port = i;
}
if (tr_variantDictFindReal(child, TR_KEY_progress, &d))
{
peer.progress = d;
}
if (tr_variantDictFindInt(child, TR_KEY_rateToClient, &i))
{
peer.rateToClient = Speed::fromBps(i);
}
if (tr_variantDictFindInt(child, TR_KEY_rateToPeer, &i))
{
peer.rateToPeer = Speed::fromBps(i);
}
peerList << peer;
}
myValues[PEERS].setValue(peerList);
changed = true;
}
return changed;
}
QString Torrent::activityString() const
{
QString str;
switch (getActivity())
{
case TR_STATUS_STOPPED:
str = isFinished() ? tr("Finished") : tr("Paused");
break;
case TR_STATUS_CHECK_WAIT:
str = tr("Queued for verification");
break;
case TR_STATUS_CHECK:
str = tr("Verifying local data");
break;
case TR_STATUS_DOWNLOAD_WAIT:
str = tr("Queued for download");
break;
case TR_STATUS_DOWNLOAD:
str = tr("Downloading");
break;
case TR_STATUS_SEED_WAIT:
str = tr("Queued for seeding");
break;
case TR_STATUS_SEED:
str = tr("Seeding");
break;
}
return str;
}
QString Torrent::getError() const
{
QString s = getString(ERROR_STRING);
switch (getInt(ERROR))
{
case TR_STAT_TRACKER_WARNING:
s = tr("Tracker gave a warning: %1").arg(s);
break;
case TR_STAT_TRACKER_ERROR:
s = tr("Tracker gave an error: %1").arg(s);
break;
case TR_STAT_LOCAL_ERROR:
s = tr("Error: %1").arg(s);
break;
default:
s.clear();
break;
}
return s;
}
QPixmap TrackerStat::getFavicon() const
{
return qApp->faviconCache().find(QUrl(announce));
}