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 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 int test_list( void ) { int64_t i; const char * str; tr_benc top; tr_rpc_parse_list_str( &top, "12", -1 ); check( tr_bencIsInt( &top ) ); check( tr_bencGetInt( &top, &i ) ); check( i == 12 ); tr_bencFree( &top ); tr_rpc_parse_list_str( &top, "12", 1 ); check( tr_bencIsInt( &top ) ); check( tr_bencGetInt( &top, &i ) ); check( i == 1 ); tr_bencFree( &top ); tr_rpc_parse_list_str( &top, "6,7", -1 ); check( tr_bencIsList( &top ) ); check( tr_bencListSize( &top ) == 2 ); check( tr_bencGetInt( tr_bencListChild( &top, 0 ), &i ) ); check( i == 6 ); check( tr_bencGetInt( tr_bencListChild( &top, 1 ), &i ) ); check( i == 7 ); tr_bencFree( &top ); tr_rpc_parse_list_str( &top, "asdf", -1 ); check( tr_bencIsString( &top ) ); check( tr_bencGetStr( &top, &str ) ); check( !strcmp( str, "asdf" ) ); tr_bencFree( &top ); tr_rpc_parse_list_str( &top, "1,3-5", -1 ); check( tr_bencIsList( &top ) ); check( tr_bencListSize( &top ) == 4 ); check( tr_bencGetInt( tr_bencListChild( &top, 0 ), &i ) ); check( i == 1 ); check( tr_bencGetInt( tr_bencListChild( &top, 1 ), &i ) ); check( i == 3 ); check( tr_bencGetInt( tr_bencListChild( &top, 2 ), &i ) ); check( i == 4 ); check( tr_bencGetInt( tr_bencListChild( &top, 3 ), &i ) ); check( i == 5 ); tr_bencFree( &top ); return 0; }
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 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 int testParse( void ) { tr_benc val; tr_benc * child; tr_benc * child2; uint8_t buf[512]; const uint8_t * end; int err; int len; int64_t i; char * saved; tr_snprintf( (char*)buf, sizeof( buf ), "i64e" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( tr_bencGetInt( &val, &i ) ); check( i == 64 ); check( end == buf + 4 ); tr_bencFree( &val ); tr_snprintf( (char*)buf, sizeof( buf ), "li64ei32ei16ee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + strlen( (char*)buf ) ); check( val.val.l.count == 3 ); check( tr_bencGetInt( &val.val.l.vals[0], &i ) ); check( i == 64 ); check( tr_bencGetInt( &val.val.l.vals[1], &i ) ); check( i == 32 ); check( tr_bencGetInt( &val.val.l.vals[2], &i ) ); check( i == 16 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, (char*)buf ) ); tr_free( saved ); tr_bencFree( &val ); end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "lllee" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); check( end == NULL ); end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "le" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + 2 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "le" ) ); tr_free( saved ); tr_bencFree( &val ); if( ( err = testString( "llleee", TRUE ) ) ) return err; if( ( err = testString( "d3:cow3:moo4:spam4:eggse", TRUE ) ) ) return err; if( ( err = testString( "d4:spaml1:a1:bee", TRUE ) ) ) return err; if( ( err = testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", TRUE ) ) ) return err; if( ( err = testString( "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", TRUE ) ) ) return err; if( ( err = testString( "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", TRUE ) ) ) return err; if( ( err = testString( "d1:ai0e1:be", FALSE ) ) ) /* odd number of children */ return err; if( ( err = testString( "", FALSE ) ) ) return err; if( ( err = testString( " ", FALSE ) ) ) return err; /* nested containers * parse an unsorted dict * save as a sorted dict */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "lld1:bi32e1:ai64eeee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + strlen( (const char*)buf ) ); check( ( child = tr_bencListChild( &val, 0 ) ) ); check( ( child2 = tr_bencListChild( child, 0 ) ) ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "lld1:ai64e1:bi32eeee" ) ); tr_free( saved ); tr_bencFree( &val ); /* too many endings */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "leee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + 2 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "le" ) ); tr_free( saved ); tr_bencFree( &val ); /* no ending */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "l1:a1:b1:c" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); /* incomplete string */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "1:" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); return 0; }
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; }