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