Transmission: update to 2.61
[tomato.git] / release / src / router / transmission / libtransmission / torrent-magnet.c
blob6708c5737bb65a24d8eb3b20c9a005bce9272f66
1 /*
2 * This file Copyright (C) Mnemosyne LLC
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.
10 * $Id: torrent-magnet.c 13397 2012-07-23 15:28:27Z jordan $
13 #include <assert.h>
14 #include <stdio.h> /* remove() */
15 #include <string.h> /* memcpy(), memset(), memcmp() */
17 #include <event2/buffer.h>
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"
30 #define dbgmsg( tor, ... ) \
31 do { \
32 if( tr_deepLoggingIsActive( ) ) \
33 tr_deepLog( __FILE__, __LINE__, tor->info.name, __VA_ARGS__ ); \
34 } while( 0 )
36 /***
37 ****
38 ***/
40 enum
42 /* don't ask for the same metadata piece more than this often */
43 MIN_REPEAT_INTERVAL_SECS = 3
46 struct metadata_node
48 time_t requestedAt;
49 int piece;
52 struct tr_incomplete_metadata
54 uint8_t * metadata;
55 int metadata_size;
56 int pieceCount;
58 /** sorted from least to most recently requested */
59 struct metadata_node * piecesNeeded;
60 int piecesNeededCount;
63 static void
64 incompleteMetadataFree( struct tr_incomplete_metadata * m )
66 tr_free( m->metadata );
67 tr_free( m->piecesNeeded );
68 tr_free( m );
71 void
72 tr_torrentSetMetadataSizeHint( tr_torrent * tor, int size )
74 if( !tr_torrentHasMetadata( tor ) )
76 if( tor->incompleteMetadata == NULL )
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 );
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 );
90 for( i=0; i<n; ++i ) {
91 m->piecesNeeded[i].piece = i;
92 m->piecesNeeded[i].requestedAt = 0;
95 tor->incompleteMetadata = m;
100 static int
101 findInfoDictOffset( const tr_torrent * tor )
103 size_t fileLen;
104 uint8_t * fileContents;
105 int offset = 0;
107 /* load the file, and find the info dict's offset inside the file */
108 if(( fileContents = tr_loadFile( tor->info.torrent, &fileLen )))
110 tr_benc top;
112 if( !tr_bencParse( fileContents, fileContents + fileLen, &top, NULL ) )
114 tr_benc * infoDict;
116 if( tr_bencDictFindDict( &top, "info", &infoDict ) )
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 );
125 tr_bencFree( &top );
128 tr_free( fileContents );
131 return offset;
134 static void
135 ensureInfoDictOffsetIsCached( tr_torrent * tor )
137 assert( tr_torrentHasMetadata( tor ) );
139 if( !tor->infoDictOffsetIsCached )
141 tor->infoDictOffset = findInfoDictOffset( tor );
142 tor->infoDictOffsetIsCached = true;
146 void*
147 tr_torrentGetMetadataPiece( tr_torrent * tor, int piece, int * len )
149 char * ret = NULL;
151 assert( tr_isTorrent( tor ) );
152 assert( piece >= 0 );
153 assert( len != NULL );
155 if( tr_torrentHasMetadata( tor ) )
157 FILE * fp;
159 ensureInfoDictOffsetIsCached( tor );
161 assert( tor->infoDictLength > 0 );
162 assert( tor->infoDictOffset >= 0 );
164 fp = fopen( tor->info.torrent, "rb" );
165 if( fp != NULL )
167 const int o = piece * METADATA_PIECE_SIZE;
169 if( !fseek( fp, tor->infoDictOffset + o, SEEK_SET ) )
171 const int l = o + METADATA_PIECE_SIZE <= tor->infoDictLength
172 ? METADATA_PIECE_SIZE
173 : tor->infoDictLength - o;
175 if( 0<l && l<=METADATA_PIECE_SIZE )
177 char * buf = tr_new( char, l );
178 const int n = fread( buf, 1, l, fp );
179 if( n == l )
181 *len = l;
182 ret = buf;
183 buf = NULL;
186 tr_free( buf );
190 fclose( fp );
194 return ret;
197 void
198 tr_torrentSetMetadataPiece( tr_torrent * tor, int piece, const void * data, int len )
200 int i;
201 struct tr_incomplete_metadata * m;
202 const int offset = piece * METADATA_PIECE_SIZE;
204 assert( tr_isTorrent( tor ) );
206 dbgmsg( tor, "got metadata piece %d", piece );
208 /* are we set up to download metadata? */
209 m = tor->incompleteMetadata;
210 if( m == NULL )
211 return;
213 /* does this data pass the smell test? */
214 if( offset + len > m->metadata_size )
215 return;
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;
224 memcpy( m->metadata + offset, data, len );
226 tr_removeElementFromArray( m->piecesNeeded, i,
227 sizeof( struct metadata_node ),
228 m->piecesNeededCount-- );
230 dbgmsg( tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount );
232 /* are we done? */
233 if( m->piecesNeededCount == 0 )
235 bool success = false;
236 bool checksumPassed = false;
237 bool metainfoParsed = false;
238 uint8_t sha1[SHA_DIGEST_LENGTH];
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 )))
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 ))
251 /* yay we have bencoded metainfo... merge it into our .torrent file */
252 tr_benc newMetainfo;
253 char * path = tr_strdup( tor->info.torrent );
255 if( !tr_bencLoadFile( &newMetainfo, TR_FMT_BENC, path ) )
257 bool hasInfo;
258 tr_info info;
259 int infoDictLength;
261 /* remove any old .torrent and .resume files */
262 remove( path );
263 tr_torrentRemoveResume( tor );
265 dbgmsg( tor, "Saving completed metadata to \"%s\"", path );
266 tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo, "info", 0 ), &infoDict );
268 memset( &info, 0, sizeof( tr_info ) );
269 success = tr_metainfoParse( tor->session, &newMetainfo, &info, &hasInfo, &infoDictLength );
271 if( success && !tr_getBlockSize( info.pieceSize ) )
273 tr_torrentSetLocalError( tor, "%s", _( "Magnet torrent's metadata is not usable" ) );
274 success = false;
277 if( success )
279 /* keep the new info */
280 tor->info = info;
281 tor->infoDictLength = infoDictLength;
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 );
290 tr_bencFree( &newMetainfo );
293 tr_bencFree( &infoDict );
294 tr_free( path );
298 if( success )
300 incompleteMetadataFree( tor->incompleteMetadata );
301 tor->incompleteMetadata = NULL;
303 else /* drat. */
305 const int n = m->pieceCount;
306 for( i=0; i<n; ++i )
308 m->piecesNeeded[i].piece = i;
309 m->piecesNeeded[i].requestedAt = 0;
311 m->piecesNeededCount = n;
312 dbgmsg( tor, "metadata error; trying again. %d pieces left", n );
314 tr_err( "magnet status: checksum passed %d, metainfo parsed %d",
315 (int)checksumPassed, (int)metainfoParsed );
320 bool
321 tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme_piece )
323 bool have_request = false;
324 struct tr_incomplete_metadata * m;
326 assert( tr_isTorrent( tor ) );
328 m = tor->incompleteMetadata;
330 if( ( m != NULL )
331 && ( m->piecesNeededCount > 0 )
332 && ( m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now ) )
334 int i;
335 const int piece = m->piecesNeeded[0].piece;
337 tr_removeElementFromArray( m->piecesNeeded, 0,
338 sizeof( struct metadata_node ),
339 m->piecesNeededCount-- );
341 i = m->piecesNeededCount++;
342 m->piecesNeeded[i].piece = piece;
343 m->piecesNeeded[i].requestedAt = now;
345 dbgmsg( tor, "next piece to request: %d", piece );
346 *setme_piece = piece;
347 have_request = true;
350 return have_request;
353 double
354 tr_torrentGetMetadataPercent( const tr_torrent * tor )
356 double ret;
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;
368 return ret;
371 char*
372 tr_torrentInfoGetMagnetLink( const tr_info * inf )
374 int i;
375 const char * name;
376 struct evbuffer * s = evbuffer_new( );
378 evbuffer_add_printf( s, "magnet:?xt=urn:btih:%s", inf->hashString );
380 name = inf->name;
381 if( name && *name )
383 evbuffer_add_printf( s, "%s", "&dn=" );
384 tr_http_escape( s, name, -1, true );
387 for( i=0; i<inf->trackerCount; ++i )
389 evbuffer_add_printf( s, "%s", "&tr=" );
390 tr_http_escape( s, inf->trackers[i].announce, -1, true );
393 return evbuffer_free_to_str( s );