source: trunk/libtransmission/torrent-magnet.c @ 10390

Last change on this file since 10390 was 10390, checked in by charles, 12 years ago

(trunk) one of the least interesting commits ever: remove trailing spaces from source code lines...

File size: 9.1 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 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:$
11 */
12
13#include <assert.h>
14#include <event.h> /* struct evbuffer */
15#include <stdio.h> /* remove() */
16
17#include "transmission.h"
18#include "bencode.h"
19#include "crypto.h"
20#include "magnet.h"
21#include "metainfo.h"
22#include "resume.h"
23#include "torrent.h"
24#include "torrent-magnet.h"
25#include "utils.h"
26#include "web.h"
27
28#define dbgmsg( tor, ... ) \
29    do { \
30        if( tr_deepLoggingIsActive( ) ) \
31            tr_deepLog( __FILE__, __LINE__, tor->info.name, __VA_ARGS__ ); \
32    } while( 0 )
33
34/***
35****
36***/
37
38enum
39{
40    /* don't ask for the same metadata piece more than this often */
41    MIN_REPEAT_INTERVAL_SECS = 3
42};
43
44struct metadata_node
45{
46    time_t requestedAt;
47    int piece;
48};
49
50struct tr_incomplete_metadata
51{
52    uint8_t * metadata;
53    int metadata_size;
54    int pieceCount;
55
56    /** sorted from least to most recently requested */
57    struct metadata_node * piecesNeeded;
58    int piecesNeededCount;
59};
60
61static void
62incompleteMetadataFree( struct tr_incomplete_metadata * m )
63{
64    tr_free( m->metadata );
65    tr_free( m->piecesNeeded );
66    tr_free( m );
67}
68
69void
70tr_torrentSetMetadataSizeHint( tr_torrent * tor, int size )
71{
72    if( !tr_torrentHasMetadata( tor ) )
73    {
74        if( tor->incompleteMetadata == NULL )
75        {
76            int i;
77            struct tr_incomplete_metadata * m;
78            int n = ( size + ( METADATA_PIECE_SIZE - 1 ) ) / METADATA_PIECE_SIZE;
79            dbgmsg( tor, "there are %d pieces", n );
80
81            m = tr_new( struct tr_incomplete_metadata, 1 );
82            m->pieceCount = n;
83            m->metadata = tr_new( uint8_t, size );
84            m->metadata_size = size;
85            m->piecesNeededCount = n;
86            m->piecesNeeded = tr_new( struct metadata_node, n );
87
88            for( i=0; i<n; ++i ) {
89                m->piecesNeeded[i].piece = i;
90                m->piecesNeeded[i].requestedAt = 0;
91            }
92
93            tor->incompleteMetadata = m;
94        }
95    }
96}
97
98void*
99tr_torrentGetMetadataPiece( const tr_torrent * tor, int piece, int * len )
100{
101    char * ret = NULL;
102
103    assert( tr_isTorrent( tor ) );
104    assert( piece >= 0 );
105    assert( len != NULL );
106
107    if( tor->infoDictLength > 0 )
108    {
109        FILE * fp = fopen( tor->info.torrent, "rb" );
110        if( fp != NULL )
111        {
112            const int o = piece  * METADATA_PIECE_SIZE;
113
114            if( !fseek( fp, tor->infoDictOffset + o, SEEK_SET ) )
115            {
116                const int l = o + METADATA_PIECE_SIZE <= tor->infoDictLength
117                            ? METADATA_PIECE_SIZE
118                            : tor->infoDictLength - o;
119
120                if( 0<l && l<=METADATA_PIECE_SIZE )
121                {
122                    char * buf = tr_new( char, l );
123                    const int n = fread( buf, 1, l, fp );
124                    if( n == l )
125                    {
126                        *len = l;
127                        ret = buf;
128                        buf = NULL;
129                    }
130
131                    tr_free( buf );
132                }
133            }
134
135            fclose( fp );
136        }
137    }
138
139    return ret;
140}
141
142void
143tr_torrentSetMetadataPiece( tr_torrent  * tor, int piece, const void  * data, int len )
144{
145    int i;
146    struct tr_incomplete_metadata * m;
147    const int offset = piece * METADATA_PIECE_SIZE;
148
149    assert( tr_isTorrent( tor ) );
150
151    dbgmsg( tor, "got metadata piece %d", piece );
152
153    /* are we set up to download metadata? */
154    m = tor->incompleteMetadata;
155    if( m == NULL )
156        return;
157
158    /* does this data pass the smell test? */
159    if( offset + len > m->metadata_size )
160        return;
161
162    /* do we need this piece? */
163    for( i=0; i<m->piecesNeededCount; ++i )
164        if( m->piecesNeeded[i].piece == piece )
165            break;
166    if( i==m->piecesNeededCount )
167        return;
168
169    memcpy( m->metadata + offset, data, len );
170
171    tr_removeElementFromArray( m->piecesNeeded, i,
172                               sizeof( struct metadata_node ),
173                               m->piecesNeededCount-- );
174
175    dbgmsg( tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount );
176
177    /* are we done? */
178    if( m->piecesNeededCount == 0 )
179    {
180        tr_bool success = FALSE;
181        tr_bool checksumPassed = FALSE;
182        tr_bool metainfoParsed = FALSE;
183        uint8_t sha1[SHA_DIGEST_LENGTH];
184
185        /* we've got a complete set of metainfo... see if it passes the checksum test */
186        dbgmsg( tor, "metainfo piece %d was the last one", piece );
187        tr_sha1( sha1, m->metadata, m->metadata_size, NULL );
188        if(( checksumPassed = !memcmp( sha1, tor->info.hash, SHA_DIGEST_LENGTH )))
189        {
190            /* checksum passed; now try to parse it as benc */
191            tr_benc infoDict;
192            const int err = tr_bencLoad( m->metadata, m->metadata_size, &infoDict, NULL );
193            dbgmsg( tor, "err is %d", err );
194            if(( metainfoParsed = !err ))
195            {
196                /* yay we have bencoded metainfo... merge it into our .torrent file */
197                tr_benc newMetainfo;
198                char * path = tr_strdup( tor->info.torrent );
199
200                if( !tr_bencLoadFile( &newMetainfo, TR_FMT_BENC, path ) )
201                {
202                    tr_bool hasInfo;
203
204                    /* remove any old .torrent and .resume files */
205                    remove( path );
206                    tr_torrentRemoveResume( tor );
207
208                    dbgmsg( tor, "Saving completed metadata to \"%s\"", path );
209                    tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo, "info", 0 ), &infoDict );
210
211                    success = tr_metainfoParse( tor->session, &newMetainfo, &tor->info,
212                                                &hasInfo, &tor->infoDictOffset, &tor->infoDictLength );
213
214                    assert( hasInfo );
215                    assert( success );
216
217                    /* save the new .torrent file */
218                    tr_bencToFile( &newMetainfo, TR_FMT_BENC, tor->info.torrent );
219                    tr_sessionSetTorrentFile( tor->session, tor->info.hashString, tor->info.torrent );
220                    tr_torrentGotNewInfoDict( tor );
221                    tr_torrentSetDirty( tor );
222
223                    tr_bencFree( &newMetainfo );
224                }
225
226                tr_bencFree( &infoDict );
227                tr_free( path );
228            }
229        }
230
231        if( success )
232        {
233            incompleteMetadataFree( tor->incompleteMetadata );
234            tor->incompleteMetadata = NULL;
235        }
236        else /* drat. */
237        {
238            const int n = m->pieceCount;
239            for( i=0; i<n; ++i )
240            {
241                m->piecesNeeded[i].piece = i;
242                m->piecesNeeded[i].requestedAt = 0;
243            }
244            m->piecesNeededCount = n;
245            dbgmsg( tor, "metadata error; trying again. %d pieces left", n );
246
247            tr_err( "magnet status: checksum passed %d, metainfo parsed %d",
248                    (int)checksumPassed, (int)metainfoParsed );
249        }
250    }
251}
252
253tr_bool
254tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme_piece )
255{
256    tr_bool have_request = FALSE;
257    struct tr_incomplete_metadata * m;
258
259    assert( tr_isTorrent( tor ) );
260
261    m = tor->incompleteMetadata;
262
263    if( ( m != NULL )
264        && ( m->piecesNeededCount > 0 )
265        && ( m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now ) )
266    {
267        int i;
268        const int piece = m->piecesNeeded[0].piece;
269
270        tr_removeElementFromArray( m->piecesNeeded, 0,
271                                   sizeof( struct metadata_node ),
272                                   m->piecesNeededCount-- );
273
274        i = m->piecesNeededCount++;
275        m->piecesNeeded[i].piece = piece;
276        m->piecesNeeded[i].requestedAt = now;
277
278        dbgmsg( tor, "next piece to request: %d", piece );
279        *setme_piece = piece;
280        have_request = TRUE;
281    }
282
283    return have_request;
284}
285
286float
287tr_torrentGetMetadataPercent( const tr_torrent * tor )
288{
289    float ret;
290
291    if( tr_torrentHasMetadata( tor ) )
292        ret = 1.0;
293    else {
294        const struct tr_incomplete_metadata * m = tor->incompleteMetadata;
295        if( m == NULL )
296            ret = 0.0;
297        else
298            ret = (m->pieceCount - m->piecesNeededCount) / (float)m->pieceCount;
299    }
300
301    return ret;
302}
303
304char*
305tr_torrentGetMagnetLink( const tr_torrent * tor )
306{
307    int i;
308    char * ret;
309    struct evbuffer * s;
310
311    assert( tr_isTorrent( tor ) );
312
313    s = evbuffer_new( );
314    evbuffer_add_printf( s, "magnet:?xt=urn:btih:%s", tor->info.hashString );
315    evbuffer_add_printf( s, "%s", "&dn=" );
316    tr_http_escape( s, tr_torrentName( tor ), -1, TRUE );
317    for( i=0; i<tor->info.trackerCount; ++i )
318    {
319        evbuffer_add_printf( s, "%s", "&tr=" );
320        tr_http_escape( s, tor->info.trackers[i].announce, -1, TRUE );
321    }
322
323    ret = tr_strndup( EVBUFFER_DATA( s ), EVBUFFER_LENGTH( s ) );
324    evbuffer_free( s );
325    return ret;
326}
Note: See TracBrowser for help on using the repository browser.