static const char* parseFiles (tr_info * inf, tr_benc * files, const tr_benc * length) { int64_t len; inf->totalSize = 0; if (tr_bencIsList (files)) /* multi-file mode */ { tr_file_index_t i; struct evbuffer * buf = evbuffer_new (); inf->isMultifile = 1; inf->fileCount = tr_bencListSize (files); inf->files = tr_new0 (tr_file, inf->fileCount); for (i=0; i<inf->fileCount; i++) { tr_benc * file; tr_benc * path; file = tr_bencListChild (files, i); if (!tr_bencIsDict (file)) return "files"; if (!tr_bencDictFindList (file, "path.utf-8", &path)) if (!tr_bencDictFindList (file, "path", &path)) return "path"; if (!getfile (&inf->files[i].name, inf->name, path, buf)) return "path"; if (!tr_bencDictFindInt (file, "length", &len)) return "length"; inf->files[i].length = len; inf->totalSize += len; } evbuffer_free (buf); } else if (tr_bencGetInt (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 void geturllist( tr_info * inf, tr_benc * meta ) { tr_benc * urls; const char * url; if( tr_bencDictFindList( meta, "url-list", &urls ) ) { int i; const int n = tr_bencListSize( urls ); inf->webseedCount = 0; inf->webseeds = tr_new0( char*, n ); for( i = 0; i < n; ++i ) { if( tr_bencGetStr( tr_bencListChild( urls, i ), &url ) ) { const size_t len = strlen( url ); if( tr_urlIsValid( url, len ) ) inf->webseeds[inf->webseedCount++] = tr_strndup( url, len ); } } }
static tr_bool addURL( tr_benc * metainfo, const char * url ) { const char * str; tr_benc * announce_list; tr_bool changed = FALSE; tr_bool match = FALSE; /* maybe add it to "announce" */ if( !tr_bencDictFindStr( metainfo, "announce", &str ) ) { printf( "\tAdded \"%s\" in \"announce\"\n", url ); tr_bencDictAddStr( metainfo, "announce", url ); changed = TRUE; } /* see if it's already in announce-list */ if( tr_bencDictFindList( metainfo, "announce-list", &announce_list ) ) { tr_benc * tier; int tierCount = 0; while(( tier = tr_bencListChild( announce_list, tierCount++ ))) { tr_benc * node; int nodeCount = 0; while(( node = tr_bencListChild( tier, nodeCount++ ))) if( tr_bencGetStr( node, &str ) && !strcmp( str, url ) ) match = TRUE; } } /* if it's not in announce-list, add it now */ if( !match ) { tr_benc * tier; if( !tr_bencDictFindList( metainfo, "announce-list", &announce_list ) ) announce_list = tr_bencDictAddList( metainfo, "announce-list", 1 ); tier = tr_bencListAddList( announce_list, 1 ); tr_bencListAddStr( tier, url ); printf( "\tAdded \"%s\" to \"announce-list\" tier %zu\n", url, tr_bencListSize( announce_list ) ); changed = TRUE; } return changed; }
static uint64_t loadDND( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_info * inf = &tor->info; const tr_file_index_t n = inf->fileCount; tr_benc * list = NULL; if( tr_bencDictFindList( dict, KEY_DND, &list ) && ( tr_bencListSize( 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_bencGetInt( tr_bencListChild( list, i ), &tmp ) && tmp ) dnd[dndCount++] = i; else dl[dlCount++] = i; } if( dndCount ) { tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE ); tr_tordbg( tor, "Resume file found %d files listed as dnd", dndCount ); } if( dlCount ) { tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE ); tr_tordbg( tor, "Resume file found %d files marked for download", dlCount ); } tr_free( dnd ); tr_free( dl ); ret = TR_FR_DND; } else { tr_tordbg( tor, "Couldn't load DND flags. DND list (%p) has %zu children; torrent has %d files", list, tr_bencListSize( list ), (int)n ); } return ret; }
static bool addURL (tr_benc * metainfo, const char * url) { const char * announce = NULL; tr_benc * announce_list = NULL; bool changed = false; const bool had_announce = tr_bencDictFindStr (metainfo, "announce", &announce); const bool had_announce_list = tr_bencDictFindList (metainfo, "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_bencDictAddStr (metainfo, "announce", url); changed = true; } else { if (!had_announce_list) { announce_list = tr_bencDictAddList (metainfo, "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_benc * tier = tr_bencListAddList (announce_list, 1); tr_bencListAddStr (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_benc * tier = tr_bencListAddList (announce_list, 1); tr_bencListAddStr (tier, url); printf ("\tAdded \"%s\" to \"announce-list\" tier %zu\n", url, tr_bencListSize (announce_list)); changed = true; } } return changed; }
static uint64_t loadFilePriorities( tr_benc * dict, tr_torrent * tor ) { tr_benc * list; uint64_t ret = 0; const tr_file_index_t n = tor->info.fileCount; if( tr_bencDictFindList( dict, KEY_FILE_PRIORITIES, &list ) && ( tr_bencListSize( list ) == n ) ) { int64_t priority; tr_file_index_t i; for( i = 0; i < n; ++i ) if( tr_bencGetInt( tr_bencListChild( list, i ), &priority ) ) tr_torrentInitFilePriority( tor, i, priority ); ret = TR_FR_FILE_PRIORITIES; } return ret; }
static bool replaceURL (tr_benc * metainfo, const char * in, const char * out) { const char * str; tr_benc * announce_list; bool changed = false; if (tr_bencDictFindStr (metainfo, "announce", &str) && strstr (str, in)) { char * newstr = replaceSubstr (str, in, out); printf ("\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str, newstr); tr_bencDictAddStr (metainfo, "announce", newstr); tr_free (newstr); changed = true; } if (tr_bencDictFindList (metainfo, "announce-list", &announce_list)) { tr_benc * tier; int tierCount = 0; while ((tier = tr_bencListChild (announce_list, tierCount++))) { tr_benc * node; int nodeCount = 0; while ((node = tr_bencListChild (tier, nodeCount++))) { if (tr_bencGetStr (node, &str) && strstr (str, in)) { char * newstr = replaceSubstr (str, in, out); printf ("\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount, str, newstr); tr_bencFree (node); tr_bencInitStr (node, newstr, -1); tr_free (newstr); changed = true; } } } } return changed; }
static uint64_t loadPriorities( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_info * inf = &tor->info; const tr_file_index_t n = inf->fileCount; tr_benc * list; if( tr_bencDictFindList( dict, KEY_PRIORITY, &list ) && ( tr_bencListSize( list ) == n ) ) { int64_t tmp; tr_file_index_t i; for( i = 0; i < n; ++i ) if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) inf->files[i].priority = tmp; ret = TR_FR_PRIORITY; } return ret; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { size_t i, n; uint64_t ret = 0; tr_benc * prog; const tr_info * inf = tr_torrentInfo( tor ); for( i=0, n=inf->pieceCount; i<n; ++i ) inf->pieces[i].timeChecked = 0; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) ) { const char * err; const char * str; const uint8_t * raw; size_t rawlen; tr_benc * l; tr_benc * b; struct tr_bitset bitset = TR_BITSET_INIT; if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &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. */ tr_file_index_t fi; for( fi=0; fi<inf->fileCount; ++fi ) { tr_benc * b = tr_bencListChild( l, fi ); const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]+1; if( tr_bencIsInt( b ) ) { int64_t t; tr_bencGetInt( b, &t ); for( ; p!=pend; ++p ) p->timeChecked = (time_t)t; } else if( tr_bencIsList( b ) ) { int i = 0; int64_t offset = 0; const int pieces = f->lastPiece + 1 - f->firstPiece; tr_bencGetInt( tr_bencListChild( b, 0 ), &offset ); for( i=0; i<pieces; ++i ) { int64_t t = 0; tr_bencGetInt( tr_bencListChild( b, i+1 ), &t ); inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0); } } } } else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) ) { tr_file_index_t fi; /* 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( fi=0; fi<inf->fileCount; ++fi ) { int64_t t; if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) ) { const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]; const time_t mtime = tr_torrentGetFileMTime( tor, fi ); const time_t timeChecked = mtime==t ? mtime : 0; for( ; p!=pend; ++p ) p->timeChecked = timeChecked; } } } err = NULL; if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS ))) { if( !tr_bitsetFromBenc( &bitset, b ) ) err = "Invalid value for PIECES"; } else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) ) { if( !strcmp( str, "all" ) ) tr_bitsetSetHaveAll( &bitset ); else err = "Invalid value for HAVE"; } else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { bitset.bitfield.bits = (void*) raw; bitset.bitfield.byteCount = rawlen; bitset.bitfield.bitCount = rawlen * 8; } else err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; if( !err && !tr_cpBlockBitsetInit( &tor->completion, &bitset ) ) err = "Error loading bitfield"; if( err != NULL ) tr_tordbg( tor, "Torrent needs to be verified - %s", err ); ret = TR_FR_PROGRESS; } 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; 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_benc benc; const int benc_loaded = !tr_bencLoad( msg, msglen, &benc, NULL ); if( getenv( "TR_CURL_VERBOSE" ) != NULL ) { struct evbuffer * buf = tr_bencToBuf( &benc, TR_FMT_JSON ); fprintf( stderr, "Announce response:\n< %s\n", evbuffer_pullup( buf, -1 ) ); tr_free( buf ); } if( benc_loaded && tr_bencIsDict( &benc ) ) { int64_t i; size_t rawlen; tr_benc * tmp; const char * str; const uint8_t * raw; if( tr_bencDictFindStr( &benc, "failure reason", &str ) ) response->errmsg = tr_strdup( str ); if( tr_bencDictFindStr( &benc, "warning message", &str ) ) response->warning = tr_strdup( str ); if( tr_bencDictFindInt( &benc, "interval", &i ) ) response->interval = i; if( tr_bencDictFindInt( &benc, "min interval", &i ) ) response->min_interval = i; if( tr_bencDictFindStr( &benc, "tracker id", &str ) ) response->tracker_id_str = tr_strdup( str ); if( tr_bencDictFindInt( &benc, "complete", &i ) ) response->seeders = i; if( tr_bencDictFindInt( &benc, "incomplete", &i ) ) response->leechers = i; if( tr_bencDictFindInt( &benc, "downloaded", &i ) ) response->downloads = i; if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) { dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen ); response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen, NULL, 0, &response->pex6_count ); } if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) { dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen ); response->pex = tr_peerMgrCompactToPex( raw, rawlen, NULL, 0, &response->pex_count ); } else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) { response->pex = listToPex( tmp, &response->pex_count ); dbgmsg( data->log_name, "got a peers list with %zu entries", response->pex_count ); } } if( benc_loaded ) tr_bencFree( &benc ); } tr_runInEventThread( session, on_announce_done_eventthread, data ); }
static bool removeURL (tr_benc * metainfo, const char * url) { const char * str; tr_benc * announce_list; bool changed = false; if (tr_bencDictFindStr (metainfo, "announce", &str) && !strcmp (str, url)) { printf ("\tRemoved \"%s\" from \"announce\"\n", str); tr_bencDictRemove (metainfo, "announce"); changed = true; } if (tr_bencDictFindList (metainfo, "announce-list", &announce_list)) { tr_benc * tier; int tierIndex = 0; while ((tier = tr_bencListChild (announce_list, tierIndex))) { tr_benc * node; int nodeIndex = 0; while ((node = tr_bencListChild (tier, nodeIndex))) { if (tr_bencGetStr (node, &str) && !strcmp (str, url)) { printf ("\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str, (tierIndex+1)); tr_bencListRemove (tier, nodeIndex); changed = true; } else ++nodeIndex; } if (tr_bencListSize (tier) == 0) { printf ("\tNo URLs left in tier #%d... removing tier\n", (tierIndex+1)); tr_bencListRemove (announce_list, tierIndex); } else { ++tierIndex; } } if (tr_bencListSize (announce_list) == 0) { printf ("\tNo tiers left... removing announce-list\n"); tr_bencDictRemove (metainfo, "announce-list"); } } /* if we removed the "announce" field and there's still another track left, * use it as the "announce" field */ if (changed && !tr_bencDictFindStr (metainfo, "announce", &str)) { tr_benc * tier; tr_benc * node; if ((tier = tr_bencListChild (announce_list, 0))) { if ((node = tr_bencListChild (tier, 0))) { if (tr_bencGetStr (node, &str)) { tr_bencDictAddStr (metainfo, "announce", str); printf ("\tAdded \"%s\" to announce\n", str); } } } } return changed; }
static const char* getannounce( tr_info * inf, tr_benc * meta ) { const char * str; tr_tracker_info * trackers = NULL; int trackerCount = 0; tr_benc * tiers; /* Announce-list */ if( tr_bencDictFindList( meta, "announce-list", &tiers ) ) { int n; int i, j, validTiers; const int numTiers = tr_bencListSize( tiers ); n = 0; for( i = 0; i < numTiers; ++i ) n += tr_bencListSize( tr_bencListChild( tiers, i ) ); trackers = tr_new0( tr_tracker_info, n ); for( i = 0, validTiers = 0; i < numTiers; ++i ) { tr_benc * tier = tr_bencListChild( tiers, i ); const int tierSize = tr_bencListSize( tier ); tr_bool anyAdded = FALSE; for( j = 0; j < tierSize; ++j ) { if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_urlIsValidTracker( url ) ) { tr_tracker_info * t = trackers + trackerCount; t->tier = validTiers; t->announce = tr_strdup( url ); t->scrape = tr_convertAnnounceToScrape( url ); t->id = trackerCount; anyAdded = TRUE; ++trackerCount; } tr_free( url ); } } if( anyAdded ) ++validTiers; } /* did we use any of the tiers? */ if( !trackerCount ) { tr_free( trackers ); trackers = NULL; } } /* Regular announce value */ if( !trackerCount && tr_bencDictFindStr( meta, "announce", &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_urlIsValidTracker( url ) ) { trackers = tr_new0( tr_tracker_info, 1 ); trackers[trackerCount].tier = 0; trackers[trackerCount].announce = tr_strdup( url ); trackers[trackerCount].scrape = tr_convertAnnounceToScrape( url ); trackers[trackerCount].id = 0; trackerCount++; /*fprintf( stderr, "single announce: [%s]\n", url );*/ } tr_free( url ); } inf->trackers = trackers; inf->trackerCount = trackerCount; return NULL; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_benc * p; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) ) { const uint8_t * raw; size_t rawlen; tr_benc * m; size_t n; time_t * curMTimes = tr_torrentGetMTimes( tor, &n ); if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m ) && ( n == tor->info.fileCount ) && ( n == tr_bencListSize( m ) ) ) { size_t i; for( i = 0; i < n; ++i ) { int64_t tmp; if( !tr_bencGetInt( tr_bencListChild( m, i ), &tmp ) ) { tr_tordbg( tor, "File #%zu needs to be verified - couldn't find benc entry", i ); tr_torrentSetFileChecked( tor, i, FALSE ); } else { const time_t t = (time_t) tmp; if( t == curMTimes[i] ) tr_torrentSetFileChecked( tor, i, TRUE ); else { tr_tordbg( tor, "File #%zu needs to be verified - times %lu and %lu don't match", i, t, curMTimes[i] ); tr_torrentSetFileChecked( tor, i, FALSE ); } } } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" ); } if( tr_bencDictFindRaw( p, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { tr_bitfield tmp; tmp.byteCount = rawlen; tmp.bitCount = tmp.byteCount * 8; tmp.bits = (uint8_t*) raw; if( !tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" ); } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" ); } tr_free( curMTimes ); ret = TR_FR_PROGRESS; } return ret; }
static const char* getannounce( tr_info * inf, tr_benc * meta ) { const char * str; tr_tracker_info * trackers = NULL; int trackerCount = 0; tr_benc * tiers; /* Announce-list */ if( tr_bencDictFindList( meta, "announce-list", &tiers ) ) { int n; int i, j; const int numTiers = tr_bencListSize( tiers ); n = 0; for( i = 0; i < numTiers; ++i ) n += tr_bencListSize( tr_bencListChild( tiers, i ) ); trackers = tr_new0( tr_tracker_info, n ); trackerCount = 0; for( i = 0; i < numTiers; ++i ) { tr_benc * tier = tr_bencListChild( tiers, i ); const int tierSize = tr_bencListSize( tier ); for( j = 0; j < tierSize; ++j ) { if( tr_bencGetStr( tr_bencListChild( tier, j ), &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_httpIsValidURL( url ) ) { tr_tracker_info * t = trackers + trackerCount++; t->tier = i; t->announce = tr_strdup( url ); t->scrape = announceToScrape( url ); } tr_free( url ); } } } /* did we use any of the tiers? */ if( !trackerCount ) { tr_free( trackers ); trackers = NULL; } } /* Regular announce value */ if( !trackerCount && tr_bencDictFindStr( meta, "announce", &str ) ) { char * url = tr_strstrip( tr_strdup( str ) ); if( tr_httpIsValidURL( url ) ) { trackers = tr_new0( tr_tracker_info, 1 ); trackers[trackerCount].tier = 0; trackers[trackerCount].announce = tr_strdup( url ); trackers[trackerCount++].scrape = announceToScrape( url ); /*fprintf( stderr, "single announce: [%s]\n", url );*/ } tr_free( url ); } inf->trackers = trackers; inf->trackerCount = trackerCount; return inf->trackerCount ? NULL : "announce"; }