static uint64_t loadIdleLimits(tr_variant* dict, tr_torrent* tor) { tr_variant* d; uint64_t ret = 0; if (tr_variantDictFindDict(dict, TR_KEY_idle_limit, &d)) { int64_t i; int64_t imin; if (tr_variantDictFindInt(d, TR_KEY_idle_limit, &imin)) { tr_torrentSetIdleLimit(tor, imin); } if (tr_variantDictFindInt(d, TR_KEY_idle_mode, &i)) { tr_torrentSetIdleMode(tor, i); } ret = TR_FR_IDLELIMIT; } return ret; }
static uint64_t loadRatioLimits(tr_variant* dict, tr_torrent* tor) { tr_variant* d; uint64_t ret = 0; if (tr_variantDictFindDict(dict, TR_KEY_ratio_limit, &d)) { int64_t i; double dratio; if (tr_variantDictFindReal(d, TR_KEY_ratio_limit, &dratio)) { tr_torrentSetRatioLimit(tor, dratio); } if (tr_variantDictFindInt(d, TR_KEY_ratio_mode, &i)) { tr_torrentSetRatioMode(tor, i); } ret = TR_FR_RATIOLIMIT; } return ret; }
static uint64_t loadSpeedLimits(tr_variant* dict, tr_torrent* tor) { tr_variant* d; uint64_t ret = 0; if (tr_variantDictFindDict(dict, TR_KEY_speed_limit_up, &d)) { loadSingleSpeedLimit(d, TR_UP, tor); ret = TR_FR_SPEEDLIMIT; } if (tr_variantDictFindDict(dict, TR_KEY_speed_limit_down, &d)) { loadSingleSpeedLimit(d, TR_DOWN, tor); ret = TR_FR_SPEEDLIMIT; } return ret; }
int tr_ctorSetMetainfoFromFile (tr_ctor * ctor, const char * filename) { uint8_t * metainfo; size_t len; int err; metainfo = tr_loadFile (filename, &len); if (metainfo && len) err = tr_ctorSetMetainfo (ctor, metainfo, len); else { clearMetainfo (ctor); err = 1; } setSourceFile (ctor, filename); /* if no `name' field was set, then set it from the filename */ if (ctor->isSet_metainfo) { tr_variant * info; if (tr_variantDictFindDict (&ctor->metainfo, TR_KEY_info, &info)) { const char * name; if (!tr_variantDictFindStr (info, TR_KEY_name_utf_8, &name, NULL)) if (!tr_variantDictFindStr (info, TR_KEY_name, &name, NULL)) name = NULL; if (!name || !*name) { char * base = tr_sys_path_basename (filename, NULL); tr_variantDictAddStr (info, TR_KEY_name, base); tr_free (base); } } } tr_free (metainfo); return err; }
static void on_scrape_done (tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata) { tr_scrape_response * response; struct scrape_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg (data->log_name, "Got scrape response for \"%s\"", response->url); if (response_code != HTTP_OK) { const char * fmt = _("Tracker gave HTTP response code %1$ld (%2$s)"); const char * response_str = tr_webGetResponseStr (response_code); response->errmsg = tr_strdup_printf (fmt, response_code, response_str); } else { tr_variant top; int64_t intVal; tr_variant * files; tr_variant * flags; size_t len; const char * str; const bool variant_loaded = !tr_variantFromBenc (&top, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Scrape response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&top, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Scrape response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded) { if (tr_variantDictFindStr (&top, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindDict (&top, TR_KEY_flags, &flags)) if (tr_variantDictFindInt (flags, TR_KEY_min_request_interval, &intVal)) response->min_request_interval = intVal; if (tr_variantDictFindDict (&top, TR_KEY_files, &files)) { int i = 0; for (;;) { int j; tr_quark key; tr_variant * val; /* get the next "file" */ if (!tr_variantDictChild (files, i++, &key, &val)) break; /* populate the corresponding row in our response array */ for (j=0; j<response->row_count; ++j) { struct tr_scrape_response_row * row = &response->rows[j]; if (!memcmp (tr_quark_get_string(key,NULL), row->info_hash, SHA_DIGEST_LENGTH)) { if (tr_variantDictFindInt (val, TR_KEY_complete, &intVal)) row->seeders = intVal; if (tr_variantDictFindInt (val, TR_KEY_incomplete, &intVal)) row->leechers = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaded, &intVal)) row->downloads = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaders, &intVal)) row->downloaders = intVal; break; } } } } tr_variantFree (&top); } } tr_runInEventThread (session, on_scrape_done_eventthread, data); }
static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor) { uint64_t ret = 0; tr_variant* prog; tr_info const* inf = tr_torrentInfo(tor); for (size_t i = 0; i < inf->pieceCount; ++i) { inf->pieces[i].timeChecked = 0; } if (tr_variantDictFindDict(dict, TR_KEY_progress, &prog)) { char const* err; char const* str; uint8_t const* raw; size_t rawlen; tr_variant* l; tr_variant* b; struct tr_bitfield blocks = TR_BITFIELD_INIT; if (tr_variantDictFindList(prog, TR_KEY_time_checked, &l)) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { tr_variant* b = tr_variantListChild(l, fi); tr_file const* f = &inf->files[fi]; if (tr_variantIsInt(b)) { int64_t t; tr_variantGetInt(b, &t); for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = (time_t)t; } } else if (tr_variantIsList(b)) { int64_t offset = 0; int const pieces = f->lastPiece + 1 - f->firstPiece; tr_variantGetInt(tr_variantListChild(b, 0), &offset); for (int i = 0; i < pieces; ++i) { int64_t t = 0; tr_variantGetInt(tr_variantListChild(b, i + 1), &t); inf->pieces[f->firstPiece + i].timeChecked = (time_t)(t != 0 ? t + offset : 0); } } } } else if (tr_variantDictFindList(prog, TR_KEY_mtimes, &l)) { /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { int64_t t; if (tr_variantGetInt(tr_variantListChild(l, fi), &t)) { tr_file const* f = &inf->files[fi]; time_t const mtime = tr_torrentGetFileMTime(tor, fi); time_t const timeChecked = mtime == t ? mtime : 0; for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = timeChecked; } } } } err = NULL; tr_bitfieldConstruct(&blocks, tor->blockCount); if ((b = tr_variantDictFind(prog, TR_KEY_blocks)) != NULL) { size_t buflen; uint8_t const* buf; if (!tr_variantGetRaw(b, &buf, &buflen)) { err = "Invalid value for \"blocks\""; } else if (buflen == 3 && memcmp(buf, "all", 3) == 0) { tr_bitfieldSetHasAll(&blocks); } else if (buflen == 4 && memcmp(buf, "none", 4) == 0) { tr_bitfieldSetHasNone(&blocks); } else { tr_bitfieldSetRaw(&blocks, buf, buflen, true); } } else if (tr_variantDictFindStr(prog, TR_KEY_have, &str, NULL)) { if (strcmp(str, "all") == 0) { tr_bitfieldSetHasAll(&blocks); } else { err = "Invalid value for HAVE"; } } else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen)) { tr_bitfieldSetRaw(&blocks, raw, rawlen, true); } else { err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; } if (err != NULL) { tr_logAddTorDbg(tor, "Torrent needs to be verified - %s", err); } else { tr_cpBlockInit(&tor->completion, &blocks); } tr_bitfieldDestruct(&blocks); ret = TR_FR_PROGRESS; } return ret; }
static void doScrape (const tr_info * inf) { unsigned int i; for (i=0; i<inf->trackerCount; ++i) { CURL * curl; CURLcode res; struct evbuffer * buf; const char * scrape = inf->trackers[i].scrape; char * url; char escaped[SHA_DIGEST_LENGTH*3 + 1]; if (scrape == NULL) continue; tr_http_escape_sha1 (escaped, inf->hash); url = tr_strdup_printf ("%s%cinfo_hash=%s", scrape, strchr (scrape, '?') ? '&' : '?', escaped); printf ("%s ... ", url); fflush (stdout); buf = evbuffer_new (); curl = tr_curl_easy_init (buf); curl_easy_setopt (curl, CURLOPT_URL, url); curl_easy_setopt (curl, CURLOPT_TIMEOUT, TIMEOUT_SECS); if ((res = curl_easy_perform (curl))) { printf ("error: %s\n", curl_easy_strerror (res)); } else { long response; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response); if (response != 200) { printf ("error: unexpected response %ld \"%s\"\n", response, tr_webGetResponseStr (response)); } else /* HTTP OK */ { tr_variant top; tr_variant * files; bool matched = false; const char * begin = (const char*) evbuffer_pullup (buf, -1); if (!tr_variantFromBenc (&top, begin, evbuffer_get_length(buf))) { if (tr_variantDictFindDict (&top, TR_KEY_files, &files)) { int i = 0; tr_quark key; tr_variant * val; while (tr_variantDictChild (files, i++, &key, &val)) { if (memcmp (inf->hash, tr_quark_get_string (key, NULL), SHA_DIGEST_LENGTH) == 0) { int64_t seeders = -1; int64_t leechers = -1; tr_variantDictFindInt (val, TR_KEY_complete, &seeders); tr_variantDictFindInt (val, TR_KEY_incomplete, &leechers); printf ("%d seeders, %d leechers\n", (int)seeders, (int)leechers); matched = true; } } } tr_variantFree (&top); } if (!matched) printf ("no match\n"); } } curl_easy_cleanup (curl); evbuffer_free (buf); tr_free (url); } }