2 * This file Copyright (C) Mnemosyne LLC
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: torrent.cc 11491 2010-12-08 15:45:43Z charles $
16 #include <QApplication>
17 #include <QFileIconProvider>
25 #include <libtransmission/transmission.h>
26 #include <libtransmission/bencode.h>
27 #include <libtransmission/utils.h> /* tr_new0, tr_strdup */
35 Torrent :: Torrent( Prefs
& prefs
, int id
):
36 magnetTorrent( false ),
39 for( int i
=0; i
<PROPERTY_COUNT
; ++i
)
40 assert( myProperties
[i
].id
== i
);
43 setIcon( MIME_ICON
, QApplication::style()->standardIcon( QStyle::SP_FileIcon
) );
46 Torrent :: ~Torrent( )
55 Torrent :: myProperties
[] =
57 { ID
, "id", QVariant::Int
, INFO
, },
58 { UPLOAD_SPEED
, "rateUpload", QVariant::ULongLong
, STAT
} /* Bps */,
59 { DOWNLOAD_SPEED
, "rateDownload", QVariant::ULongLong
, STAT
}, /* Bps */
60 { DOWNLOAD_DIR
, "downloadDir", QVariant::String
, STAT
},
61 { ACTIVITY
, "status", QVariant::Int
, STAT
},
62 { NAME
, "name", QVariant::String
, INFO
},
63 { ERROR
, "error", QVariant::Int
, STAT
},
64 { ERROR_STRING
, "errorString", QVariant::String
, STAT
},
65 { SIZE_WHEN_DONE
, "sizeWhenDone", QVariant::ULongLong
, STAT
},
66 { LEFT_UNTIL_DONE
, "leftUntilDone", QVariant::ULongLong
, STAT
},
67 { HAVE_UNCHECKED
, "haveUnchecked", QVariant::ULongLong
, STAT
},
68 { HAVE_VERIFIED
, "haveValid", QVariant::ULongLong
, STAT
},
69 { DESIRED_AVAILABLE
, "desiredAvailable", QVariant::ULongLong
, STAT
},
70 { TOTAL_SIZE
, "totalSize", QVariant::ULongLong
, INFO
},
71 { PIECE_SIZE
, "pieceSize", QVariant::ULongLong
, INFO
},
72 { PIECE_COUNT
, "pieceCount", QVariant::Int
, INFO
},
73 { PEERS_GETTING_FROM_US
, "peersGettingFromUs", QVariant::Int
, STAT
},
74 { PEERS_SENDING_TO_US
, "peersSendingToUs", QVariant::Int
, STAT
},
75 { WEBSEEDS_SENDING_TO_US
, "webseedsSendingToUs", QVariant::Int
, STAT_EXTRA
},
76 { PERCENT_DONE
, "percentDone", QVariant::Double
, STAT
},
77 { METADATA_PERCENT_DONE
, "metadataPercentComplete", QVariant::Double
, STAT
},
78 { PERCENT_VERIFIED
, "recheckProgress", QVariant::Double
, STAT
},
79 { DATE_ACTIVITY
, "activityDate", QVariant::DateTime
, STAT_EXTRA
},
80 { DATE_ADDED
, "addedDate", QVariant::DateTime
, INFO
},
81 { DATE_STARTED
, "startDate", QVariant::DateTime
, STAT_EXTRA
},
82 { DATE_CREATED
, "dateCreated", QVariant::DateTime
, INFO
},
83 { PEERS_CONNECTED
, "peersConnected", QVariant::Int
, STAT
},
84 { ETA
, "eta", QVariant::Int
, STAT
},
85 { RATIO
, "uploadRatio", QVariant::Double
, STAT
},
86 { DOWNLOADED_EVER
, "downloadedEver", QVariant::ULongLong
, STAT
},
87 { UPLOADED_EVER
, "uploadedEver", QVariant::ULongLong
, STAT
},
88 { FAILED_EVER
, "corruptEver", QVariant::ULongLong
, STAT_EXTRA
},
89 { TRACKERS
, "trackers", QVariant::StringList
, STAT
},
90 { TRACKERSTATS
, "trackerStats", TrTypes::TrackerStatsList
, STAT_EXTRA
},
91 { MIME_ICON
, "ccc", QVariant::Icon
, DERIVED
},
92 { SEED_RATIO_LIMIT
, "seedRatioLimit", QVariant::Double
, STAT
},
93 { SEED_RATIO_MODE
, "seedRatioMode", QVariant::Int
, STAT
},
94 { SEED_IDLE_LIMIT
, "seedIdleLimit", QVariant::Int
, STAT_EXTRA
},
95 { SEED_IDLE_MODE
, "seedIdleMode", QVariant::Int
, STAT_EXTRA
},
96 { DOWN_LIMIT
, "downloadLimit", QVariant::Int
, STAT_EXTRA
}, /* KB/s */
97 { DOWN_LIMITED
, "downloadLimited", QVariant::Bool
, STAT_EXTRA
},
98 { UP_LIMIT
, "uploadLimit", QVariant::Int
, STAT_EXTRA
}, /* KB/s */
99 { UP_LIMITED
, "uploadLimited", QVariant::Bool
, STAT_EXTRA
},
100 { HONORS_SESSION_LIMITS
, "honorsSessionLimits", QVariant::Bool
, STAT_EXTRA
},
101 { PEER_LIMIT
, "peer-limit", QVariant::Int
, STAT_EXTRA
},
102 { HASH_STRING
, "hashString", QVariant::String
, INFO
},
103 { IS_FINISHED
, "isFinished", QVariant::Bool
, STAT
},
104 { IS_PRIVATE
, "isPrivate", QVariant::Bool
, INFO
},
105 { COMMENT
, "comment", QVariant::String
, INFO
},
106 { CREATOR
, "creator", QVariant::String
, INFO
},
107 { MANUAL_ANNOUNCE_TIME
, "manualAnnounceTime", QVariant::DateTime
, STAT_EXTRA
},
108 { PEERS
, "peers", TrTypes::PeerList
, STAT_EXTRA
},
109 { TORRENT_FILE
, "torrentFile", QVariant::String
, STAT_EXTRA
},
110 { BANDWIDTH_PRIORITY
, "bandwidthPriority", QVariant::Int
, STAT_EXTRA
}
114 Torrent :: buildKeyList( Group group
)
119 for( int i
=0; i
<PROPERTY_COUNT
; ++i
)
120 if( myProperties
[i
].id
==ID
|| myProperties
[i
].group
==group
)
121 keys
<< myProperties
[i
].key
;
126 const Torrent :: KeyList
&
127 Torrent :: getInfoKeys( )
130 if( keys
.isEmpty( ) )
131 keys
<< buildKeyList( INFO
) << "files";
135 const Torrent :: KeyList
&
136 Torrent :: getStatKeys( )
138 static KeyList
keys( buildKeyList( STAT
) );
142 const Torrent :: KeyList
&
143 Torrent :: getExtraStatKeys( )
146 if( keys
.isEmpty( ) )
147 keys
<< buildKeyList( STAT_EXTRA
) << "fileStats";
153 Torrent :: setInt( int i
, int value
)
155 bool changed
= false;
157 assert( 0<=i
&& i
<PROPERTY_COUNT
);
158 assert( myProperties
[i
].type
== QVariant::Int
);
160 if( myValues
[i
].isNull() || myValues
[i
].toInt()!=value
)
162 myValues
[i
].setValue( value
);
170 Torrent :: setBool( int i
, bool value
)
172 bool changed
= false;
174 assert( 0<=i
&& i
<PROPERTY_COUNT
);
175 assert( myProperties
[i
].type
== QVariant::Bool
);
177 if( myValues
[i
].isNull() || myValues
[i
].toBool()!=value
)
179 myValues
[i
].setValue( value
);
187 Torrent :: setDouble( int i
, double value
)
189 bool changed
= false;
191 assert( 0<=i
&& i
<PROPERTY_COUNT
);
192 assert( myProperties
[i
].type
== QVariant::Double
);
194 if( myValues
[i
].isNull() || myValues
[i
].toDouble()!=value
)
196 myValues
[i
].setValue( value
);
204 Torrent :: setDateTime( int i
, const QDateTime
& value
)
206 bool changed
= false;
208 assert( 0<=i
&& i
<PROPERTY_COUNT
);
209 assert( myProperties
[i
].type
== QVariant::DateTime
);
211 if( myValues
[i
].isNull() || myValues
[i
].toDateTime()!=value
)
213 myValues
[i
].setValue( value
);
221 Torrent :: setSize( int i
, qulonglong value
)
223 bool changed
= false;
225 assert( 0<=i
&& i
<PROPERTY_COUNT
);
226 assert( myProperties
[i
].type
== QVariant::ULongLong
);
228 if( myValues
[i
].isNull() || myValues
[i
].toULongLong()!=value
)
230 myValues
[i
].setValue( value
);
238 Torrent :: setString( int i
, const char * value
)
240 bool changed
= false;
242 assert( 0<=i
&& i
<PROPERTY_COUNT
);
243 assert( myProperties
[i
].type
== QVariant::String
);
245 if( myValues
[i
].isNull() || myValues
[i
].toString()!=value
)
247 myValues
[i
].setValue( QString::fromUtf8( value
) );
255 Torrent :: setIcon( int i
, const QIcon
& value
)
257 assert( 0<=i
&& i
<PROPERTY_COUNT
);
258 assert( myProperties
[i
].type
== QVariant::Icon
);
260 myValues
[i
].setValue( value
);
265 Torrent :: getInt( int i
) const
267 assert( 0<=i
&& i
<PROPERTY_COUNT
);
268 assert( myProperties
[i
].type
== QVariant::Int
);
270 return myValues
[i
].toInt( );
274 Torrent :: getDateTime( int i
) const
276 assert( 0<=i
&& i
<PROPERTY_COUNT
);
277 assert( myProperties
[i
].type
== QVariant::DateTime
);
279 return myValues
[i
].toDateTime( );
283 Torrent :: getBool( int i
) const
285 assert( 0<=i
&& i
<PROPERTY_COUNT
);
286 assert( myProperties
[i
].type
== QVariant::Bool
);
288 return myValues
[i
].toBool( );
292 Torrent :: getSize( int i
) const
294 assert( 0<=i
&& i
<PROPERTY_COUNT
);
295 assert( myProperties
[i
].type
== QVariant::ULongLong
);
297 return myValues
[i
].toULongLong( );
300 Torrent :: getDouble( int i
) const
302 assert( 0<=i
&& i
<PROPERTY_COUNT
);
303 assert( myProperties
[i
].type
== QVariant::Double
);
305 return myValues
[i
].toDouble( );
308 Torrent :: getString( int i
) const
310 assert( 0<=i
&& i
<PROPERTY_COUNT
);
311 assert( myProperties
[i
].type
== QVariant::String
);
313 return myValues
[i
].toString( );
316 Torrent :: getIcon( int i
) const
318 assert( 0<=i
&& i
<PROPERTY_COUNT
);
319 assert( myProperties
[i
].type
== QVariant::Icon
);
321 return myValues
[i
].value
<QIcon
>();
329 Torrent :: getSeedRatio( double& ratio
) const
333 switch( seedRatioMode( ) )
335 case TR_RATIOLIMIT_SINGLE
:
337 ratio
= seedRatioLimit( );
340 case TR_RATIOLIMIT_GLOBAL
:
341 if(( isLimited
= myPrefs
.getBool( Prefs :: RATIO_ENABLED
)))
342 ratio
= myPrefs
.getDouble( Prefs :: RATIO
);
345 default: // TR_RATIOLIMIT_UNLIMITED:
354 Torrent :: hasFileSubstring( const QString
& substr
) const
356 foreach( const TrFile file
, myFiles
)
357 if( file
.filename
.contains( substr
, Qt::CaseInsensitive
) )
363 Torrent :: hasTrackerSubstring( const QString
& substr
) const
365 foreach( QString s
, myValues
[TRACKERS
].toStringList() )
366 if( s
.contains( substr
, Qt::CaseInsensitive
) )
372 Torrent :: compareSeedRatio( const Torrent
& that
) const
376 const bool has_a
= getSeedRatio( a
);
377 const bool has_b
= that
.getSeedRatio( b
);
378 if( !has_a
&& !has_b
) return 0;
379 if( !has_a
|| !has_b
) return has_a
? -1 : 1;
380 if( a
< b
) return -1;
381 if( a
> b
) return 1;
386 Torrent :: compareRatio( const Torrent
& that
) const
388 const double a
= ratio( );
389 const double b
= that
.ratio( );
390 if( (int)a
== TR_RATIO_INF
&& (int)b
== TR_RATIO_INF
) return 0;
391 if( (int)a
== TR_RATIO_INF
) return 1;
392 if( (int)b
== TR_RATIO_INF
) return -1;
393 if( a
< b
) return -1;
394 if( a
> b
) return 1;
399 Torrent :: compareETA( const Torrent
& that
) const
401 const bool haveA( hasETA( ) );
402 const bool haveB( that
.hasETA( ) );
403 if( haveA
&& haveB
) return getETA() - that
.getETA();
404 if( haveA
) return 1;
405 if( haveB
) return -1;
410 Torrent :: compareTracker( const Torrent
& that
) const
423 Torrent :: updateMimeIcon( )
425 const FileList
& files( myFiles
);
429 if( files
.size( ) > 1 )
430 icon
= QFileIconProvider().icon( QFileIconProvider::Folder
);
431 else if( files
.size( ) == 1 )
432 icon
= Utils :: guessMimeIcon( files
.at(0).filename
);
436 setIcon( MIME_ICON
, icon
);
444 Torrent :: notifyComplete( ) const
446 // if someone wants to implement notification, here's the hook.
454 Torrent :: update( tr_benc
* d
)
456 bool changed
= false;
458 for( int i
=0; i
<PROPERTY_COUNT
; ++i
)
460 tr_benc
* child
= tr_bencDictFind( d
, myProperties
[i
].key
);
464 switch( myProperties
[i
].type
)
466 case QVariant :: Int
: {
468 if( tr_bencGetInt( child
, &val
) )
469 changed
|= setInt( i
, val
);
473 case QVariant :: Bool
: {
475 if( tr_bencGetBool( child
, &val
) )
476 changed
|= setBool( i
, val
);
480 case QVariant :: String
: {
482 if( tr_bencGetStr( child
, &val
) )
483 changed
|= setString( i
, val
);
487 case QVariant :: ULongLong
: {
489 if( tr_bencGetInt( child
, &val
) )
490 changed
|= setSize( i
, val
);
494 case QVariant :: Double
: {
496 if( tr_bencGetReal( child
, &val
) )
497 changed
|= setDouble( i
, val
);
501 case QVariant :: DateTime
: {
503 if( tr_bencGetInt( child
, &val
) && val
)
504 changed
|= setDateTime( i
, QDateTime :: fromTime_t( val
) );
508 case QVariant :: StringList
:
509 case TrTypes :: PeerList
:
513 assert( 0 && "unhandled type" );
519 if( tr_bencDictFindList( d
, "files", &files
) ) {
525 while(( child
= tr_bencListChild( files
, i
))) {
528 if( tr_bencDictFindStr( child
, "name", &str
) )
529 file
.filename
= QString::fromUtf8( str
);
530 if( tr_bencDictFindInt( child
, "length", &intVal
) )
532 myFiles
.append( file
);
538 if( tr_bencDictFindList( d
, "fileStats", &files
) ) {
539 const int n
= tr_bencListSize( files
);
540 for( int i
=0; i
<n
&& i
<myFiles
.size(); ++i
) {
543 tr_benc
* child
= tr_bencListChild( files
, i
);
544 TrFile
& file( myFiles
[i
] );
545 if( tr_bencDictFindInt( child
, "bytesCompleted", &intVal
) )
547 if( tr_bencDictFindBool( child
, "wanted", &boolVal
) )
548 file
.wanted
= boolVal
;
549 if( tr_bencDictFindInt( child
, "priority", &intVal
) )
550 file
.priority
= intVal
;
556 if( tr_bencDictFindList( d
, "trackers", &trackers
) ) {
561 while(( child
= tr_bencListChild( trackers
, i
++ ))) {
562 if( tr_bencDictFindStr( child
, "announce", &str
)) {
563 dynamic_cast<MyApp
*>(QApplication::instance())->favicons
.add( QUrl(str
) );
564 list
.append( QString::fromUtf8( str
) );
567 if( myValues
[TRACKERS
] != list
) {
568 myValues
[TRACKERS
].setValue( list
);
573 tr_benc
* trackerStats
;
574 if( tr_bencDictFindList( d
, "trackerStats", &trackerStats
) ) {
576 TrackerStatsList trackerStatsList
;
578 while(( child
= tr_bencListChild( trackerStats
, childNum
++ ))) {
582 TrackerStat trackerStat
;
583 if( tr_bencDictFindStr( child
, "announce", &str
) ) {
584 trackerStat
.announce
= QString::fromUtf8( str
);
585 dynamic_cast<MyApp
*>(QApplication::instance())->favicons
.add( QUrl( trackerStat
.announce
) );
587 if( tr_bencDictFindInt( child
, "announceState", &i
) )
588 trackerStat
.announceState
= i
;
589 if( tr_bencDictFindInt( child
, "downloadCount", &i
) )
590 trackerStat
.downloadCount
= i
;
591 if( tr_bencDictFindBool( child
, "hasAnnounced", &b
) )
592 trackerStat
.hasAnnounced
= b
;
593 if( tr_bencDictFindBool( child
, "hasScraped", &b
) )
594 trackerStat
.hasScraped
= b
;
595 if( tr_bencDictFindStr( child
, "host", &str
) )
596 trackerStat
.host
= QString::fromUtf8( str
);
597 if( tr_bencDictFindInt( child
, "id", &i
) )
599 if( tr_bencDictFindBool( child
, "isBackup", &b
) )
600 trackerStat
.isBackup
= b
;
601 if( tr_bencDictFindInt( child
, "lastAnnouncePeerCount", &i
) )
602 trackerStat
.lastAnnouncePeerCount
= i
;
603 if( tr_bencDictFindStr( child
, "lastAnnounceResult", &str
) )
604 trackerStat
.lastAnnounceResult
= str
;
605 if( tr_bencDictFindInt( child
, "lastAnnounceStartTime", &i
) )
606 trackerStat
.lastAnnounceStartTime
= i
;
607 if( tr_bencDictFindBool( child
, "lastAnnounceSucceeded", &b
) )
608 trackerStat
.lastAnnounceSucceeded
= b
;
609 if( tr_bencDictFindInt( child
, "lastAnnounceTime", &i
) )
610 trackerStat
.lastAnnounceTime
= i
;
611 if( tr_bencDictFindBool( child
, "lastAnnounceTimedOut", &b
) )
612 trackerStat
.lastAnnounceTimedOut
= b
;
613 if( tr_bencDictFindStr( child
, "lastScrapeResult", &str
) )
614 trackerStat
.lastScrapeResult
= QString::fromUtf8( str
);
615 if( tr_bencDictFindInt( child
, "lastScrapeStartTime", &i
) )
616 trackerStat
.lastScrapeStartTime
= i
;
617 if( tr_bencDictFindBool( child
, "lastScrapeSucceeded", &b
) )
618 trackerStat
.lastScrapeSucceeded
= b
;
619 if( tr_bencDictFindInt( child
, "lastScrapeTime", &i
) )
620 trackerStat
.lastScrapeTime
= i
;
621 if( tr_bencDictFindBool( child
, "lastScrapeTimedOut", &b
) )
622 trackerStat
.lastScrapeTimedOut
= b
;
623 if( tr_bencDictFindInt( child
, "leecherCount", &i
) )
624 trackerStat
.leecherCount
= i
;
625 if( tr_bencDictFindInt( child
, "nextAnnounceTime", &i
) )
626 trackerStat
.nextAnnounceTime
= i
;
627 if( tr_bencDictFindInt( child
, "nextScrapeTime", &i
) )
628 trackerStat
.nextScrapeTime
= i
;
629 if( tr_bencDictFindInt( child
, "scrapeState", &i
) )
630 trackerStat
.scrapeState
= i
;
631 if( tr_bencDictFindInt( child
, "seederCount", &i
) )
632 trackerStat
.seederCount
= i
;
633 if( tr_bencDictFindInt( child
, "tier", &i
) )
634 trackerStat
.tier
= i
;
635 trackerStatsList
<< trackerStat
;
637 myValues
[TRACKERSTATS
].setValue( trackerStatsList
);
642 if( tr_bencDictFindList( d
, "peers", &peers
) ) {
646 while(( child
= tr_bencListChild( peers
, childNum
++ ))) {
652 if( tr_bencDictFindStr( child
, "address", &str
) )
653 peer
.address
= QString::fromUtf8( str
);
654 if( tr_bencDictFindStr( child
, "clientName", &str
) )
655 peer
.clientName
= QString::fromUtf8( str
);
656 if( tr_bencDictFindBool( child
, "clientIsChoked", &b
) )
657 peer
.clientIsChoked
= b
;
658 if( tr_bencDictFindBool( child
, "clientIsInterested", &b
) )
659 peer
.clientIsInterested
= b
;
660 if( tr_bencDictFindStr( child
, "flagStr", &str
) )
661 peer
.flagStr
= QString::fromUtf8( str
);
662 if( tr_bencDictFindBool( child
, "isDownloadingFrom", &b
) )
663 peer
.isDownloadingFrom
= b
;
664 if( tr_bencDictFindBool( child
, "isEncrypted", &b
) )
665 peer
.isEncrypted
= b
;
666 if( tr_bencDictFindBool( child
, "isIncoming", &b
) )
668 if( tr_bencDictFindBool( child
, "isUploadingTo", &b
) )
669 peer
.isUploadingTo
= b
;
670 if( tr_bencDictFindBool( child
, "peerIsChoked", &b
) )
671 peer
.peerIsChoked
= b
;
672 if( tr_bencDictFindBool( child
, "peerIsInterested", &b
) )
673 peer
.peerIsInterested
= b
;
674 if( tr_bencDictFindInt( child
, "port", &i
) )
676 if( tr_bencDictFindReal( child
, "progress", &d
) )
678 if( tr_bencDictFindInt( child
, "rateToClient", &i
) )
679 peer
.rateToClient
= Speed::fromBps( i
);
680 if( tr_bencDictFindInt( child
, "rateToPeer", &i
) )
681 peer
.rateToPeer
= Speed::fromBps( i
);
684 myValues
[PEERS
].setValue( peerList
);
689 emit
torrentChanged( id( ) );
693 Torrent :: activityString( ) const
697 switch( getActivity( ) )
699 case TR_STATUS_CHECK_WAIT
: str
= tr( "Waiting to verify local data" ); break;
700 case TR_STATUS_CHECK
: str
= tr( "Verifying local data" ); break;
701 case TR_STATUS_DOWNLOAD
: str
= tr( "Downloading" ); break;
702 case TR_STATUS_SEED
: str
= tr( "Seeding" ); break;
703 case TR_STATUS_STOPPED
: str
= isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
710 Torrent :: getError( ) const
712 QString s
= getString( ERROR_STRING
);
714 switch( getInt( ERROR
) )
716 case TR_STAT_TRACKER_WARNING
: s
= tr( "Tracker gave a warning: %1" ).arg( s
); break;
717 case TR_STAT_TRACKER_ERROR
: s
= tr( "Tracker gave an error: %1" ).arg( s
); break;
718 case TR_STAT_LOCAL_ERROR
: s
= tr( "Error: %1" ).arg( s
); break;
719 default: s
.clear(); break;
726 TrackerStat :: getFavicon( ) const
728 MyApp
* myApp
= dynamic_cast<MyApp
*>(QApplication::instance());
729 return myApp
->favicons
.find( QUrl( announce
) );