/* * This file Copyright (C) 2009-2010 Mnemosyne LLC * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* tr_new0, tr_strdup */ #include "app.h" #include "prefs.h" #include "torrent.h" #include "utils.h" Torrent :: Torrent( Prefs& prefs, int id ): myPrefs( prefs ) { for( int i=0; istandardIcon( QStyle::SP_FileIcon ) ); } Torrent :: ~Torrent( ) { } /*** **** ***/ Torrent :: Property Torrent :: myProperties[] = { { ID, "id", QVariant::Int, INFO, }, { UPLOAD_SPEED, "rateUpload", QVariant::Double, STAT } /* KBps */, { DOWNLOAD_SPEED, "rateDownload", QVariant::Double, STAT }, /* KBps */ { DOWNLOAD_DIR, "downloadDir", QVariant::String, STAT }, { ACTIVITY, "status", QVariant::Int, STAT }, { NAME, "name", QVariant::String, INFO }, { ERROR, "error", QVariant::Int, STAT }, { ERROR_STRING, "errorString", QVariant::String, STAT }, { SIZE_WHEN_DONE, "sizeWhenDone", QVariant::ULongLong, STAT }, { LEFT_UNTIL_DONE, "leftUntilDone", QVariant::ULongLong, STAT }, { HAVE_UNCHECKED, "haveUnchecked", QVariant::ULongLong, STAT }, { HAVE_VERIFIED, "haveValid", QVariant::ULongLong, STAT }, { DESIRED_AVAILABLE, "desiredAvailable", QVariant::ULongLong, STAT }, { TOTAL_SIZE, "totalSize", QVariant::ULongLong, INFO }, { PIECE_SIZE, "pieceSize", QVariant::ULongLong, INFO }, { PIECE_COUNT, "pieceCount", QVariant::Int, INFO }, { PEERS_GETTING_FROM_US, "peersGettingFromUs", QVariant::Int, STAT }, { PEERS_SENDING_TO_US, "peersSendingToUs", QVariant::Int, STAT }, { WEBSEEDS_SENDING_TO_US, "webseedsSendingToUs", QVariant::Int, STAT_EXTRA }, { PERCENT_DONE, "percentDone", QVariant::Double, STAT }, { METADATA_PERCENT_DONE, "metadataPercentComplete", QVariant::Double, STAT }, { PERCENT_VERIFIED, "recheckProgress", QVariant::Double, STAT }, { DATE_ACTIVITY, "activityDate", QVariant::DateTime, STAT_EXTRA }, { DATE_ADDED, "addedDate", QVariant::DateTime, INFO }, { DATE_STARTED, "startDate", QVariant::DateTime, STAT_EXTRA }, { DATE_CREATED, "dateCreated", QVariant::DateTime, INFO }, { PEERS_CONNECTED, "peersConnected", QVariant::Int, STAT }, { ETA, "eta", QVariant::Int, STAT }, { RATIO, "uploadRatio", QVariant::Double, STAT }, { DOWNLOADED_EVER, "downloadedEver", QVariant::ULongLong, STAT }, { UPLOADED_EVER, "uploadedEver", QVariant::ULongLong, STAT }, { FAILED_EVER, "corruptEver", QVariant::ULongLong, STAT_EXTRA }, { TRACKERS, "trackers", QVariant::StringList, INFO }, { TRACKERSTATS, "trackerStats", TrTypes::TrackerStatsList, STAT_EXTRA }, { MIME_ICON, "ccc", QVariant::Icon, DERIVED }, { SEED_RATIO_LIMIT, "seedRatioLimit", QVariant::Double, STAT }, { SEED_RATIO_MODE, "seedRatioMode", QVariant::Int, STAT }, { SEED_IDLE_LIMIT, "seedIdleLimit", QVariant::Int, STAT_EXTRA }, { SEED_IDLE_MODE, "seedIdleMode", QVariant::Int, STAT_EXTRA }, { DOWN_LIMIT, "downloadLimit", QVariant::Int, STAT_EXTRA }, /* KB/s */ { DOWN_LIMITED, "downloadLimited", QVariant::Bool, STAT_EXTRA }, { UP_LIMIT, "uploadLimit", QVariant::Int, STAT_EXTRA }, /* KB/s */ { UP_LIMITED, "uploadLimited", QVariant::Bool, STAT_EXTRA }, { HONORS_SESSION_LIMITS, "honorsSessionLimits", QVariant::Bool, STAT_EXTRA }, { PEER_LIMIT, "peer-limit", QVariant::Int, STAT_EXTRA }, { HASH_STRING, "hashString", QVariant::String, INFO }, { IS_FINISHED, "isFinished", QVariant::Bool, STAT }, { IS_PRIVATE, "isPrivate", QVariant::Bool, INFO }, { COMMENT, "comment", QVariant::String, INFO }, { CREATOR, "creator", QVariant::String, INFO }, { MANUAL_ANNOUNCE_TIME, "manualAnnounceTime", QVariant::DateTime, STAT_EXTRA }, { PEERS, "peers", TrTypes::PeerList, STAT_EXTRA }, { TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA }, { BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA } }; 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; case 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 :: 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_benc * d ) { bool changed = false; for( int i=0; i(QApplication::instance())->favicons.add( QUrl(str) ); list.append( QString::fromUtf8( str ) ); } } if( myValues[TRACKERS] != list ) { myValues[TRACKERS].setValue( list ); changed = true; } } tr_benc * trackerStats; if( tr_bencDictFindList( d, "trackerStats", &trackerStats ) ) { tr_benc * child; TrackerStatsList trackerStatsList; int childNum = 0; while(( child = tr_bencListChild( trackerStats, childNum++ ))) { tr_bool b; int64_t i; const char * str; TrackerStat trackerStat; if( tr_bencDictFindStr( child, "announce", &str ) ) trackerStat.announce = QString::fromUtf8( str ); if( tr_bencDictFindInt( child, "announceState", &i ) ) trackerStat.announceState = i; if( tr_bencDictFindInt( child, "downloadCount", &i ) ) trackerStat.downloadCount = i; if( tr_bencDictFindBool( child, "hasAnnounced", &b ) ) trackerStat.hasAnnounced = b; if( tr_bencDictFindBool( child, "hasScraped", &b ) ) trackerStat.hasScraped = b; if( tr_bencDictFindStr( child, "host", &str ) ) trackerStat.host = QString::fromUtf8( str ); if( tr_bencDictFindInt( child, "id", &i ) ) trackerStat.id = i; if( tr_bencDictFindBool( child, "isBackup", &b ) ) trackerStat.isBackup = b; if( tr_bencDictFindInt( child, "lastAnnouncePeerCount", &i ) ) trackerStat.lastAnnouncePeerCount = i; if( tr_bencDictFindInt( child, "lastAnnounceResult", &i ) ) trackerStat.lastAnnounceResult = i; if( tr_bencDictFindInt( child, "lastAnnounceStartTime", &i ) ) trackerStat.lastAnnounceStartTime = i; if( tr_bencDictFindBool( child, "lastAnnounceSucceeded", &b ) ) trackerStat.lastAnnounceSucceeded = b; if( tr_bencDictFindInt( child, "lastAnnounceTime", &i ) ) trackerStat.lastAnnounceTime = i; if( tr_bencDictFindBool( child, "lastAnnounceTimedOut", &b ) ) trackerStat.lastAnnounceTimedOut = b; if( tr_bencDictFindStr( child, "lastScrapeResult", &str ) ) trackerStat.lastScrapeResult = QString::fromUtf8( str ); if( tr_bencDictFindInt( child, "lastScrapeStartTime", &i ) ) trackerStat.lastScrapeStartTime = i; if( tr_bencDictFindBool( child, "lastScrapeSucceeded", &b ) ) trackerStat.lastScrapeSucceeded = b; if( tr_bencDictFindInt( child, "lastScrapeTime", &i ) ) trackerStat.lastScrapeTime = i; if( tr_bencDictFindBool( child, "lastScrapeTimedOut", &b ) ) trackerStat.lastScrapeTimedOut = b; if( tr_bencDictFindInt( child, "leecherCount", &i ) ) trackerStat.leecherCount = i; if( tr_bencDictFindInt( child, "nextAnnounceTime", &i ) ) trackerStat.nextAnnounceTime = i; if( tr_bencDictFindInt( child, "nextScrapeTime", &i ) ) trackerStat.nextScrapeTime = i; if( tr_bencDictFindInt( child, "scrapeState", &i ) ) trackerStat.scrapeState = i; if( tr_bencDictFindInt( child, "seederCount", &i ) ) trackerStat.seederCount = i; if( tr_bencDictFindInt( child, "tier", &i ) ) trackerStat.tier = i; trackerStatsList << trackerStat; } myValues[TRACKERSTATS].setValue( trackerStatsList ); changed = true; } tr_benc * peers; if( tr_bencDictFindList( d, "peers", &peers ) ) { tr_benc * child; PeerList peerList; int childNum = 0; while(( child = tr_bencListChild( peers, childNum++ ))) { double d; tr_bool b; int64_t i; const char * str; Peer peer; if( tr_bencDictFindStr( child, "address", &str ) ) peer.address = QString::fromUtf8( str ); if( tr_bencDictFindStr( child, "clientName", &str ) ) peer.clientName = QString::fromUtf8( str ); if( tr_bencDictFindBool( child, "clientIsChoked", &b ) ) peer.clientIsChoked = b; if( tr_bencDictFindBool( child, "clientIsInterested", &b ) ) peer.clientIsInterested = b; if( tr_bencDictFindStr( child, "flagStr", &str ) ) peer.flagStr = QString::fromUtf8( str ); if( tr_bencDictFindBool( child, "isDownloadingFrom", &b ) ) peer.isDownloadingFrom = b; if( tr_bencDictFindBool( child, "isEncrypted", &b ) ) peer.isEncrypted = b; if( tr_bencDictFindBool( child, "isIncoming", &b ) ) peer.isIncoming = b; if( tr_bencDictFindBool( child, "isUploadingTo", &b ) ) peer.isUploadingTo = b; if( tr_bencDictFindBool( child, "peerIsChoked", &b ) ) peer.peerIsChoked = b; if( tr_bencDictFindBool( child, "peerIsInterested", &b ) ) peer.peerIsInterested = b; if( tr_bencDictFindInt( child, "port", &i ) ) peer.port = i; if( tr_bencDictFindReal( child, "progress", &d ) ) peer.progress = d; if( tr_bencDictFindReal( child, "rateToClient", &d ) ) peer.rateToClient = Speed::fromKBps( d ); if( tr_bencDictFindReal( child, "rateToPeer", &d ) ) peer.rateToPeer = Speed::fromKBps( d ); peerList << peer; } myValues[PEERS].setValue( peerList ); changed = true; } if( changed ) emit torrentChanged( id( ) ); } QString Torrent :: activityString( ) const { QString str; switch( getActivity( ) ) { case TR_STATUS_CHECK_WAIT: str = tr( "Waiting to verify local data" ); break; case TR_STATUS_CHECK: str = tr( "Verifying local data" ); break; case TR_STATUS_DOWNLOAD: str = tr( "Downloading" ); break; case TR_STATUS_SEED: str = tr( "Seeding" ); break; case TR_STATUS_STOPPED: str = isFinished() ? tr( "Finished" ): tr( "Paused" ); 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; }