1 /*
2 * This file Copyright (C) 2009-2015 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <cassert>
10 #include <iostream>
11
12 #include <QApplication>
13 #include <QString>
14 #include <QUrl>
15 #include <QVariant>
16
17 #include <libtransmission/transmission.h>
18 #include <libtransmission/utils.h> /* tr_new0, tr_strdup */
19 #include <libtransmission/variant.h>
20
21 #include "Application.h"
22 #include "Prefs.h"
23 #include "Torrent.h"
24 #include "Utils.h"
25
26 struct Property
27 {
28 int id;
29 tr_quark key;
30 int type;
31 };
32
33 Property constexpr myProperties[] =
34 {
35 { Torrent::UPLOAD_SPEED, TR_KEY_rateUpload, QVariant::ULongLong } /* Bps */,
36 { Torrent::DOWNLOAD_SPEED, TR_KEY_rateDownload, QVariant::ULongLong }, /* Bps */
37 { Torrent::DOWNLOAD_DIR, TR_KEY_downloadDir, QVariant::String },
38 { Torrent::ACTIVITY, TR_KEY_status, QVariant::Int },
39 { Torrent::NAME, TR_KEY_name, QVariant::String },
40 { Torrent::ERROR, TR_KEY_error, QVariant::Int },
41 { Torrent::ERROR_STRING, TR_KEY_errorString, QVariant::String },
42 { Torrent::SIZE_WHEN_DONE, TR_KEY_sizeWhenDone, QVariant::ULongLong },
43 { Torrent::LEFT_UNTIL_DONE, TR_KEY_leftUntilDone, QVariant::ULongLong },
44 { Torrent::HAVE_UNCHECKED, TR_KEY_haveUnchecked, QVariant::ULongLong },
45 { Torrent::HAVE_VERIFIED, TR_KEY_haveValid, QVariant::ULongLong },
46 { Torrent::DESIRED_AVAILABLE, TR_KEY_desiredAvailable, QVariant::ULongLong },
47 { Torrent::TOTAL_SIZE, TR_KEY_totalSize, QVariant::ULongLong },
48 { Torrent::PIECE_SIZE, TR_KEY_pieceSize, QVariant::ULongLong },
49 { Torrent::PIECE_COUNT, TR_KEY_pieceCount, QVariant::Int },
50 { Torrent::PEERS_GETTING_FROM_US, TR_KEY_peersGettingFromUs, QVariant::Int },
51 { Torrent::PEERS_SENDING_TO_US, TR_KEY_peersSendingToUs, QVariant::Int },
52 { Torrent::WEBSEEDS_SENDING_TO_US, TR_KEY_webseedsSendingToUs, QVariant::Int },
53 { Torrent::PERCENT_DONE, TR_KEY_percentDone, QVariant::Double },
54 { Torrent::METADATA_PERCENT_DONE, TR_KEY_metadataPercentComplete, QVariant::Double },
55 { Torrent::PERCENT_VERIFIED, TR_KEY_recheckProgress, QVariant::Double },
56 { Torrent::DATE_ACTIVITY, TR_KEY_activityDate, QVariant::DateTime },
57 { Torrent::DATE_ADDED, TR_KEY_addedDate, QVariant::DateTime },
58 { Torrent::DATE_STARTED, TR_KEY_startDate, QVariant::DateTime },
59 { Torrent::DATE_CREATED, TR_KEY_dateCreated, QVariant::DateTime },
60 { Torrent::PEERS_CONNECTED, TR_KEY_peersConnected, QVariant::Int },
61 { Torrent::ETA, TR_KEY_eta, QVariant::Int },
62 { Torrent::DOWNLOADED_EVER, TR_KEY_downloadedEver, QVariant::ULongLong },
63 { Torrent::UPLOADED_EVER, TR_KEY_uploadedEver, QVariant::ULongLong },
64 { Torrent::FAILED_EVER, TR_KEY_corruptEver, QVariant::ULongLong },
65 { Torrent::TRACKERSTATS, TR_KEY_trackerStats, CustomVariantType::TrackerStatsList },
66 { Torrent::MIME_ICON, TR_KEY_NONE, QVariant::Icon },
67 { Torrent::SEED_RATIO_LIMIT, TR_KEY_seedRatioLimit, QVariant::Double },
68 { Torrent::SEED_RATIO_MODE, TR_KEY_seedRatioMode, QVariant::Int },
69 { Torrent::SEED_IDLE_LIMIT, TR_KEY_seedIdleLimit, QVariant::Int },
70 { Torrent::SEED_IDLE_MODE, TR_KEY_seedIdleMode, QVariant::Int },
71 { Torrent::DOWN_LIMIT, TR_KEY_downloadLimit, QVariant::Int }, /* KB/s */
72 { Torrent::DOWN_LIMITED, TR_KEY_downloadLimited, QVariant::Bool },
73 { Torrent::UP_LIMIT, TR_KEY_uploadLimit, QVariant::Int }, /* KB/s */
74 { Torrent::UP_LIMITED, TR_KEY_uploadLimited, QVariant::Bool },
75 { Torrent::HONORS_SESSION_LIMITS, TR_KEY_honorsSessionLimits, QVariant::Bool },
76 { Torrent::PEER_LIMIT, TR_KEY_peer_limit, QVariant::Int },
77 { Torrent::HASH_STRING, TR_KEY_hashString, QVariant::String },
78 { Torrent::IS_FINISHED, TR_KEY_isFinished, QVariant::Bool },
79 { Torrent::IS_PRIVATE, TR_KEY_isPrivate, QVariant::Bool },
80 { Torrent::IS_STALLED, TR_KEY_isStalled, QVariant::Bool },
81 { Torrent::COMMENT, TR_KEY_comment, QVariant::String },
82 { Torrent::CREATOR, TR_KEY_creator, QVariant::String },
83 { Torrent::MANUAL_ANNOUNCE_TIME, TR_KEY_manualAnnounceTime, QVariant::DateTime },
84 { Torrent::PEERS, TR_KEY_peers, CustomVariantType::PeerList },
85 { Torrent::BANDWIDTH_PRIORITY, TR_KEY_bandwidthPriority, QVariant::Int },
86 { Torrent::QUEUE_POSITION, TR_KEY_queuePosition, QVariant::Int },
87 { Torrent::EDIT_DATE, TR_KEY_editDate, QVariant::Int },
88 };
89
90 /***
91 ****
92 ***/
93
94 // unchanging fields needed by the main window
95 Torrent::KeyList const Torrent::mainInfoKeys{
96 TR_KEY_addedDate,
97 TR_KEY_downloadDir,
98 TR_KEY_hashString,
99 TR_KEY_id, // must be in every req
100 TR_KEY_name,
101 TR_KEY_totalSize,
102 TR_KEY_trackers,
103 };
104
105 // changing fields needed by the main window
106 Torrent::KeyList const Torrent::mainStatKeys{
107 TR_KEY_downloadedEver,
108 TR_KEY_error,
109 TR_KEY_errorString,
110 TR_KEY_eta,
111 TR_KEY_haveUnchecked,
112 TR_KEY_haveValid,
113 TR_KEY_id, // must be in every req
114 TR_KEY_isFinished,
115 TR_KEY_leftUntilDone,
116 TR_KEY_manualAnnounceTime,
117 TR_KEY_metadataPercentComplete,
118 TR_KEY_peersConnected,
119 TR_KEY_peersGettingFromUs,
120 TR_KEY_peersSendingToUs,
121 TR_KEY_percentDone,
122 TR_KEY_queuePosition,
123 TR_KEY_rateDownload,
124 TR_KEY_rateUpload,
125 TR_KEY_recheckProgress,
126 TR_KEY_seedRatioLimit,
127 TR_KEY_seedRatioMode,
128 TR_KEY_sizeWhenDone,
129 TR_KEY_status,
130 TR_KEY_uploadedEver,
131 TR_KEY_webseedsSendingToUs
132 };
133
134 Torrent::KeyList const Torrent::allMainKeys = Torrent::mainInfoKeys + Torrent::mainStatKeys;
135
136 // unchanging fields needed by the details dialog
137 Torrent::KeyList const Torrent::detailInfoKeys{
138 TR_KEY_comment,
139 TR_KEY_creator,
140 TR_KEY_dateCreated,
141 TR_KEY_files,
142 TR_KEY_id, // must be in every req
143 TR_KEY_isPrivate,
144 TR_KEY_pieceCount,
145 TR_KEY_pieceSize,
146 TR_KEY_trackers
147 };
148
149 // changing fields needed by the details dialog
150 Torrent::KeyList const Torrent::detailStatKeys{
151 TR_KEY_activityDate,
152 TR_KEY_bandwidthPriority,
153 TR_KEY_corruptEver,
154 TR_KEY_desiredAvailable,
155 TR_KEY_downloadedEver,
156 TR_KEY_downloadLimit,
157 TR_KEY_downloadLimited,
158 TR_KEY_fileStats,
159 TR_KEY_honorsSessionLimits,
160 TR_KEY_id, // must be in every req
161 TR_KEY_peer_limit,
162 TR_KEY_peers,
163 TR_KEY_seedIdleLimit,
164 TR_KEY_seedIdleMode,
165 TR_KEY_startDate,
166 TR_KEY_trackerStats,
167 TR_KEY_uploadLimit,
168 TR_KEY_uploadLimited
169 };
170
171 /***
172 ****
173 ***/
174
Torrent(Prefs const & prefs,int id)175 Torrent::Torrent(Prefs const& prefs, int id) :
176 myId(id),
177 myPrefs(prefs)
178 {
179 static_assert(TR_N_ELEMENTS(myProperties) == PROPERTY_COUNT);
180
181 static_assert(([] () constexpr
182 {
183 int i = 0;
184
185 for (auto const& property : myProperties)
186 {
187 if (property.id != i)
188 {
189 return false;
190 }
191
192 ++i;
193 }
194
195 return true;
196 })());
197
198 setIcon(MIME_ICON, Utils::getFileIcon());
199 }
200
201 /***
202 ****
203 ***/
204
setInt(int i,int value)205 bool Torrent::setInt(int i, int value)
206 {
207 bool changed = false;
208
209 assert(0 <= i && i < PROPERTY_COUNT);
210 assert(myProperties[i].type == QVariant::Int);
211
212 if (myValues[i].isNull() || myValues[i].toInt() != value)
213 {
214 myValues[i].setValue(value);
215 changed = true;
216 }
217
218 return changed;
219 }
220
setBool(int i,bool value)221 bool Torrent::setBool(int i, bool value)
222 {
223 bool changed = false;
224
225 assert(0 <= i && i < PROPERTY_COUNT);
226 assert(myProperties[i].type == QVariant::Bool);
227
228 if (myValues[i].isNull() || myValues[i].toBool() != value)
229 {
230 myValues[i].setValue(value);
231 changed = true;
232 }
233
234 return changed;
235 }
236
setDouble(int i,double value)237 bool Torrent::setDouble(int i, double value)
238 {
239 bool changed = false;
240
241 assert(0 <= i && i < PROPERTY_COUNT);
242 assert(myProperties[i].type == QVariant::Double);
243
244 if (myValues[i] != value)
245 {
246 myValues[i].setValue(value);
247 changed = true;
248 }
249
250 return changed;
251 }
252
setTime(int i,time_t value)253 bool Torrent::setTime(int i, time_t value)
254 {
255 bool changed = false;
256
257 assert(0 <= i && i < PROPERTY_COUNT);
258 assert(myProperties[i].type == QVariant::DateTime);
259
260 auto& oldval = myValues[i];
261 auto const newval = qlonglong(value);
262
263 if (oldval != newval)
264 {
265 oldval = newval;
266 changed = true;
267 }
268
269 return changed;
270 }
271
setSize(int i,qulonglong value)272 bool Torrent::setSize(int i, qulonglong value)
273 {
274 bool changed = false;
275
276 assert(0 <= i && i < PROPERTY_COUNT);
277 assert(myProperties[i].type == QVariant::ULongLong);
278
279 if (myValues[i].isNull() || myValues[i].toULongLong() != value)
280 {
281 myValues[i].setValue(value);
282 changed = true;
283 }
284
285 return changed;
286 }
287
setString(int i,char const * value,size_t len)288 bool Torrent::setString(int i, char const* value, size_t len)
289 {
290 bool changed = false;
291
292 assert(0 <= i && i < PROPERTY_COUNT);
293 assert(myProperties[i].type == QVariant::String);
294
295 auto& oldval = myValues[i];
296 auto const newval = QString::fromUtf8(value, len);
297
298 if (oldval != newval)
299 {
300 oldval = newval;
301 changed = true;
302 }
303
304 return changed;
305 }
306
setIcon(int i,QIcon const & value)307 bool Torrent::setIcon(int i, QIcon const& value)
308 {
309 assert(0 <= i && i < PROPERTY_COUNT);
310 assert(myProperties[i].type == QVariant::Icon);
311
312 myValues[i].setValue(value);
313 return true;
314 }
315
getInt(int i) const316 int Torrent::getInt(int i) const
317 {
318 assert(0 <= i && i < PROPERTY_COUNT);
319 assert(myProperties[i].type == QVariant::Int);
320 // assert(!myValues[i].isNull());
321
322 return myValues[i].toInt();
323 }
324
getTime(int i) const325 time_t Torrent::getTime(int i) const
326 {
327 assert(0 <= i && i < PROPERTY_COUNT);
328 assert(myProperties[i].type == QVariant::DateTime);
329 // assert((i == DATE_ADDED) || !myValues[i].isNull());
330
331 return time_t(myValues[i].toLongLong());
332 }
333
getBool(int i) const334 bool Torrent::getBool(int i) const
335 {
336 assert(0 <= i && i < PROPERTY_COUNT);
337 assert(myProperties[i].type == QVariant::Bool);
338 // assert(!myValues[i].isNull());
339
340 return myValues[i].toBool();
341 }
342
getSize(int i) const343 qulonglong Torrent::getSize(int i) const
344 {
345 assert(0 <= i && i < PROPERTY_COUNT);
346 assert(myProperties[i].type == QVariant::ULongLong);
347 // assert(!myValues[i].isNull());
348
349 return myValues[i].toULongLong();
350 }
351
getDouble(int i) const352 double Torrent::getDouble(int i) const
353 {
354 assert(0 <= i && i < PROPERTY_COUNT);
355 assert(myProperties[i].type == QVariant::Double);
356 // assert(!myValues[i].isNull());
357
358 return myValues[i].toDouble();
359 }
360
getString(int i) const361 QString Torrent::getString(int i) const
362 {
363 assert(0 <= i && i < PROPERTY_COUNT);
364 assert(myProperties[i].type == QVariant::String);
365 // assert((i == HASH_STRING) || !myValues[i].isNull());
366
367 return myValues[i].toString();
368 }
369
getIcon(int i) const370 QIcon Torrent::getIcon(int i) const
371 {
372 assert(0 <= i && i < PROPERTY_COUNT);
373 assert(myProperties[i].type == QVariant::Icon);
374 // assert(!myValues[i].isNull());
375
376 return myValues[i].value<QIcon>();
377 }
378
379 /***
380 ****
381 ***/
382
getSeedRatio(double & ratio) const383 bool Torrent::getSeedRatio(double& ratio) const
384 {
385 bool isLimited;
386
387 switch (seedRatioMode())
388 {
389 case TR_RATIOLIMIT_SINGLE:
390 isLimited = true;
391 ratio = seedRatioLimit();
392 break;
393
394 case TR_RATIOLIMIT_GLOBAL:
395 if ((isLimited = myPrefs.getBool(Prefs::RATIO_ENABLED)))
396 {
397 ratio = myPrefs.getDouble(Prefs::RATIO);
398 }
399
400 break;
401
402 default: // TR_RATIOLIMIT_UNLIMITED:
403 isLimited = false;
404 break;
405 }
406
407 return isLimited;
408 }
409
hasTrackerSubstring(QString const & substr) const410 bool Torrent::hasTrackerSubstring(QString const& substr) const
411 {
412 for (auto const& s : trackers())
413 {
414 if (s.contains(substr, Qt::CaseInsensitive))
415 {
416 return true;
417 }
418 }
419
420 return false;
421 }
422
compareSeedRatio(Torrent const & that) const423 int Torrent::compareSeedRatio(Torrent const& that) const
424 {
425 double a;
426 double b;
427 bool const has_a = getSeedRatio(a);
428 bool const has_b = that.getSeedRatio(b);
429
430 if (!has_a && !has_b)
431 {
432 return 0;
433 }
434
435 if (!has_a || !has_b)
436 {
437 return has_a ? -1 : 1;
438 }
439
440 if (a < b)
441 {
442 return -1;
443 }
444
445 if (a > b)
446 {
447 return 1;
448 }
449
450 return 0;
451 }
452
compareRatio(Torrent const & that) const453 int Torrent::compareRatio(Torrent const& that) const
454 {
455 double const a = ratio();
456 double const b = that.ratio();
457
458 if (static_cast<int>(a) == TR_RATIO_INF && static_cast<int>(b) == TR_RATIO_INF)
459 {
460 return 0;
461 }
462
463 if (static_cast<int>(a) == TR_RATIO_INF)
464 {
465 return 1;
466 }
467
468 if (static_cast<int>(b) == TR_RATIO_INF)
469 {
470 return -1;
471 }
472
473 if (a < b)
474 {
475 return -1;
476 }
477
478 if (a > b)
479 {
480 return 1;
481 }
482
483 return 0;
484 }
485
compareETA(Torrent const & that) const486 int Torrent::compareETA(Torrent const& that) const
487 {
488 bool const haveA(hasETA());
489 bool const haveB(that.hasETA());
490
491 if (haveA && haveB)
492 {
493 return getETA() - that.getETA();
494 }
495
496 if (haveA)
497 {
498 return 1;
499 }
500
501 if (haveB)
502 {
503 return -1;
504 }
505
506 return 0;
507 }
508
509 /***
510 ****
511 ***/
512
updateMimeIcon()513 void Torrent::updateMimeIcon()
514 {
515 FileList const& files(myFiles);
516
517 QIcon icon;
518
519 if (files.size() > 1)
520 {
521 icon = Utils::getFolderIcon();
522 }
523 else if (files.size() == 1)
524 {
525 icon = Utils::guessMimeIcon(files.at(0).filename);
526 }
527 else
528 {
529 icon = Utils::guessMimeIcon(name());
530 }
531
532 setIcon(MIME_ICON, icon);
533 }
534
535 /***
536 ****
537 ***/
538
update(tr_quark const * keys,tr_variant ** values,size_t n)539 bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n)
540 {
541 static bool lookup_initialized = false;
542 static int key_to_property_index[TR_N_KEYS];
543 bool changed = false;
544
545 if (!lookup_initialized)
546 {
547 lookup_initialized = true;
548
549 for (int i = 0; i < TR_N_KEYS; ++i)
550 {
551 key_to_property_index[i] = -1;
552 }
553
554 for (int i = 0; i < PROPERTY_COUNT; i++)
555 {
556 key_to_property_index[myProperties[i].key] = i;
557 }
558 }
559
560 for (size_t pos = 0; pos < n; ++pos)
561 {
562 tr_quark key = keys[pos];
563 tr_variant* child = values[pos];
564
565 int const property_index = key_to_property_index[key];
566
567 if (property_index == -1) // we're not interested in this one
568 {
569 continue;
570 }
571
572 assert(myProperties[property_index].key == key);
573
574 switch (myProperties[property_index].type)
575 {
576 case QVariant::Int:
577 {
578 int64_t val;
579
580 if (tr_variantGetInt(child, &val))
581 {
582 changed |= setInt(property_index, val);
583 }
584
585 break;
586 }
587
588 case QVariant::Bool:
589 {
590 bool val;
591
592 if (tr_variantGetBool(child, &val))
593 {
594 changed |= setBool(property_index, val);
595 }
596
597 break;
598 }
599
600 case QVariant::String:
601 {
602 char const* val;
603 size_t len;
604
605 if (tr_variantGetStr(child, &val, &len))
606 {
607 bool const field_changed = setString(property_index, val, len);
608 changed |= field_changed;
609
610 if (field_changed && key == TR_KEY_name)
611 {
612 updateMimeIcon();
613 }
614 }
615
616 break;
617 }
618
619 case QVariant::ULongLong:
620 {
621 int64_t val;
622
623 if (tr_variantGetInt(child, &val))
624 {
625 changed |= setSize(property_index, val);
626 }
627
628 break;
629 }
630
631 case QVariant::Double:
632 {
633 double val;
634
635 if (tr_variantGetReal(child, &val))
636 {
637 changed |= setDouble(property_index, val);
638 }
639
640 break;
641 }
642
643 case QVariant::DateTime:
644 {
645 int64_t val;
646 if (tr_variantGetInt(child, &val) && val &&
647 setTime(property_index, time_t(val)))
648 {
649 changed = true;
650
651 if (key == TR_KEY_editDate)
652 {
653 // FIXME
654 // emit torrentEdited(*this);
655 }
656 }
657
658 break;
659 }
660
661 case CustomVariantType::PeerList:
662 // handled below
663 break;
664
665 default:
666 std::cerr << __FILE__ << ':' << __LINE__ << "unhandled type: " << tr_quark_get_string(key, nullptr) << std::endl;
667 assert(false && "unhandled type");
668 }
669 }
670
671 auto it = std::find(keys, keys + n, TR_KEY_files);
672 if (it != keys + n)
673 {
674 tr_variant* files = values[std::distance(keys, it)];
675 char const* str;
676 int64_t intVal;
677 int i = 0;
678 tr_variant* child;
679
680 myFiles.clear();
681 myFiles.reserve(tr_variantListSize(files));
682
683 while ((child = tr_variantListChild(files, i)) != nullptr)
684 {
685 TorrentFile file;
686 size_t len;
687 file.index = i++;
688
689 if (tr_variantDictFindStr(child, TR_KEY_name, &str, &len))
690 {
691 file.filename = QString::fromUtf8(str, len);
692 }
693
694 if (tr_variantDictFindInt(child, TR_KEY_length, &intVal))
695 {
696 file.size = intVal;
697 }
698
699 myFiles.append(file);
700 }
701
702 updateMimeIcon();
703 changed = true;
704 }
705
706 it = std::find(keys, keys + n, TR_KEY_fileStats);
707 if (it != keys + n)
708 {
709 tr_variant* files = values[std::distance(keys, it)];
710 int const n = tr_variantListSize(files);
711
712 for (int i = 0; i < n && i < myFiles.size(); ++i)
713 {
714 int64_t intVal;
715 bool boolVal;
716 tr_variant* child = tr_variantListChild(files, i);
717 TorrentFile& file(myFiles[i]);
718
719 if (tr_variantDictFindInt(child, TR_KEY_bytesCompleted, &intVal))
720 {
721 file.have = intVal;
722 }
723
724 if (tr_variantDictFindBool(child, TR_KEY_wanted, &boolVal))
725 {
726 file.wanted = boolVal;
727 }
728
729 if (tr_variantDictFindInt(child, TR_KEY_priority, &intVal))
730 {
731 file.priority = intVal;
732 }
733 }
734
735 changed = true;
736 }
737
738 it = std::find(keys, keys + n, TR_KEY_trackers);
739 if (it != keys + n)
740 {
741 tr_variant* v = values[std::distance(keys, it)];
742
743 // build the new tracker list
744 QStringList trackers;
745 trackers.reserve(tr_variantListSize(v));
746 tr_variant* child;
747 int i = 0;
748 while ((child = tr_variantListChild(v, i++)) != nullptr)
749 {
750 char const* str;
751 size_t len;
752 if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len))
753 {
754 trackers.append(QString::fromUtf8(str, len));
755 }
756 }
757
758 // update the trackers
759 if (trackers_ != trackers)
760 {
761 QStringList displayNames;
762 displayNames.reserve(trackers.size());
763 for (auto const& tracker : trackers)
764 {
765 auto const url = QUrl(tracker);
766 auto const key = qApp->faviconCache().add(url);
767 displayNames.append(FaviconCache::getDisplayName(key));
768 }
769
770 displayNames.removeDuplicates();
771
772 trackers_.swap(trackers);
773 trackerDisplayNames_.swap(displayNames);
774 changed = true;
775 }
776 }
777
778 it = std::find(keys, keys + n, TR_KEY_trackerStats);
779 if (it != keys + n)
780 {
781 tr_variant* trackerStats = values[std::distance(keys, it)];
782 tr_variant* child;
783 TrackerStatsList trackerStatsList;
784 int childNum = 0;
785
786 while ((child = tr_variantListChild(trackerStats, childNum++)) != nullptr)
787 {
788 bool b;
789 int64_t i;
790 size_t len;
791 char const* str;
792 TrackerStat trackerStat;
793
794 if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len))
795 {
796 trackerStat.announce = QString::fromUtf8(str, len);
797 }
798
799 if (tr_variantDictFindInt(child, TR_KEY_announceState, &i))
800 {
801 trackerStat.announceState = i;
802 }
803
804 if (tr_variantDictFindInt(child, TR_KEY_downloadCount, &i))
805 {
806 trackerStat.downloadCount = i;
807 }
808
809 if (tr_variantDictFindBool(child, TR_KEY_hasAnnounced, &b))
810 {
811 trackerStat.hasAnnounced = b;
812 }
813
814 if (tr_variantDictFindBool(child, TR_KEY_hasScraped, &b))
815 {
816 trackerStat.hasScraped = b;
817 }
818
819 if (tr_variantDictFindStr(child, TR_KEY_host, &str, &len))
820 {
821 trackerStat.host = QString::fromUtf8(str, len);
822 }
823
824 if (tr_variantDictFindInt(child, TR_KEY_id, &i))
825 {
826 trackerStat.id = i;
827 }
828
829 if (tr_variantDictFindBool(child, TR_KEY_isBackup, &b))
830 {
831 trackerStat.isBackup = b;
832 }
833
834 if (tr_variantDictFindInt(child, TR_KEY_lastAnnouncePeerCount, &i))
835 {
836 trackerStat.lastAnnouncePeerCount = i;
837 }
838
839 if (tr_variantDictFindStr(child, TR_KEY_lastAnnounceResult, &str, &len))
840 {
841 trackerStat.lastAnnounceResult = QString::fromUtf8(str, len);
842 }
843
844 if (tr_variantDictFindInt(child, TR_KEY_lastAnnounceStartTime, &i))
845 {
846 trackerStat.lastAnnounceStartTime = i;
847 }
848
849 if (tr_variantDictFindBool(child, TR_KEY_lastAnnounceSucceeded, &b))
850 {
851 trackerStat.lastAnnounceSucceeded = b;
852 }
853
854 if (tr_variantDictFindInt(child, TR_KEY_lastAnnounceTime, &i))
855 {
856 trackerStat.lastAnnounceTime = i;
857 }
858
859 if (tr_variantDictFindBool(child, TR_KEY_lastAnnounceTimedOut, &b))
860 {
861 trackerStat.lastAnnounceTimedOut = b;
862 }
863
864 if (tr_variantDictFindStr(child, TR_KEY_lastScrapeResult, &str, &len))
865 {
866 trackerStat.lastScrapeResult = QString::fromUtf8(str, len);
867 }
868
869 if (tr_variantDictFindInt(child, TR_KEY_lastScrapeStartTime, &i))
870 {
871 trackerStat.lastScrapeStartTime = i;
872 }
873
874 if (tr_variantDictFindBool(child, TR_KEY_lastScrapeSucceeded, &b))
875 {
876 trackerStat.lastScrapeSucceeded = b;
877 }
878
879 if (tr_variantDictFindInt(child, TR_KEY_lastScrapeTime, &i))
880 {
881 trackerStat.lastScrapeTime = i;
882 }
883
884 if (tr_variantDictFindBool(child, TR_KEY_lastScrapeTimedOut, &b))
885 {
886 trackerStat.lastScrapeTimedOut = b;
887 }
888
889 if (tr_variantDictFindInt(child, TR_KEY_leecherCount, &i))
890 {
891 trackerStat.leecherCount = i;
892 }
893
894 if (tr_variantDictFindInt(child, TR_KEY_nextAnnounceTime, &i))
895 {
896 trackerStat.nextAnnounceTime = i;
897 }
898
899 if (tr_variantDictFindInt(child, TR_KEY_nextScrapeTime, &i))
900 {
901 trackerStat.nextScrapeTime = i;
902 }
903
904 if (tr_variantDictFindInt(child, TR_KEY_scrapeState, &i))
905 {
906 trackerStat.scrapeState = i;
907 }
908
909 if (tr_variantDictFindInt(child, TR_KEY_seederCount, &i))
910 {
911 trackerStat.seederCount = i;
912 }
913
914 if (tr_variantDictFindInt(child, TR_KEY_tier, &i))
915 {
916 trackerStat.tier = i;
917 }
918
919 trackerStatsList << trackerStat;
920 }
921
922 myValues[TRACKERSTATS].setValue(trackerStatsList);
923 changed = true;
924 }
925
926 it = std::find(keys, keys + n, TR_KEY_peers);
927 if (it != keys + n)
928 {
929 tr_variant* peers = values[std::distance(keys, it)];
930 tr_variant* child;
931 PeerList peerList;
932 int childNum = 0;
933
934 while ((child = tr_variantListChild(peers, childNum++)) != nullptr)
935 {
936 double d;
937 bool b;
938 int64_t i;
939 size_t len;
940 char const* str;
941 Peer peer;
942
943 if (tr_variantDictFindStr(child, TR_KEY_address, &str, &len))
944 {
945 peer.address = QString::fromUtf8(str, len);
946 }
947
948 if (tr_variantDictFindStr(child, TR_KEY_clientName, &str, &len))
949 {
950 peer.clientName = QString::fromUtf8(str, len);
951 }
952
953 if (tr_variantDictFindBool(child, TR_KEY_clientIsChoked, &b))
954 {
955 peer.clientIsChoked = b;
956 }
957
958 if (tr_variantDictFindBool(child, TR_KEY_clientIsInterested, &b))
959 {
960 peer.clientIsInterested = b;
961 }
962
963 if (tr_variantDictFindStr(child, TR_KEY_flagStr, &str, &len))
964 {
965 peer.flagStr = QString::fromUtf8(str, len);
966 }
967
968 if (tr_variantDictFindBool(child, TR_KEY_isDownloadingFrom, &b))
969 {
970 peer.isDownloadingFrom = b;
971 }
972
973 if (tr_variantDictFindBool(child, TR_KEY_isEncrypted, &b))
974 {
975 peer.isEncrypted = b;
976 }
977
978 if (tr_variantDictFindBool(child, TR_KEY_isIncoming, &b))
979 {
980 peer.isIncoming = b;
981 }
982
983 if (tr_variantDictFindBool(child, TR_KEY_isUploadingTo, &b))
984 {
985 peer.isUploadingTo = b;
986 }
987
988 if (tr_variantDictFindBool(child, TR_KEY_peerIsChoked, &b))
989 {
990 peer.peerIsChoked = b;
991 }
992
993 if (tr_variantDictFindBool(child, TR_KEY_peerIsInterested, &b))
994 {
995 peer.peerIsInterested = b;
996 }
997
998 if (tr_variantDictFindInt(child, TR_KEY_port, &i))
999 {
1000 peer.port = i;
1001 }
1002
1003 if (tr_variantDictFindReal(child, TR_KEY_progress, &d))
1004 {
1005 peer.progress = d;
1006 }
1007
1008 if (tr_variantDictFindInt(child, TR_KEY_rateToClient, &i))
1009 {
1010 peer.rateToClient = Speed::fromBps(i);
1011 }
1012
1013 if (tr_variantDictFindInt(child, TR_KEY_rateToPeer, &i))
1014 {
1015 peer.rateToPeer = Speed::fromBps(i);
1016 }
1017
1018 peerList << peer;
1019 }
1020
1021 myValues[PEERS].setValue(peerList);
1022 changed = true;
1023 }
1024
1025 return changed;
1026 }
1027
activityString() const1028 QString Torrent::activityString() const
1029 {
1030 QString str;
1031
1032 switch (getActivity())
1033 {
1034 case TR_STATUS_STOPPED:
1035 str = isFinished() ? tr("Finished") : tr("Paused");
1036 break;
1037
1038 case TR_STATUS_CHECK_WAIT:
1039 str = tr("Queued for verification");
1040 break;
1041
1042 case TR_STATUS_CHECK:
1043 str = tr("Verifying local data");
1044 break;
1045
1046 case TR_STATUS_DOWNLOAD_WAIT:
1047 str = tr("Queued for download");
1048 break;
1049
1050 case TR_STATUS_DOWNLOAD:
1051 str = tr("Downloading");
1052 break;
1053
1054 case TR_STATUS_SEED_WAIT:
1055 str = tr("Queued for seeding");
1056 break;
1057
1058 case TR_STATUS_SEED:
1059 str = tr("Seeding");
1060 break;
1061 }
1062
1063 return str;
1064 }
1065
getError() const1066 QString Torrent::getError() const
1067 {
1068 QString s = getString(ERROR_STRING);
1069
1070 switch (getInt(ERROR))
1071 {
1072 case TR_STAT_TRACKER_WARNING:
1073 s = tr("Tracker gave a warning: %1").arg(s);
1074 break;
1075
1076 case TR_STAT_TRACKER_ERROR:
1077 s = tr("Tracker gave an error: %1").arg(s);
1078 break;
1079
1080 case TR_STAT_LOCAL_ERROR:
1081 s = tr("Error: %1").arg(s);
1082 break;
1083
1084 default:
1085 s.clear();
1086 break;
1087 }
1088
1089 return s;
1090 }
1091
getFavicon() const1092 QPixmap TrackerStat::getFavicon() const
1093 {
1094 return qApp->faviconCache().find(QUrl(announce));
1095 }
1096