1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: torrent-magnet.c 13576 2012-10-16 02:48:26Z jordan $ 11 */ 12 13#include <assert.h> 14#include <stdio.h> /* remove() */ 15#include <string.h> /* memcpy(), memset(), memcmp() */ 16 17#include <event2/buffer.h> 18 19#include "transmission.h" 20#include "bencode.h" 21#include "crypto.h" /* tr_sha1() */ 22#include "magnet.h" 23#include "metainfo.h" 24#include "resume.h" 25#include "torrent.h" 26#include "torrent-magnet.h" 27#include "utils.h" 28#include "web.h" 29 30#define dbgmsg( tor, ... ) \ 31 do { \ 32 if( tr_deepLoggingIsActive( ) ) \ 33 tr_deepLog( __FILE__, __LINE__, tr_torrentName( tor ), __VA_ARGS__ ); \ 34 } while( 0 ) 35 36/*** 37**** 38***/ 39 40enum 41{ 42 /* don't ask for the same metadata piece more than this often */ 43 MIN_REPEAT_INTERVAL_SECS = 3 44}; 45 46struct metadata_node 47{ 48 time_t requestedAt; 49 int piece; 50}; 51 52struct tr_incomplete_metadata 53{ 54 uint8_t * metadata; 55 int metadata_size; 56 int pieceCount; 57 58 /** sorted from least to most recently requested */ 59 struct metadata_node * piecesNeeded; 60 int piecesNeededCount; 61}; 62 63static void 64incompleteMetadataFree( struct tr_incomplete_metadata * m ) 65{ 66 tr_free( m->metadata ); 67 tr_free( m->piecesNeeded ); 68 tr_free( m ); 69} 70 71void 72tr_torrentSetMetadataSizeHint( tr_torrent * tor, int size ) 73{ 74 if( !tr_torrentHasMetadata( tor ) ) 75 { 76 if( tor->incompleteMetadata == NULL ) 77 { 78 int i; 79 struct tr_incomplete_metadata * m; 80 const int n = ( size + ( METADATA_PIECE_SIZE - 1 ) ) / METADATA_PIECE_SIZE; 81 dbgmsg( tor, "metadata is %d bytes in %d pieces", size, n ); 82 83 m = tr_new( struct tr_incomplete_metadata, 1 ); 84 m->pieceCount = n; 85 m->metadata = tr_new( uint8_t, size ); 86 m->metadata_size = size; 87 m->piecesNeededCount = n; 88 m->piecesNeeded = tr_new( struct metadata_node, n ); 89 90 for( i=0; i<n; ++i ) { 91 m->piecesNeeded[i].piece = i; 92 m->piecesNeeded[i].requestedAt = 0; 93 } 94 95 tor->incompleteMetadata = m; 96 } 97 } 98} 99 100static int 101findInfoDictOffset( const tr_torrent * tor ) 102{ 103 size_t fileLen; 104 uint8_t * fileContents; 105 int offset = 0; 106 107 /* load the file, and find the info dict's offset inside the file */ 108 if(( fileContents = tr_loadFile( tor->info.torrent, &fileLen ))) 109 { 110 tr_benc top; 111 112 if( !tr_bencParse( fileContents, fileContents + fileLen, &top, NULL ) ) 113 { 114 tr_benc * infoDict; 115 116 if( tr_bencDictFindDict( &top, "info", &infoDict ) ) 117 { 118 int infoLen; 119 char * infoContents = tr_bencToStr( infoDict, TR_FMT_BENC, &infoLen ); 120 const uint8_t * i = (const uint8_t*) tr_memmem( (char*)fileContents, fileLen, infoContents, infoLen ); 121 offset = i != NULL ? i - fileContents : 0; 122 tr_free( infoContents ); 123 } 124 125 tr_bencFree( &top ); 126 } 127 128 tr_free( fileContents ); 129 } 130 131 return offset; 132} 133 134static void 135ensureInfoDictOffsetIsCached( tr_torrent * tor ) 136{ 137 assert( tr_torrentHasMetadata( tor ) ); 138 139 if( !tor->infoDictOffsetIsCached ) 140 { 141 tor->infoDictOffset = findInfoDictOffset( tor ); 142 tor->infoDictOffsetIsCached = true; 143 } 144} 145 146void* 147tr_torrentGetMetadataPiece( tr_torrent * tor, int piece, int * len ) 148{ 149 char * ret = NULL; 150 151 assert( tr_isTorrent( tor ) ); 152 assert( piece >= 0 ); 153 assert( len != NULL ); 154 155 if( tr_torrentHasMetadata( tor ) ) 156 { 157 FILE * fp; 158 159 ensureInfoDictOffsetIsCached( tor ); 160 161 assert( tor->infoDictLength > 0 ); 162 assert( tor->infoDictOffset >= 0 ); 163 164 fp = fopen( tor->info.torrent, "rb" ); 165 if( fp != NULL ) 166 { 167 const int o = piece * METADATA_PIECE_SIZE; 168 169 if( !fseek( fp, tor->infoDictOffset + o, SEEK_SET ) ) 170 { 171 const int l = o + METADATA_PIECE_SIZE <= tor->infoDictLength 172 ? METADATA_PIECE_SIZE 173 : tor->infoDictLength - o; 174 175 if( 0<l && l<=METADATA_PIECE_SIZE ) 176 { 177 char * buf = tr_new( char, l ); 178 const int n = fread( buf, 1, l, fp ); 179 if( n == l ) 180 { 181 *len = l; 182 ret = buf; 183 buf = NULL; 184 } 185 186 tr_free( buf ); 187 } 188 } 189 190 fclose( fp ); 191 } 192 } 193 194 return ret; 195} 196 197void 198tr_torrentSetMetadataPiece( tr_torrent * tor, int piece, const void * data, int len ) 199{ 200 int i; 201 struct tr_incomplete_metadata * m; 202 const int offset = piece * METADATA_PIECE_SIZE; 203 204 assert( tr_isTorrent( tor ) ); 205 206 dbgmsg( tor, "got metadata piece %d", piece ); 207 208 /* are we set up to download metadata? */ 209 m = tor->incompleteMetadata; 210 if( m == NULL ) 211 return; 212 213 /* does this data pass the smell test? */ 214 if( offset + len > m->metadata_size ) 215 return; 216 217 /* do we need this piece? */ 218 for( i=0; i<m->piecesNeededCount; ++i ) 219 if( m->piecesNeeded[i].piece == piece ) 220 break; 221 if( i==m->piecesNeededCount ) 222 return; 223 224 memcpy( m->metadata + offset, data, len ); 225 226 tr_removeElementFromArray( m->piecesNeeded, i, 227 sizeof( struct metadata_node ), 228 m->piecesNeededCount-- ); 229 230 dbgmsg( tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount ); 231 232 /* are we done? */ 233 if( m->piecesNeededCount == 0 ) 234 { 235 bool success = false; 236 bool checksumPassed = false; 237 bool metainfoParsed = false; 238 uint8_t sha1[SHA_DIGEST_LENGTH]; 239 240 /* we've got a complete set of metainfo... see if it passes the checksum test */ 241 dbgmsg( tor, "metainfo piece %d was the last one", piece ); 242 tr_sha1( sha1, m->metadata, m->metadata_size, NULL ); 243 if(( checksumPassed = !memcmp( sha1, tor->info.hash, SHA_DIGEST_LENGTH ))) 244 { 245 /* checksum passed; now try to parse it as benc */ 246 tr_benc infoDict; 247 const int err = tr_bencLoad( m->metadata, m->metadata_size, &infoDict, NULL ); 248 dbgmsg( tor, "err is %d", err ); 249 if(( metainfoParsed = !err )) 250 { 251 /* yay we have bencoded metainfo... merge it into our .torrent file */ 252 tr_benc newMetainfo; 253 char * path = tr_strdup( tor->info.torrent ); 254 255 if( !tr_bencLoadFile( &newMetainfo, TR_FMT_BENC, path ) ) 256 { 257 bool hasInfo; 258 tr_info info; 259 int infoDictLength; 260 261 /* remove any old .torrent and .resume files */ 262 remove( path ); 263 tr_torrentRemoveResume( tor ); 264 265 dbgmsg( tor, "Saving completed metadata to \"%s\"", path ); 266 tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo, "info", 0 ), &infoDict ); 267 268 memset( &info, 0, sizeof( tr_info ) ); 269 success = tr_metainfoParse( tor->session, &newMetainfo, &info, &hasInfo, &infoDictLength ); 270 271 if( success && !tr_getBlockSize( info.pieceSize ) ) 272 { 273 tr_torrentSetLocalError( tor, "%s", _( "Magnet torrent's metadata is not usable" ) ); 274 success = false; 275 } 276 277 if( success ) 278 { 279 /* keep the new info */ 280 tor->info = info; 281 tor->infoDictLength = infoDictLength; 282 283 /* save the new .torrent file */ 284 tr_bencToFile( &newMetainfo, TR_FMT_BENC, tor->info.torrent ); 285 tr_sessionSetTorrentFile( tor->session, tor->info.hashString, tor->info.torrent ); 286 tr_torrentGotNewInfoDict( tor ); 287 tr_torrentSetDirty( tor ); 288 } 289 290 tr_bencFree( &newMetainfo ); 291 } 292 293 tr_bencFree( &infoDict ); 294 tr_free( path ); 295 } 296 } 297 298 if( success ) 299 { 300 incompleteMetadataFree( tor->incompleteMetadata ); 301 tor->incompleteMetadata = NULL; 302 } 303 else /* drat. */ 304 { 305 const int n = m->pieceCount; 306 for( i=0; i<n; ++i ) 307 { 308 m->piecesNeeded[i].piece = i; 309 m->piecesNeeded[i].requestedAt = 0; 310 } 311 m->piecesNeededCount = n; 312 dbgmsg( tor, "metadata error; trying again. %d pieces left", n ); 313 314 tr_err( "magnet status: checksum passed %d, metainfo parsed %d", 315 (int)checksumPassed, (int)metainfoParsed ); 316 } 317 } 318} 319 320bool 321tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme_piece ) 322{ 323 bool have_request = false; 324 struct tr_incomplete_metadata * m; 325 326 assert( tr_isTorrent( tor ) ); 327 328 m = tor->incompleteMetadata; 329 330 if( ( m != NULL ) 331 && ( m->piecesNeededCount > 0 ) 332 && ( m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now ) ) 333 { 334 int i; 335 const int piece = m->piecesNeeded[0].piece; 336 337 tr_removeElementFromArray( m->piecesNeeded, 0, 338 sizeof( struct metadata_node ), 339 m->piecesNeededCount-- ); 340 341 i = m->piecesNeededCount++; 342 m->piecesNeeded[i].piece = piece; 343 m->piecesNeeded[i].requestedAt = now; 344 345 dbgmsg( tor, "next piece to request: %d", piece ); 346 *setme_piece = piece; 347 have_request = true; 348 } 349 350 return have_request; 351} 352 353double 354tr_torrentGetMetadataPercent( const tr_torrent * tor ) 355{ 356 double ret; 357 358 if( tr_torrentHasMetadata( tor ) ) 359 ret = 1.0; 360 else { 361 const struct tr_incomplete_metadata * m = tor->incompleteMetadata; 362 if( !m || !m->pieceCount ) 363 ret = 0.0; 364 else 365 ret = (m->pieceCount - m->piecesNeededCount) / (double)m->pieceCount; 366 } 367 368 return ret; 369} 370 371char* 372tr_torrentInfoGetMagnetLink( const tr_info * inf ) 373{ 374 int i; 375 const char * name; 376 struct evbuffer * s = evbuffer_new( ); 377 378 evbuffer_add_printf( s, "magnet:?xt=urn:btih:%s", inf->hashString ); 379 380 name = inf->name; 381 if( name && *name ) 382 { 383 evbuffer_add_printf( s, "%s", "&dn=" ); 384 tr_http_escape( s, name, -1, true ); 385 } 386 387 for( i=0; i<inf->trackerCount; ++i ) 388 { 389 evbuffer_add_printf( s, "%s", "&tr=" ); 390 tr_http_escape( s, inf->trackers[i].announce, -1, true ); 391 } 392 393 return evbuffer_free_to_str( s ); 394} 395