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

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

(trunk libT) #2716 "magnet torrents not being saved after quit" -- fix regression reported by RolCol? @ http://trac.transmissionbt.com/ticket/2716#comment:8

File size: 9.2 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                    tr_benc * tmp;
204
205                    /* remove any old .torrent and .resume files */
206                    remove( path );
207                    tr_torrentRemoveResume( tor );
208
209                    dbgmsg( tor, "Saving completed metadata to \"%s\"", path );
210                    assert( !tr_bencDictFindDict( &newMetainfo, "info", &tmp ) );
211                    tr_bencMergeDicts( tr_bencDictAddDict( &newMetainfo, "info", 0 ), &infoDict );
212
213                    success = tr_metainfoParse( tor->session, &newMetainfo, &tor->info,
214                                                &hasInfo, &tor->infoDictOffset, &tor->infoDictLength );
215
216                    assert( hasInfo );
217                    assert( success );
218
219                    /* save the new .torrent file */
220                    tr_bencToFile( &newMetainfo, TR_FMT_BENC, tor->info.torrent );
221                    tr_sessionSetTorrentFile( tor->session, tor->info.hashString, tor->info.torrent );
222                    tr_torrentGotNewInfoDict( tor );
223                    tr_torrentSetDirty( tor );
224
225                    tr_bencFree( &newMetainfo );
226                }
227       
228                tr_bencFree( &infoDict );
229                tr_free( path );
230            }
231        }
232
233        if( success )
234        {
235            incompleteMetadataFree( tor->incompleteMetadata );
236            tor->incompleteMetadata = NULL;
237        }
238        else /* drat. */
239        {
240            const int n = m->pieceCount;
241            for( i=0; i<n; ++i )
242            {
243                m->piecesNeeded[i].piece = i;
244                m->piecesNeeded[i].requestedAt = 0;
245            }
246            m->piecesNeededCount = n;
247            dbgmsg( tor, "metadata error; trying again. %d pieces left", n );
248
249            tr_err( "magnet status: checksum passed %d, metainfo parsed %d",
250                    (int)checksumPassed, (int)metainfoParsed );
251        }
252    }
253}
254
255tr_bool
256tr_torrentGetNextMetadataRequest( tr_torrent * tor, time_t now, int * setme_piece )
257{
258    tr_bool have_request = FALSE;
259    struct tr_incomplete_metadata * m;
260
261    assert( tr_isTorrent( tor ) );
262
263    m = tor->incompleteMetadata;
264
265    if( ( m != NULL )
266        && ( m->piecesNeededCount > 0 )
267        && ( m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now ) )
268    {
269        int i;
270        const int piece = m->piecesNeeded[0].piece;
271
272        tr_removeElementFromArray( m->piecesNeeded, 0,
273                                   sizeof( struct metadata_node ),
274                                   m->piecesNeededCount-- );
275
276        i = m->piecesNeededCount++;
277        m->piecesNeeded[i].piece = piece;
278        m->piecesNeeded[i].requestedAt = now;
279
280        dbgmsg( tor, "next piece to request: %d", piece );
281        *setme_piece = piece;
282        have_request = TRUE;
283    }
284
285    return have_request;
286}
287
288float
289tr_torrentGetMetadataPercent( const tr_torrent * tor )
290{
291    float ret;
292
293    if( tr_torrentHasMetadata( tor ) )
294        ret = 1.0;
295    else {
296        const struct tr_incomplete_metadata * m = tor->incompleteMetadata;
297        if( m == NULL )
298            ret = 0.0;
299        else
300            ret = (m->pieceCount - m->piecesNeededCount) / (float)m->pieceCount;
301    }
302
303    return ret;
304}
305
306char*
307tr_torrentGetMagnetLink( const tr_torrent * tor )
308{
309    int i;
310    char * ret;
311    struct evbuffer * s;
312
313    assert( tr_isTorrent( tor ) );
314
315    s = evbuffer_new( );
316    evbuffer_add_printf( s, "magnet:?xt=urn:btih:%s", tor->info.hashString );
317    evbuffer_add_printf( s, "%s", "&dn=" );
318    tr_http_escape( s, tr_torrentName( tor ), -1, TRUE );
319    for( i=0; i<tor->info.trackerCount; ++i )
320    {
321        evbuffer_add_printf( s, "%s", "&tr=" );
322        tr_http_escape( s, tor->info.trackers[i].announce, -1, TRUE );
323    }
324
325    ret = tr_strndup( EVBUFFER_DATA( s ), EVBUFFER_LENGTH( s ) );
326    evbuffer_free( s );
327    return ret;
328}
Note: See TracBrowser for help on using the repository browser.