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 $
14 #include <stdio.h> /* remove() */
15 #include <string.h> /* memcpy(), memset(), memcmp() */
17 #include <event2/buffer.h>
19 #include "transmission.h"
21 #include "crypto.h" /* tr_sha1() */
26 #include "torrent-magnet.h"
30 #define dbgmsg( tor, ... ) \
32 if( tr_deepLoggingIsActive( ) ) \
33 tr_deepLog( __FILE__, __LINE__, tor->info.name, __VA_ARGS__ ); \
42 /* don't ask for the same metadata piece more than this often */
43 MIN_REPEAT_INTERVAL_SECS
= 3
52 struct tr_incomplete_metadata
58 /** sorted from least to most recently requested */
59 struct metadata_node
* piecesNeeded
;
60 int piecesNeededCount
;
64 incompleteMetadataFree( struct tr_incomplete_metadata
* m
)
66 tr_free( m
->metadata
);
67 tr_free( m
->piecesNeeded
);
72 tr_torrentSetMetadataSizeHint( tr_torrent
* tor
, int size
)
74 if( !tr_torrentHasMetadata( tor
) )
76 if( tor
->incompleteMetadata
== NULL
)
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 );
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
;
101 findInfoDictOffset( const tr_torrent
* tor
)
104 uint8_t * fileContents
;
107 /* load the file, and find the info dict's offset inside the file */
108 if(( fileContents
= tr_loadFile( tor
->info
.torrent
, &fileLen
)))
112 if( !tr_bencParse( fileContents
, fileContents
+ fileLen
, &top
, NULL
) )
116 if( tr_bencDictFindDict( &top
, "info", &infoDict
) )
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
);
128 tr_free( fileContents
);
135 ensureInfoDictOffsetIsCached( tr_torrent
* tor
)
137 assert( tr_torrentHasMetadata( tor
) );
139 if( !tor
->infoDictOffsetIsCached
)
141 tor
->infoDictOffset
= findInfoDictOffset( tor
);
142 tor
->infoDictOffsetIsCached
= true;
147 tr_torrentGetMetadataPiece( tr_torrent
* tor
, int piece
, int * len
)
151 assert( tr_isTorrent( tor
) );
152 assert( piece
>= 0 );
153 assert( len
!= NULL
);
155 if( tr_torrentHasMetadata( tor
) )
159 ensureInfoDictOffsetIsCached( tor
);
161 assert( tor
->infoDictLength
> 0 );
162 assert( tor
->infoDictOffset
>= 0 );
164 fp
= fopen( tor
->info
.torrent
, "rb" );
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
);
198 tr_torrentSetMetadataPiece( tr_torrent
* tor
, int piece
, const void * data
, int len
)
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
;
213 /* does this data pass the smell test? */
214 if( offset
+ len
> m
->metadata_size
)
217 /* do we need this piece? */
218 for( i
=0; i
<m
->piecesNeededCount
; ++i
)
219 if( m
->piecesNeeded
[i
].piece
== piece
)
221 if( i
==m
->piecesNeededCount
)
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
);
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 */
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 */
253 char * path
= tr_strdup( tor
->info
.torrent
);
255 if( !tr_bencLoadFile( &newMetainfo
, TR_FMT_BENC
, path
) )
261 /* remove any old .torrent and .resume files */
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" ) );
279 /* keep the new 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
);
300 incompleteMetadataFree( tor
->incompleteMetadata
);
301 tor
->incompleteMetadata
= NULL
;
305 const int n
= m
->pieceCount
;
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
);
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
;
331 && ( m
->piecesNeededCount
> 0 )
332 && ( m
->piecesNeeded
[0].requestedAt
+ MIN_REPEAT_INTERVAL_SECS
< now
) )
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
;
354 tr_torrentGetMetadataPercent( const tr_torrent
* tor
)
358 if( tr_torrentHasMetadata( tor
) )
361 const struct tr_incomplete_metadata
* m
= tor
->incompleteMetadata
;
362 if( !m
|| !m
->pieceCount
)
365 ret
= (m
->pieceCount
- m
->piecesNeededCount
) / (double)m
->pieceCount
;
372 tr_torrentInfoGetMagnetLink( const tr_info
* inf
)
376 struct evbuffer
* s
= evbuffer_new( );
378 evbuffer_add_printf( s
, "magnet:?xt=urn:btih:%s", inf
->hashString
);
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
);