static const char* parseFiles (tr_info * inf, tr_variant * files, const tr_variant * length) { int64_t len; inf->totalSize = 0; if (tr_variantIsList (files)) /* multi-file mode */ { tr_file_index_t i; struct evbuffer * buf = evbuffer_new (); inf->isMultifile = 1; inf->fileCount = tr_variantListSize (files); inf->files = tr_new0 (tr_file, inf->fileCount); for (i=0; i<inf->fileCount; i++) { tr_variant * file; tr_variant * path; file = tr_variantListChild (files, i); if (!tr_variantIsDict (file)) return "files"; if (!tr_variantDictFindList (file, TR_KEY_path_utf_8, &path)) if (!tr_variantDictFindList (file, TR_KEY_path, &path)) return "path"; if (!getfile (&inf->files[i].name, inf->name, path, buf)) return "path"; if (!tr_variantDictFindInt (file, TR_KEY_length, &len)) return "length"; inf->files[i].length = len; inf->totalSize += len; } evbuffer_free (buf); } else if (tr_variantGetInt (length, &len)) /* single-file mode */ { if (path_is_suspicious (inf->name)) return "path"; inf->isMultifile = 0; inf->fileCount = 1; inf->files = tr_new0 (tr_file, 1); inf->files[0].name = tr_strdup (inf->name); inf->files[0].length = len; inf->totalSize += len; } else { return "length"; } return NULL; }
static uint64_t loadFilenames(tr_variant* dict, tr_torrent* tor) { tr_variant* list; uint64_t ret = 0; if (tr_variantDictFindList(dict, TR_KEY_files, &list)) { size_t const n = tr_variantListSize(list); tr_file* files = tor->info.files; for (size_t i = 0; i < tor->info.fileCount && i < n; ++i) { char const* str; size_t str_len; if (tr_variantGetStr(tr_variantListChild(list, i), &str, &str_len) && str != NULL && str_len != 0) { tr_free(files[i].name); files[i].name = tr_strndup(str, str_len); files[i].is_renamed = true; } } ret = TR_FR_FILENAMES; } return ret; }
static void on_announce_done(tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata) { tr_announce_response * response; struct announce_data * data = vdata; response = &data->response; (...) // lê os dados da resposta do tracker if (variant_loaded && tr_variantIsDict(&benc)) { int64_t i; size_t len; tr_variant * tmp; // variáveis ... const char * str; const uint8_t * raw; // ... temporárias if (tr_variantDictFindStr(&benc, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup(str, len); if (tr_variantDictFindStr(&benc, TR_KEY_tracker_id, &str, &len)) response->tracker_id_str = tr_strndup(str, len); if (tr_variantDictFindInt(&benc, TR_KEY_complete, &i)) response->seeders = i; (...) if (tr_variantDictFindRaw(&benc, TR_KEY_peers, &raw, &len)) { response->pex = tr_peerMgrCompactToPex(raw, len, NULL, 0, &response->pex_count); } else if (tr_variantDictFindList(&benc, TR_KEY_peers, &tmp)) { response->pex = listToPex(tmp, &response->pex_count); } }
static uint64_t loadDND (tr_variant * dict, tr_torrent * tor) { uint64_t ret = 0; tr_variant * list = NULL; const tr_file_index_t n = tor->info.fileCount; if (tr_variantDictFindList (dict, TR_KEY_dnd, &list) && (tr_variantListSize (list) == n)) { int64_t tmp; tr_file_index_t * dl = tr_new (tr_file_index_t, n); tr_file_index_t * dnd = tr_new (tr_file_index_t, n); tr_file_index_t i, dlCount = 0, dndCount = 0; for (i=0; i<n; ++i) { if (tr_variantGetInt (tr_variantListChild (list, i), &tmp) && tmp) dnd[dndCount++] = i; else dl[dlCount++] = i; } if (dndCount) { tr_torrentInitFileDLs (tor, dnd, dndCount, false); tr_logAddTorDbg (tor, "Resume file found %d files listed as dnd", dndCount); } if (dlCount) { tr_torrentInitFileDLs (tor, dl, dlCount, true); tr_logAddTorDbg (tor, "Resume file found %d files marked for download", dlCount); } tr_free (dnd); tr_free (dl); ret = TR_FR_DND; } else { tr_logAddTorDbg ( tor, "Couldn't load DND flags. DND list (%p) has %zu children; torrent has %d files", list, tr_variantListSize (list), (int)n); } return ret; }
static bool addURL (tr_variant * metainfo, const char * url) { const char * announce = NULL; tr_variant * announce_list = NULL; bool changed = false; const bool had_announce = tr_variantDictFindStr (metainfo, TR_KEY_announce, &announce, NULL); const bool had_announce_list = tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list); if (!had_announce && !had_announce_list) { /* this new tracker is the only one, so add it to "announce"... */ printf ("\tAdded \"%s\" in \"announce\"\n", url); tr_variantDictAddStr (metainfo, TR_KEY_announce, url); changed = true; } else { if (!had_announce_list) { announce_list = tr_variantDictAddList (metainfo, TR_KEY_announce_list, 2); if (had_announce) { /* we're moving from an 'announce' to an 'announce-list', * so copy the old announce URL to the list */ tr_variant * tier = tr_variantListAddList (announce_list, 1); tr_variantListAddStr (tier, announce); changed = true; } } /* If the user-specified URL isn't in the announce list yet, add it */ if (!announce_list_has_url (announce_list, url)) { tr_variant * tier = tr_variantListAddList (announce_list, 1); tr_variantListAddStr (tier, url); printf ("\tAdded \"%s\" to \"announce-list\" tier %"TR_PRIuSIZE"\n", url, tr_variantListSize (announce_list)); changed = true; } } return changed; }
static uint64_t loadFilePriorities (tr_variant * dict, tr_torrent * tor) { tr_variant * list; uint64_t ret = 0; const tr_file_index_t n = tor->info.fileCount; if (tr_variantDictFindList (dict, TR_KEY_priority, &list) && (tr_variantListSize (list) == n)) { int64_t priority; tr_file_index_t i; for (i = 0; i < n; ++i) if (tr_variantGetInt (tr_variantListChild (list, i), &priority)) tr_torrentInitFilePriority (tor, i, priority); ret = TR_FR_FILE_PRIORITIES; } return ret; }
static bool replaceURL (tr_variant * metainfo, const char * in, const char * out) { const char * str; tr_variant * announce_list; bool changed = false; if (tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL) && strstr (str, in)) { char * newstr = replaceSubstr (str, in, out); printf ("\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str, newstr); tr_variantDictAddStr (metainfo, TR_KEY_announce, newstr); tr_free (newstr); changed = true; } if (tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list)) { tr_variant * tier; int tierCount = 0; while ((tier = tr_variantListChild (announce_list, tierCount++))) { tr_variant * node; int nodeCount = 0; while ((node = tr_variantListChild (tier, nodeCount++))) { if (tr_variantGetStr (node, &str, NULL) && strstr (str, in)) { char * newstr = replaceSubstr (str, in, out); printf ("\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount, str, newstr); tr_variantFree (node); tr_variantInitStr (node, newstr, -1); tr_free (newstr); changed = true; } } } } return changed; }
static void on_announce_done (tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata) { tr_announce_response * response; struct announce_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg (data->log_name, "Got announce response"); 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 benc; const bool variant_loaded = !tr_variantFromBenc (&benc, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Announce response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&benc, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Announce response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded && tr_variantIsDict (&benc)) { int64_t i; size_t len; tr_variant * tmp; const char * str; const uint8_t * raw; if (tr_variantDictFindStr (&benc, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindStr (&benc, TR_KEY_warning_message, &str, &len)) response->warning = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_interval, &i)) response->interval = i; if (tr_variantDictFindInt (&benc, TR_KEY_min_interval, &i)) response->min_interval = i; if (tr_variantDictFindStr (&benc, TR_KEY_tracker_id, &str, &len)) response->tracker_id_str = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_complete, &i)) response->seeders = i; if (tr_variantDictFindInt (&benc, TR_KEY_incomplete, &i)) response->leechers = i; if (tr_variantDictFindInt (&benc, TR_KEY_downloaded, &i)) response->downloads = i; if (tr_variantDictFindRaw (&benc, TR_KEY_peers6, &raw, &len)) { dbgmsg (data->log_name, "got a peers6 length of %zu", len); response->pex6 = tr_peerMgrCompact6ToPex (raw, len, NULL, 0, &response->pex6_count); } if (tr_variantDictFindRaw (&benc, TR_KEY_peers, &raw, &len)) { dbgmsg (data->log_name, "got a compact peers length of %zu", len); response->pex = tr_peerMgrCompactToPex (raw, len, NULL, 0, &response->pex_count); } else if (tr_variantDictFindList (&benc, TR_KEY_peers, &tmp)) { response->pex = listToPex (tmp, &response->pex_count); dbgmsg (data->log_name, "got a peers list with %zu entries", response->pex_count); } } if (variant_loaded) tr_variantFree (&benc); } tr_runInEventThread (session, on_announce_done_eventthread, data); }
static char const* parseFiles(tr_info* inf, tr_variant* files, tr_variant const* length) { int64_t len; inf->totalSize = 0; if (tr_variantIsList(files)) /* multi-file mode */ { struct evbuffer* buf; char const* result; if (path_component_is_suspicious(inf->name)) { return "path"; } buf = evbuffer_new(); result = NULL; inf->isFolder = true; inf->fileCount = tr_variantListSize(files); inf->files = tr_new0(tr_file, inf->fileCount); for (tr_file_index_t i = 0; i < inf->fileCount; i++) { tr_variant* file; tr_variant* path; file = tr_variantListChild(files, i); if (!tr_variantIsDict(file)) { result = "files"; break; } if (!tr_variantDictFindList(file, TR_KEY_path_utf_8, &path)) { if (!tr_variantDictFindList(file, TR_KEY_path, &path)) { result = "path"; break; } } if (!getfile(&inf->files[i].name, inf->name, path, buf)) { result = "path"; break; } if (!tr_variantDictFindInt(file, TR_KEY_length, &len)) { result = "length"; break; } inf->files[i].length = len; inf->totalSize += len; } evbuffer_free(buf); return result; } else if (tr_variantGetInt(length, &len)) /* single-file mode */ { if (path_component_is_suspicious(inf->name)) { return "path"; } inf->isFolder = false; inf->fileCount = 1; inf->files = tr_new0(tr_file, 1); inf->files[0].name = tr_strdup(inf->name); inf->files[0].length = len; inf->totalSize += len; } else { return "length"; } return NULL; }
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 bool removeURL (tr_variant * metainfo, const char * url) { const char * str; tr_variant * announce_list; bool changed = false; if (tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL) && !strcmp (str, url)) { printf ("\tRemoved \"%s\" from \"announce\"\n", str); tr_variantDictRemove (metainfo, TR_KEY_announce); changed = true; } if (tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list)) { tr_variant * tier; int tierIndex = 0; while ((tier = tr_variantListChild (announce_list, tierIndex))) { tr_variant * node; int nodeIndex = 0; while ((node = tr_variantListChild (tier, nodeIndex))) { if (tr_variantGetStr (node, &str, NULL) && !strcmp (str, url)) { printf ("\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str, (tierIndex+1)); tr_variantListRemove (tier, nodeIndex); changed = true; } else ++nodeIndex; } if (tr_variantListSize (tier) == 0) { printf ("\tNo URLs left in tier #%d... removing tier\n", (tierIndex+1)); tr_variantListRemove (announce_list, tierIndex); } else { ++tierIndex; } } if (tr_variantListSize (announce_list) == 0) { printf ("\tNo tiers left... removing announce-list\n"); tr_variantDictRemove (metainfo, TR_KEY_announce_list); } } /* if we removed the "announce" field and there's still another track left, * use it as the "announce" field */ if (changed && !tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL)) { tr_variant * tier; tr_variant * node; if ((tier = tr_variantListChild (announce_list, 0))) { if ((node = tr_variantListChild (tier, 0))) { if (tr_variantGetStr (node, &str, NULL)) { tr_variantDictAddStr (metainfo, TR_KEY_announce, str); printf ("\tAdded \"%s\" to announce\n", str); } } } } return changed; }