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 11709 2011-01-19 13:48:47Z jordan $
14 #include <stdio.h> /* remove() */
16 #include <event2/buffer.h>
18 #include "transmission.h"
25 #include "torrent-magnet.h"
29 #define dbgmsg( tor, ... ) \
31 if( tr_deepLoggingIsActive( ) ) \
32 tr_deepLog( __FILE__, __LINE__, tor->info.name, __VA_ARGS__ ); \
41 /* don't ask for the same metadata piece more than this often */
42 MIN_REPEAT_INTERVAL_SECS
= 3
51 struct tr_incomplete_metadata
57 /** sorted from least to most recently requested */
58 struct metadata_node
* piecesNeeded
;
59 int piecesNeededCount
;
63 incompleteMetadataFree( struct tr_incomplete_metadata
* m
)
65 tr_free( m
->metadata
);
66 tr_free( m
->piecesNeeded
);
71 tr_torrentSetMetadataSizeHint( tr_torrent
* tor
, int size
)
73 if( !tr_torrentHasMetadata( tor
) )
75 if( tor
->incompleteMetadata
== NULL
)
78 struct tr_incomplete_metadata
* m
;
79 const int n
= ( size
+ ( METADATA_PIECE_SIZE
- 1 ) ) / METADATA_PIECE_SIZE
;
80 dbgmsg( tor
, "metadata is %d bytes in %d pieces", size
, n
);
82 m
= tr_new( struct tr_incomplete_metadata
, 1 );
84 m
->metadata
= tr_new( uint8_t, size
);
85 m
->metadata_size
= size
;
86 m
->piecesNeededCount
= n
;
87 m
->piecesNeeded
= tr_new( struct metadata_node
, n
);
89 for( i
=0; i
<n
; ++i
) {
90 m
->piecesNeeded
[i
].piece
= i
;
91 m
->piecesNeeded
[i
].requestedAt
= 0;
94 tor
->incompleteMetadata
= m
;
100 findInfoDictOffset( const tr_torrent
* tor
)
103 uint8_t * fileContents
;
106 /* load the file, and find the info dict's offset inside the file */
107 if(( fileContents
= tr_loadFile( tor
->info
.torrent
, &fileLen
)))
111 if( !tr_bencParse( fileContents
, fileContents
+ fileLen
, &top
, NULL
) )
115 if( tr_bencDictFindDict( &top
, "info", &infoDict
) )
118 char * infoContents
= tr_bencToStr( infoDict
, TR_FMT_BENC
, &infoLen
);
119 const uint8_t * i
= (const uint8_t*) tr_memmem( (char*)fileContents
, fileLen
, infoContents
, infoLen
);
120 offset
= i
!= NULL
? i
- fileContents
: 0;
121 tr_free( infoContents
);
127 tr_free( fileContents
);
134 ensureInfoDictOffsetIsCached( tr_torrent
* tor
)
136 assert( tr_torrentHasMetadata( tor
) );
138 if( !tor
->infoDictOffsetIsCached
)
140 tor
->infoDictOffset
= findInfoDictOffset( tor
);
141 tor
->infoDictOffsetIsCached
= TRUE
;
146 tr_torrentGetMetadataPiece( tr_torrent
* tor
, int piece
, int * len
)
150 assert( tr_isTorrent( tor
) );
151 assert( piece
>= 0 );
152 assert( len
!= NULL
);
154 if( tr_torrentHasMetadata( tor
) )
158 ensureInfoDictOffsetIsCached( tor
);
160 assert( tor
->infoDictLength
> 0 );
161 assert( tor
->infoDictOffset
>= 0 );
163 fp
= fopen( tor
->info
.torrent
, "rb" );
166 const int o
= piece
* METADATA_PIECE_SIZE
;
168 if( !fseek( fp
, tor
->infoDictOffset
+ o
, SEEK_SET
) )
170 const int l
= o
+ METADATA_PIECE_SIZE
<= tor
->infoDictLength
171 ? METADATA_PIECE_SIZE
172 : tor
->infoDictLength
- o
;
174 if( 0<l
&& l
<=METADATA_PIECE_SIZE
)
176 char * buf
= tr_new( char, l
);
177 const int n
= fread( buf
, 1, l
, fp
);
197 tr_torrentSetMetadataPiece( tr_torrent
* tor
, int piece
, const void * data
, int len
)
200 struct tr_incomplete_metadata
* m
;
201 const int offset
= piece
* METADATA_PIECE_SIZE
;
203 assert( tr_isTorrent( tor
) );
205 dbgmsg( tor
, "got metadata piece %d", piece
);
207 /* are we set up to download metadata? */
208 m
= tor
->incompleteMetadata
;
212 /* does this data pass the smell test? */
213 if( offset
+ len
> m
->metadata_size
)
216 /* do we need this piece? */
217 for( i
=0; i
<m
->piecesNeededCount
; ++i
)
218 if( m
->piecesNeeded
[i
].piece
== piece
)
220 if( i
==m
->piecesNeededCount
)
223 memcpy( m
->metadata
+ offset
, data
, len
);
225 tr_removeElementFromArray( m
->piecesNeeded
, i
,
226 sizeof( struct metadata_node
),
227 m
->piecesNeededCount
-- );
229 dbgmsg( tor
, "saving metainfo piece %d... %d remain", piece
, m
->piecesNeededCount
);
232 if( m
->piecesNeededCount
== 0 )
234 tr_bool success
= FALSE
;
235 tr_bool checksumPassed
= FALSE
;
236 tr_bool metainfoParsed
= FALSE
;
237 uint8_t sha1
[SHA_DIGEST_LENGTH
];
239 /* we've got a complete set of metainfo... see if it passes the checksum test */
240 dbgmsg( tor
, "metainfo piece %d was the last one", piece
);
241 tr_sha1( sha1
, m
->metadata
, m
->metadata_size
, NULL
);
242 if(( checksumPassed
= !memcmp( sha1
, tor
->info
.hash
, SHA_DIGEST_LENGTH
)))
244 /* checksum passed; now try to parse it as benc */
246 const int err
= tr_bencLoad( m
->metadata
, m
->metadata_size
, &infoDict
, NULL
);
247 dbgmsg( tor
, "err is %d", err
);
248 if(( metainfoParsed
= !err
))
250 /* yay we have bencoded metainfo... merge it into our .torrent file */
252 char * path
= tr_strdup( tor
->info
.torrent
);
254 if( !tr_bencLoadFile( &newMetainfo
, TR_FMT_BENC
, path
) )
260 /* remove any old .torrent and .resume files */
262 tr_torrentRemoveResume( tor
);
264 dbgmsg( tor
, "Saving completed metadata to \"%s\"", path
);
265 tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo
, "info", 0 ), &infoDict
);
267 memset( &info
, 0, sizeof( tr_info
) );
268 success
= tr_metainfoParse( tor
->session
, &newMetainfo
, &info
, &hasInfo
, &infoDictLength
);
270 if( success
&& !tr_getBlockSize( info
.pieceSize
) )
272 tr_torrentSetLocalError( tor
, "%s", _( "Magnet torrent's metadata is not usable" ) );
278 /* keep the new info */
280 tor
->infoDictLength
= infoDictLength
;
282 /* save the new .torrent file */
283 tr_bencToFile( &newMetainfo
, TR_FMT_BENC
, tor
->info
.torrent
);
284 tr_sessionSetTorrentFile( tor
->session
, tor
->info
.hashString
, tor
->info
.torrent
);
285 tr_torrentGotNewInfoDict( tor
);
286 tr_torrentSetDirty( tor
);
289 tr_bencFree( &newMetainfo
);
292 tr_bencFree( &infoDict
);
299 incompleteMetadataFree( tor
->incompleteMetadata
);
300 tor
->incompleteMetadata
= NULL
;
304 const int n
= m
->pieceCount
;
307 m
->piecesNeeded
[i
].piece
= i
;
308 m
->piecesNeeded
[i
].requestedAt
= 0;
310 m
->piecesNeededCount
= n
;
311 dbgmsg( tor
, "metadata error; trying again. %d pieces left", n
);
313 tr_err( "magnet status: checksum passed %d, metainfo parsed %d",
314 (int)checksumPassed
, (int)metainfoParsed
);
320 tr_torrentGetNextMetadataRequest( tr_torrent
* tor
, time_t now
, int * setme_piece
)
322 tr_bool have_request
= FALSE
;
323 struct tr_incomplete_metadata
* m
;
325 assert( tr_isTorrent( tor
) );
327 m
= tor
->incompleteMetadata
;
330 && ( m
->piecesNeededCount
> 0 )
331 && ( m
->piecesNeeded
[0].requestedAt
+ MIN_REPEAT_INTERVAL_SECS
< now
) )
334 const int piece
= m
->piecesNeeded
[0].piece
;
336 tr_removeElementFromArray( m
->piecesNeeded
, 0,
337 sizeof( struct metadata_node
),
338 m
->piecesNeededCount
-- );
340 i
= m
->piecesNeededCount
++;
341 m
->piecesNeeded
[i
].piece
= piece
;
342 m
->piecesNeeded
[i
].requestedAt
= now
;
344 dbgmsg( tor
, "next piece to request: %d", piece
);
345 *setme_piece
= piece
;
353 tr_torrentGetMetadataPercent( const tr_torrent
* tor
)
357 if( tr_torrentHasMetadata( tor
) )
360 const struct tr_incomplete_metadata
* m
= tor
->incompleteMetadata
;
361 if( !m
|| !m
->pieceCount
)
364 ret
= (m
->pieceCount
- m
->piecesNeededCount
) / (double)m
->pieceCount
;
371 tr_torrentGetMagnetLink( const tr_torrent
* tor
)
377 assert( tr_isTorrent( tor
) );
380 evbuffer_add_printf( s
, "magnet:?xt=urn:btih:%s", tor
->info
.hashString
);
381 name
= tr_torrentName( tor
);
384 evbuffer_add_printf( s
, "%s", "&dn=" );
385 tr_http_escape( s
, tr_torrentName( tor
), -1, TRUE
);
387 for( i
=0; i
<tor
->info
.trackerCount
; ++i
)
389 evbuffer_add_printf( s
, "%s", "&tr=" );
390 tr_http_escape( s
, tor
->info
.trackers
[i
].announce
, -1, TRUE
);
393 return evbuffer_free_to_str( s
);