/* * This file Copyright (C) 2009-2014 Mnemosyne LLC * * It may be used under the GNU Public License v2 or v3 licenses, * or any future license endorsed by Mnemosyne LLC. * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include /* tr_new0, tr_strdup */ #include #include "app.h" #include "prefs.h" #include "torrent.h" #include "utils.h" Torrent::Torrent (Prefs& prefs, int id): magnetTorrent (false), myPrefs (prefs) { #ifndef NDEBUG for (int i=0; istandardIcon (QStyle::SP_FileIcon)); } Torrent::~Torrent () { } /*** **** ***/ Torrent::Property Torrent::myProperties[] = { { ID, TR_KEY_id, QVariant::Int, INFO, }, { UPLOAD_SPEED, TR_KEY_rateUpload, QVariant::ULongLong, STAT } /* Bps */, { DOWNLOAD_SPEED, TR_KEY_rateDownload, QVariant::ULongLong, STAT }, /* Bps */ { DOWNLOAD_DIR, TR_KEY_downloadDir, QVariant::String, STAT }, { ACTIVITY, TR_KEY_status, QVariant::Int, STAT }, { NAME, TR_KEY_name, QVariant::String, INFO }, { ERROR, TR_KEY_error, QVariant::Int, STAT }, { ERROR_STRING, TR_KEY_errorString, QVariant::String, STAT }, { SIZE_WHEN_DONE, TR_KEY_sizeWhenDone, QVariant::ULongLong, STAT }, { LEFT_UNTIL_DONE, TR_KEY_leftUntilDone, QVariant::ULongLong, STAT }, { HAVE_UNCHECKED, TR_KEY_haveUnchecked, QVariant::ULongLong, STAT }, { HAVE_VERIFIED, TR_KEY_haveValid, QVariant::ULongLong, STAT }, { DESIRED_AVAILABLE, TR_KEY_desiredAvailable, QVariant::ULongLong, STAT }, { TOTAL_SIZE, TR_KEY_totalSize, QVariant::ULongLong, INFO }, { PIECE_SIZE, TR_KEY_pieceSize, QVariant::ULongLong, INFO }, { PIECE_COUNT, TR_KEY_pieceCount, QVariant::Int, INFO }, { PEERS_GETTING_FROM_US, TR_KEY_peersGettingFromUs, QVariant::Int, STAT }, { PEERS_SENDING_TO_US, TR_KEY_peersSendingToUs, QVariant::Int, STAT }, { WEBSEEDS_SENDING_TO_US, TR_KEY_webseedsSendingToUs, QVariant::Int, STAT_EXTRA }, { PERCENT_DONE, TR_KEY_percentDone, QVariant::Double, STAT }, { METADATA_PERCENT_DONE, TR_KEY_metadataPercentComplete, QVariant::Double, STAT }, { PERCENT_VERIFIED, TR_KEY_recheckProgress, QVariant::Double, STAT }, { DATE_ACTIVITY, TR_KEY_activityDate, QVariant::DateTime, STAT_EXTRA }, { DATE_ADDED, TR_KEY_addedDate, QVariant::DateTime, INFO }, { DATE_STARTED, TR_KEY_startDate, QVariant::DateTime, STAT_EXTRA }, { DATE_CREATED, TR_KEY_dateCreated, QVariant::DateTime, INFO }, { PEERS_CONNECTED, TR_KEY_peersConnected, QVariant::Int, STAT }, { ETA, TR_KEY_eta, QVariant::Int, STAT }, { RATIO, TR_KEY_uploadRatio, QVariant::Double, STAT }, { DOWNLOADED_EVER, TR_KEY_downloadedEver, QVariant::ULongLong, STAT }, { UPLOADED_EVER, TR_KEY_uploadedEver, QVariant::ULongLong, STAT }, { FAILED_EVER, TR_KEY_corruptEver, QVariant::ULongLong, STAT_EXTRA }, { TRACKERS, TR_KEY_trackers, QVariant::StringList, STAT }, { HOSTS, TR_KEY_NONE, QVariant::StringList, DERIVED }, { TRACKERSTATS, TR_KEY_trackerStats, TrTypes::TrackerStatsList, STAT_EXTRA }, { MIME_ICON, TR_KEY_NONE, QVariant::Icon, DERIVED }, { SEED_RATIO_LIMIT, TR_KEY_seedRatioLimit, QVariant::Double, STAT }, { SEED_RATIO_MODE, TR_KEY_seedRatioMode, QVariant::Int, STAT }, { SEED_IDLE_LIMIT, TR_KEY_seedIdleLimit, QVariant::Int, STAT_EXTRA }, { SEED_IDLE_MODE, TR_KEY_seedIdleMode, QVariant::Int, STAT_EXTRA }, { DOWN_LIMIT, TR_KEY_downloadLimit, QVariant::Int, STAT_EXTRA }, /* KB/s */ { DOWN_LIMITED, TR_KEY_downloadLimited, QVariant::Bool, STAT_EXTRA }, { UP_LIMIT, TR_KEY_uploadLimit, QVariant::Int, STAT_EXTRA }, /* KB/s */ { UP_LIMITED, TR_KEY_uploadLimited, QVariant::Bool, STAT_EXTRA }, { HONORS_SESSION_LIMITS, TR_KEY_honorsSessionLimits, QVariant::Bool, STAT_EXTRA }, { PEER_LIMIT, TR_KEY_peer_limit, QVariant::Int, STAT_EXTRA }, { HASH_STRING, TR_KEY_hashString, QVariant::String, INFO }, { IS_FINISHED, TR_KEY_isFinished, QVariant::Bool, STAT }, { IS_PRIVATE, TR_KEY_isPrivate, QVariant::Bool, INFO }, { IS_STALLED, TR_KEY_isStalled, QVariant::Bool, STAT }, { COMMENT, TR_KEY_comment, QVariant::String, INFO }, { CREATOR, TR_KEY_creator, QVariant::String, INFO }, { MANUAL_ANNOUNCE_TIME, TR_KEY_manualAnnounceTime, QVariant::DateTime, STAT_EXTRA }, { PEERS, TR_KEY_peers, TrTypes::PeerList, STAT_EXTRA }, { BANDWIDTH_PRIORITY, TR_KEY_bandwidthPriority, QVariant::Int, STAT_EXTRA }, { QUEUE_POSITION, TR_KEY_queuePosition, QVariant::Int, STAT }, }; Torrent::KeyList Torrent::buildKeyList (Group group) { KeyList keys; if (keys.empty()) for (int i=0; i(); } /*** **** ***/ 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 (const QString& substr) const { foreach (const TrFile file, myFiles) if (file.filename.contains (substr, Qt::CaseInsensitive)) return true; return false; } bool Torrent::hasTrackerSubstring (const QString& substr) const { foreach (QString s, myValues[TRACKERS].toStringList()) if (s.contains (substr, Qt::CaseInsensitive)) return true; return false; } int Torrent::compareSeedRatio (const Torrent& that) const { double a; double b; const bool has_a = getSeedRatio (a); const bool 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 (const Torrent& that) const { const double a = ratio (); const double b = that.ratio (); if ((int)a == TR_RATIO_INF && (int)b == TR_RATIO_INF) return 0; if ((int)a == TR_RATIO_INF) return 1; if ((int)b == TR_RATIO_INF) return -1; if (a < b) return -1; if (a > b) return 1; return 0; } int Torrent::compareETA (const Torrent& that) const { const bool haveA (hasETA ()); const bool haveB (that.hasETA ()); if (haveA && haveB) return getETA() - that.getETA(); if (haveA) return 1; if (haveB) return -1; return 0; } int Torrent::compareTracker (const Torrent& that) const { Q_UNUSED (that); // FIXME return 0; } /*** **** ***/ void Torrent::updateMimeIcon () { const FileList& files (myFiles); QIcon icon; if (files.size () > 1) icon = QFileIconProvider().icon (QFileIconProvider::Folder); else if (files.size () == 1) icon = Utils::guessMimeIcon (files.at(0).filename); else icon = QIcon (); setIcon (MIME_ICON, icon); } /*** **** ***/ void Torrent::notifyComplete () const { // if someone wants to implement notification, here's the hook. } /*** **** ***/ void Torrent::update (tr_variant * d) { static bool lookup_initialized = false; static int key_to_property_index[TR_N_KEYS]; bool changed = false; const bool was_seed = isSeed (); const uint64_t old_verified_size = haveVerified (); if (!lookup_initialized) { lookup_initialized = true; for (int i=0; i(QApplication::instance())->favicons.add (QUrl(QString::fromUtf8(str))); list.append (QString::fromUtf8 (str, len)); } } if (myValues[TRACKERS] != list) { QStringList hosts; foreach (QString tracker, list) { const QString host = Favicons::getHost (QUrl (tracker)); if (!host.isEmpty()) hosts.append (host); } hosts.removeDuplicates(); myValues[TRACKERS].setValue (list); myValues[HOSTS].setValue (hosts); changed = true; } } tr_variant * trackerStats; if (tr_variantDictFindList (d, TR_KEY_trackerStats, &trackerStats)) { tr_variant * child; TrackerStatsList trackerStatsList; int childNum = 0; while ((child = tr_variantListChild (trackerStats, childNum++))) { bool b; int64_t i; size_t len; const char * str; TrackerStat trackerStat; if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len)) { trackerStat.announce = QString::fromUtf8 (str, len); dynamic_cast(QApplication::instance())->favicons.add (QUrl (trackerStat.announce)); } 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; } tr_variant * peers; if (tr_variantDictFindList (d, TR_KEY_peers, &peers)) { tr_variant * child; PeerList peerList; int childNum = 0; while ((child = tr_variantListChild (peers, childNum++))) { double d; bool b; int64_t i; size_t len; const char * 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; } if (changed) emit torrentChanged (id ()); if (!was_seed && isSeed() && (old_verified_size>0)) emit torrentCompleted (id ()); } 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 { MyApp * myApp = dynamic_cast(QApplication::instance()); return myApp->favicons.find (QUrl (announce)); }