1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
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.
7 *
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 *
10 * $Id: torrent.cc 12697 2011-08-20 05:19:27Z jordan $
11 */
12
13#include <cassert>
14#include <iostream>
15
16#include <QApplication>
17#include <QFileIconProvider>
18#include <QFileInfo>
19#include <QSet>
20#include <QString>
21#include <QStyle>
22#include <QUrl>
23#include <QVariant>
24
25#include <libtransmission/transmission.h>
26#include <libtransmission/bencode.h>
27#include <libtransmission/utils.h> /* tr_new0, tr_strdup */
28
29#include "app.h"
30#include "prefs.h"
31#include "torrent.h"
32#include "utils.h"
33
34
35Torrent :: Torrent( Prefs& prefs, int id ):
36    magnetTorrent( false ),
37    myPrefs( prefs )
38{
39    for( int i=0; i<PROPERTY_COUNT; ++i )
40        assert( myProperties[i].id == i );
41
42    setInt( ID, id );
43    setIcon( MIME_ICON, QApplication::style()->standardIcon( QStyle::SP_FileIcon ) );
44}
45
46Torrent :: ~Torrent( )
47{
48}
49
50/***
51****
52***/
53
54Torrent :: Property
55Torrent :: myProperties[] =
56{
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    { IS_STALLED, "isStalled", QVariant::Bool, STAT },
106    { COMMENT, "comment", QVariant::String, INFO },
107    { CREATOR, "creator", QVariant::String, INFO },
108    { MANUAL_ANNOUNCE_TIME, "manualAnnounceTime", QVariant::DateTime, STAT_EXTRA },
109    { PEERS, "peers", TrTypes::PeerList, STAT_EXTRA },
110    { TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA },
111    { BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA },
112    { QUEUE_POSITION, "queuePosition", QVariant::Int, STAT },
113};
114
115Torrent :: KeyList
116Torrent :: buildKeyList( Group group )
117{
118    KeyList keys;
119
120    if( keys.empty( ) )
121        for( int i=0; i<PROPERTY_COUNT; ++i )
122            if( myProperties[i].id==ID || myProperties[i].group==group )
123                keys << myProperties[i].key;
124
125    return keys;
126}
127
128const Torrent :: KeyList&
129Torrent :: getInfoKeys( )
130{
131    static KeyList keys;
132    if( keys.isEmpty( ) )
133        keys << buildKeyList( INFO ) << "files";
134    return keys;
135}
136
137const Torrent :: KeyList&
138Torrent :: getStatKeys( )
139{
140    static KeyList keys( buildKeyList( STAT ) );
141    return keys;
142}
143
144const Torrent :: KeyList&
145Torrent :: getExtraStatKeys( )
146{
147    static KeyList keys;
148    if( keys.isEmpty( ) )
149        keys << buildKeyList( STAT_EXTRA ) << "fileStats";
150    return keys;
151}
152
153
154bool
155Torrent :: setInt( int i, int value )
156{
157    bool changed = false;
158
159    assert( 0<=i && i<PROPERTY_COUNT );
160    assert( myProperties[i].type == QVariant::Int );
161
162    if( myValues[i].isNull() || myValues[i].toInt()!=value )
163    {
164        myValues[i].setValue( value );
165        changed = true;
166    }
167
168    return changed;
169}
170
171bool
172Torrent :: setBool( int i, bool value )
173{
174    bool changed = false;
175
176    assert( 0<=i && i<PROPERTY_COUNT );
177    assert( myProperties[i].type == QVariant::Bool );
178
179    if( myValues[i].isNull() || myValues[i].toBool()!=value )
180    {
181        myValues[i].setValue( value );
182        changed = true;
183    }
184
185    return changed;
186}
187
188bool
189Torrent :: setDouble( int i, double value )
190{
191    bool changed = false;
192
193    assert( 0<=i && i<PROPERTY_COUNT );
194    assert( myProperties[i].type == QVariant::Double );
195
196    if( myValues[i].isNull() || myValues[i].toDouble()!=value )
197    {
198        myValues[i].setValue( value );
199        changed = true;
200    }
201
202    return changed;
203}
204
205bool
206Torrent :: setDateTime( int i, const QDateTime& value )
207{
208    bool changed = false;
209
210    assert( 0<=i && i<PROPERTY_COUNT );
211    assert( myProperties[i].type == QVariant::DateTime );
212
213    if( myValues[i].isNull() || myValues[i].toDateTime()!=value )
214    {
215        myValues[i].setValue( value );
216        changed = true;
217    }
218
219    return changed;
220}
221
222bool
223Torrent :: setSize( int i, qulonglong value )
224{
225    bool changed = false;
226
227    assert( 0<=i && i<PROPERTY_COUNT );
228    assert( myProperties[i].type == QVariant::ULongLong );
229
230    if( myValues[i].isNull() || myValues[i].toULongLong()!=value )
231    {
232        myValues[i].setValue( value );
233        changed = true;
234    }
235
236    return changed;
237}
238
239bool
240Torrent :: setString( int i, const char * value )
241{
242    bool changed = false;
243
244    assert( 0<=i && i<PROPERTY_COUNT );
245    assert( myProperties[i].type == QVariant::String );
246
247    if( myValues[i].isNull() || myValues[i].toString()!=value )
248    {
249        myValues[i].setValue( QString::fromUtf8( value ) );
250        changed = true;
251    }
252
253    return changed;
254}
255
256bool
257Torrent :: setIcon( int i, const QIcon& value )
258{
259    assert( 0<=i && i<PROPERTY_COUNT );
260    assert( myProperties[i].type == QVariant::Icon );
261
262    myValues[i].setValue( value );
263    return true;
264}
265
266int
267Torrent :: getInt( int i ) const
268{
269    assert( 0<=i && i<PROPERTY_COUNT );
270    assert( myProperties[i].type == QVariant::Int );
271
272    return myValues[i].toInt( );
273}
274
275QDateTime
276Torrent :: getDateTime( int i ) const
277{
278    assert( 0<=i && i<PROPERTY_COUNT );
279    assert( myProperties[i].type == QVariant::DateTime );
280
281    return myValues[i].toDateTime( );
282}
283
284bool
285Torrent :: getBool( int i ) const
286{
287    assert( 0<=i && i<PROPERTY_COUNT );
288    assert( myProperties[i].type == QVariant::Bool );
289
290    return myValues[i].toBool( );
291}
292
293qulonglong
294Torrent :: getSize( int i ) const
295{
296    assert( 0<=i && i<PROPERTY_COUNT );
297    assert( myProperties[i].type == QVariant::ULongLong );
298
299    return myValues[i].toULongLong( );
300}
301double
302Torrent :: getDouble( int i ) const
303{
304    assert( 0<=i && i<PROPERTY_COUNT );
305    assert( myProperties[i].type == QVariant::Double );
306
307    return myValues[i].toDouble( );
308}
309QString
310Torrent :: getString( int i ) const
311{
312    assert( 0<=i && i<PROPERTY_COUNT );
313    assert( myProperties[i].type == QVariant::String );
314
315    return myValues[i].toString( );
316}
317QIcon
318Torrent :: getIcon( int i ) const
319{
320    assert( 0<=i && i<PROPERTY_COUNT );
321    assert( myProperties[i].type == QVariant::Icon );
322
323    return myValues[i].value<QIcon>();
324}
325
326/***
327****
328***/
329
330bool
331Torrent :: getSeedRatio( double& ratio ) const
332{
333    bool isLimited;
334
335    switch( seedRatioMode( ) )
336    {
337        case TR_RATIOLIMIT_SINGLE:
338            isLimited = true;
339            ratio = seedRatioLimit( );
340            break;
341
342        case TR_RATIOLIMIT_GLOBAL:
343            if(( isLimited = myPrefs.getBool( Prefs :: RATIO_ENABLED )))
344                ratio = myPrefs.getDouble( Prefs :: RATIO );
345            break;
346
347        default: // TR_RATIOLIMIT_UNLIMITED:
348            isLimited = false;
349            break;
350    }
351
352    return isLimited;
353}
354
355bool
356Torrent :: hasFileSubstring( const QString& substr ) const
357{
358    foreach( const TrFile file, myFiles )
359        if( file.filename.contains( substr, Qt::CaseInsensitive ) )
360            return true;
361    return false;
362}
363
364bool
365Torrent :: hasTrackerSubstring( const QString& substr ) const
366{
367    foreach( QString s, myValues[TRACKERS].toStringList() )
368        if( s.contains( substr, Qt::CaseInsensitive ) )
369            return true;
370    return false;
371}
372
373int
374Torrent :: compareSeedRatio( const Torrent& that ) const
375{
376    double a;
377    double b;
378    const bool has_a = getSeedRatio( a );
379    const bool has_b = that.getSeedRatio( b );
380    if( !has_a && !has_b ) return 0;
381    if( !has_a || !has_b ) return has_a ? -1 : 1;
382    if( a < b ) return -1;
383    if( a > b ) return 1;
384    return 0;
385}
386
387int
388Torrent :: compareRatio( const Torrent& that ) const
389{
390    const double a = ratio( );
391    const double b = that.ratio( );
392    if( (int)a == TR_RATIO_INF && (int)b == TR_RATIO_INF ) return 0;
393    if( (int)a == TR_RATIO_INF ) return 1;
394    if( (int)b == TR_RATIO_INF ) return -1;
395    if( a < b ) return -1;
396    if( a > b ) return 1;
397    return 0;
398}
399
400int
401Torrent :: compareETA( const Torrent& that ) const
402{
403    const bool haveA( hasETA( ) );
404    const bool haveB( that.hasETA( ) );
405    if( haveA && haveB ) return getETA() - that.getETA();
406    if( haveA ) return 1;
407    if( haveB ) return -1;
408    return 0;
409}
410
411int
412Torrent :: compareTracker( const Torrent& that ) const
413{
414    Q_UNUSED( that );
415
416    // FIXME
417    return 0;
418}
419
420/***
421****
422***/
423
424void
425Torrent :: updateMimeIcon( )
426{
427    const FileList& files( myFiles );
428
429    QIcon icon;
430
431    if( files.size( ) > 1 )
432        icon = QFileIconProvider().icon( QFileIconProvider::Folder );
433    else if( files.size( ) == 1 )
434        icon = Utils :: guessMimeIcon( files.at(0).filename );
435    else
436        icon = QIcon( );
437
438    setIcon( MIME_ICON, icon );
439}
440
441/***
442****
443***/
444
445void
446Torrent :: notifyComplete( ) const
447{
448    // if someone wants to implement notification, here's the hook.
449}
450
451/***
452****
453***/
454
455void
456Torrent :: update( tr_benc * d )
457{
458    bool changed = false;
459    const bool was_seed = isSeed( );
460    const uint64_t old_verified_size = haveVerified( );
461
462    for( int  i=0; i<PROPERTY_COUNT; ++i )
463    {
464        tr_benc * child = tr_bencDictFind( d, myProperties[i].key );
465        if( !child )
466            continue;
467
468        switch( myProperties[i].type )
469        {
470            case QVariant :: Int: {
471                int64_t val;
472                if( tr_bencGetInt( child, &val ) )
473                    changed |= setInt( i, val );
474                break;
475            }
476
477            case QVariant :: Bool: {
478                bool val;
479                if( tr_bencGetBool( child, &val ) )
480                    changed |= setBool( i, val );
481                break;
482            }
483
484            case QVariant :: String: {
485                const char * val;
486                if( tr_bencGetStr( child, &val ) )
487                    changed |= setString( i, val );
488                break;
489            }
490
491            case QVariant :: ULongLong: {
492                int64_t val;
493                if( tr_bencGetInt( child, &val ) )
494                    changed |= setSize( i, val );
495                break;
496            }
497
498            case QVariant :: Double: {
499                double val;
500                if( tr_bencGetReal( child, &val ) )
501                    changed |= setDouble( i, val );
502                break;
503            }
504
505            case QVariant :: DateTime: {
506                int64_t val;
507                if( tr_bencGetInt( child, &val ) && val )
508                    changed |= setDateTime( i, QDateTime :: fromTime_t( val ) );
509                break;
510            }
511
512            case QVariant :: StringList:
513            case TrTypes :: PeerList:
514                break;
515
516            default:
517                assert( 0 && "unhandled type" );
518        }
519    }
520
521    tr_benc * files;
522
523    if( tr_bencDictFindList( d, "files", &files ) ) {
524        const char * str;
525        int64_t intVal;
526        int i = 0;
527        myFiles.clear( );
528        tr_benc * child;
529        while(( child = tr_bencListChild( files, i ))) {
530            TrFile file;
531            file.index = i++;
532            if( tr_bencDictFindStr( child, "name", &str ) )
533                file.filename = QString::fromUtf8( str );
534            if( tr_bencDictFindInt( child, "length", &intVal ) )
535                file.size = intVal;
536            myFiles.append( file );
537        }
538        updateMimeIcon( );
539        changed = true;
540    }
541
542    if( tr_bencDictFindList( d, "fileStats", &files ) ) {
543        const int n = tr_bencListSize( files );
544        for( int i=0; i<n && i<myFiles.size(); ++i ) {
545            int64_t intVal;
546            bool boolVal;
547            tr_benc * child = tr_bencListChild( files, i );
548            TrFile& file( myFiles[i] );
549            if( tr_bencDictFindInt( child, "bytesCompleted", &intVal ) )
550                file.have = intVal;
551            if( tr_bencDictFindBool( child, "wanted", &boolVal ) )
552                file.wanted = boolVal;
553            if( tr_bencDictFindInt( child, "priority", &intVal ) )
554                file.priority = intVal;
555        }
556        changed = true;
557    }
558
559    tr_benc * trackers;
560    if( tr_bencDictFindList( d, "trackers", &trackers ) ) {
561        const char * str;
562        int i = 0;
563        QStringList list;
564        tr_benc * child;
565        while(( child = tr_bencListChild( trackers, i++ ))) {
566            if( tr_bencDictFindStr( child, "announce", &str )) {
567                dynamic_cast<MyApp*>(QApplication::instance())->favicons.add( QUrl(QString::fromUtf8(str)) );
568                list.append( QString::fromUtf8( str ) );
569            }
570        }
571        if( myValues[TRACKERS] != list ) {
572            myValues[TRACKERS].setValue( list );
573            changed = true;
574        }
575    }
576
577    tr_benc * trackerStats;
578    if( tr_bencDictFindList( d, "trackerStats", &trackerStats ) ) {
579        tr_benc * child;
580        TrackerStatsList  trackerStatsList;
581        int childNum = 0;
582        while(( child = tr_bencListChild( trackerStats, childNum++ ))) {
583            bool b;
584            int64_t i;
585            const char * str;
586            TrackerStat trackerStat;
587            if( tr_bencDictFindStr( child, "announce", &str ) ) {
588                trackerStat.announce = QString::fromUtf8( str );
589                dynamic_cast<MyApp*>(QApplication::instance())->favicons.add( QUrl( trackerStat.announce ) );
590            }
591            if( tr_bencDictFindInt( child, "announceState", &i ) )
592                trackerStat.announceState = i;
593            if( tr_bencDictFindInt( child, "downloadCount", &i ) )
594                trackerStat.downloadCount = i;
595            if( tr_bencDictFindBool( child, "hasAnnounced", &b ) )
596                trackerStat.hasAnnounced = b;
597            if( tr_bencDictFindBool( child, "hasScraped", &b ) )
598                trackerStat.hasScraped = b;
599            if( tr_bencDictFindStr( child, "host", &str ) )
600                trackerStat.host = QString::fromUtf8( str );
601            if( tr_bencDictFindInt( child, "id", &i ) )
602                trackerStat.id = i;
603            if( tr_bencDictFindBool( child, "isBackup", &b ) )
604                trackerStat.isBackup = b;
605            if( tr_bencDictFindInt( child, "lastAnnouncePeerCount", &i ) )
606                trackerStat.lastAnnouncePeerCount = i;
607            if( tr_bencDictFindStr( child, "lastAnnounceResult", &str ) )
608                trackerStat.lastAnnounceResult = QString::fromUtf8(str);
609            if( tr_bencDictFindInt( child, "lastAnnounceStartTime", &i ) )
610                trackerStat.lastAnnounceStartTime = i;
611            if( tr_bencDictFindBool( child, "lastAnnounceSucceeded", &b ) )
612                trackerStat.lastAnnounceSucceeded = b;
613            if( tr_bencDictFindInt( child, "lastAnnounceTime", &i ) )
614                trackerStat.lastAnnounceTime = i;
615            if( tr_bencDictFindBool( child, "lastAnnounceTimedOut", &b ) )
616                trackerStat.lastAnnounceTimedOut = b;
617            if( tr_bencDictFindStr( child, "lastScrapeResult", &str ) )
618                trackerStat.lastScrapeResult = QString::fromUtf8( str );
619            if( tr_bencDictFindInt( child, "lastScrapeStartTime", &i ) )
620                trackerStat.lastScrapeStartTime = i;
621            if( tr_bencDictFindBool( child, "lastScrapeSucceeded", &b ) )
622                trackerStat.lastScrapeSucceeded = b;
623            if( tr_bencDictFindInt( child, "lastScrapeTime", &i ) )
624                trackerStat.lastScrapeTime = i;
625            if( tr_bencDictFindBool( child, "lastScrapeTimedOut", &b ) )
626                trackerStat.lastScrapeTimedOut = b;
627            if( tr_bencDictFindInt( child, "leecherCount", &i ) )
628                trackerStat.leecherCount = i;
629            if( tr_bencDictFindInt( child, "nextAnnounceTime", &i ) )
630                trackerStat.nextAnnounceTime = i;
631            if( tr_bencDictFindInt( child, "nextScrapeTime", &i ) )
632                trackerStat.nextScrapeTime = i;
633            if( tr_bencDictFindInt( child, "scrapeState", &i ) )
634                trackerStat.scrapeState = i;
635            if( tr_bencDictFindInt( child, "seederCount", &i ) )
636                trackerStat.seederCount = i;
637            if( tr_bencDictFindInt( child, "tier", &i ) )
638                trackerStat.tier = i;
639            trackerStatsList << trackerStat;
640        }
641        myValues[TRACKERSTATS].setValue( trackerStatsList );
642        changed = true;
643    }
644
645    tr_benc * peers;
646    if( tr_bencDictFindList( d, "peers", &peers ) ) {
647        tr_benc * child;
648        PeerList peerList;
649        int childNum = 0;
650        while(( child = tr_bencListChild( peers, childNum++ ))) {
651            double d;
652            bool b;
653            int64_t i;
654            const char * str;
655            Peer peer;
656            if( tr_bencDictFindStr( child, "address", &str ) )
657                peer.address = QString::fromUtf8( str );
658            if( tr_bencDictFindStr( child, "clientName", &str ) )
659                peer.clientName = QString::fromUtf8( str );
660            if( tr_bencDictFindBool( child, "clientIsChoked", &b ) )
661                peer.clientIsChoked = b;
662            if( tr_bencDictFindBool( child, "clientIsInterested", &b ) )
663                peer.clientIsInterested = b;
664            if( tr_bencDictFindStr( child, "flagStr", &str ) )
665                peer.flagStr = QString::fromUtf8( str );
666            if( tr_bencDictFindBool( child, "isDownloadingFrom", &b ) )
667                peer.isDownloadingFrom = b;
668            if( tr_bencDictFindBool( child, "isEncrypted", &b ) )
669                peer.isEncrypted = b;
670            if( tr_bencDictFindBool( child, "isIncoming", &b ) )
671                peer.isIncoming = b;
672            if( tr_bencDictFindBool( child, "isUploadingTo", &b ) )
673                peer.isUploadingTo = b;
674            if( tr_bencDictFindBool( child, "peerIsChoked", &b ) )
675                peer.peerIsChoked = b;
676            if( tr_bencDictFindBool( child, "peerIsInterested", &b ) )
677                peer.peerIsInterested = b;
678            if( tr_bencDictFindInt( child, "port", &i ) )
679                peer.port = i;
680            if( tr_bencDictFindReal( child, "progress", &d ) )
681                peer.progress = d;
682            if( tr_bencDictFindInt( child, "rateToClient", &i ) )
683                peer.rateToClient = Speed::fromBps( i );
684            if( tr_bencDictFindInt( child, "rateToPeer", &i ) )
685                peer.rateToPeer = Speed::fromBps( i );
686            peerList << peer;
687        }
688        myValues[PEERS].setValue( peerList );
689        changed = true;
690    }
691
692    if( changed )
693        emit torrentChanged( id( ) );
694
695    if( !was_seed && isSeed() && (old_verified_size>0) )
696        emit torrentCompleted( id( ) );
697}
698
699QString
700Torrent :: activityString( ) const
701{
702    QString str;
703
704    switch( getActivity( ) )
705    {
706        case TR_STATUS_STOPPED:       str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
707        case TR_STATUS_CHECK_WAIT:    str = tr( "Queued for verification" ); break;
708        case TR_STATUS_CHECK:         str = tr( "Verifying local data" ); break;
709        case TR_STATUS_DOWNLOAD_WAIT: str = tr( "Queued for download" ); break;
710        case TR_STATUS_DOWNLOAD:      str = tr( "Downloading" ); break;
711        case TR_STATUS_SEED_WAIT:     str = tr( "Queued for seeding" ); break;
712        case TR_STATUS_SEED:          str = tr( "Seeding" ); break;
713    }
714
715    return str;
716}
717
718QString
719Torrent :: getError( ) const
720{
721    QString s = getString( ERROR_STRING );
722
723    switch( getInt( ERROR ) )
724    {
725        case TR_STAT_TRACKER_WARNING: s = tr( "Tracker gave a warning: %1" ).arg( s ); break;
726        case TR_STAT_TRACKER_ERROR: s = tr( "Tracker gave an error: %1" ).arg( s ); break;
727        case TR_STAT_LOCAL_ERROR: s = tr( "Error: %1" ).arg( s ); break;
728        default: s.clear(); break;
729    }
730
731    return s;
732}
733
734QPixmap
735TrackerStat :: getFavicon( ) const
736{
737    MyApp * myApp = dynamic_cast<MyApp*>(QApplication::instance());
738    return myApp->favicons.find( QUrl( announce ) );
739}
740
741