source: trunk/libtransmission/metainfo.c @ 1124

Last change on this file since 1124 was 1124, checked in by livings124, 15 years ago

merge tracker address and announce address into one field in the inspector

  • Property svn:keywords set to Date Rev Author Id
File size: 11.4 KB
Line 
1/******************************************************************************
2 * $Id: metainfo.c 1124 2006-11-23 04:10:16Z livings124 $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27#define TORRENT_MAX_SIZE (5*1024*1024)
28
29/***********************************************************************
30 * Local prototypes
31 **********************************************************************/
32#define strcatUTF8( dst, src) _strcatUTF8( (dst), sizeof( dst ) - 1, (src) )
33static void _strcatUTF8( char *, int, char * );
34
35/***********************************************************************
36 * tr_metainfoParse
37 ***********************************************************************
38 *
39 **********************************************************************/
40int tr_metainfoParse( tr_info_t * inf, const char * path,
41                      const char * savedHash, int saveCopy )
42{
43    FILE       * file;
44    char       * buf;
45    benc_val_t   meta, * beInfo, * list, * val;
46    char * s, * s2, * s3;
47    int          i;
48    struct stat sb;
49
50    assert( NULL == path || NULL == savedHash );
51    /* if savedHash isn't null, saveCopy should be false */
52    assert( NULL == savedHash || !saveCopy );
53
54    if ( NULL != savedHash )
55    {
56        snprintf( inf->torrent, MAX_PATH_LENGTH, "%s/%s",
57                  tr_getTorrentsDirectory(), savedHash );
58        path = inf->torrent;
59    }
60
61    if( stat( path, &sb ) )
62    {
63        tr_err( "Could not stat file (%s)", path );
64        return 1;
65    }
66    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
67    {
68        tr_err( "Not a regular file (%s)", path );
69        return 1;
70    }
71    if( sb.st_size > TORRENT_MAX_SIZE )
72    {
73        tr_err( "Torrent file is too big (%d bytes)", (int)sb.st_size );
74        return 1;
75    }
76
77    /* Load the torrent file into our buffer */
78    file = fopen( path, "rb" );
79    if( !file )
80    {
81        tr_err( "Could not open file (%s)", path );
82        return 1;
83    }
84    buf = malloc( sb.st_size );
85    fseek( file, 0, SEEK_SET );
86    if( fread( buf, sb.st_size, 1, file ) != 1 )
87    {
88        tr_err( "Read error (%s)", path );
89        free( buf );
90        fclose( file );
91        return 1;
92    }
93    fclose( file );
94
95    /* Parse bencoded infos */
96    if( tr_bencLoad( buf, sb.st_size, &meta, NULL ) )
97    {
98        tr_err( "Error while parsing bencoded data" );
99        free( buf );
100        return 1;
101    }
102
103    /* Get info hash */
104    if( !( beInfo = tr_bencDictFind( &meta, "info" ) ) )
105    {
106        tr_err( "Could not find \"info\" dictionary" );
107        tr_bencFree( &meta );
108        free( buf );
109        return 1;
110    }
111    SHA1( (uint8_t *) beInfo->begin,
112          (long) beInfo->end - (long) beInfo->begin, inf->hash );
113    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
114    {
115        sprintf( inf->hashString + i * 2, "%02x", inf->hash[i] );
116    }
117
118    if( saveCopy )
119    {
120        /* Save a copy of the torrent file in the private torrent directory */
121        snprintf( inf->torrent, MAX_PATH_LENGTH, "%s/%s",
122                  tr_getTorrentsDirectory(), inf->hashString );
123        file = fopen( inf->torrent, "wb" );
124        if( !file )
125        {
126            tr_err( "Could not open file (%s) (%s)",
127                    inf->torrent, strerror(errno) );
128            tr_bencFree( &meta );
129            free( buf );
130            return 1;
131        }
132        fseek( file, 0, SEEK_SET );
133        if( fwrite( buf, sb.st_size, 1, file ) != 1 )
134        {
135            tr_err( "Write error (%s)", inf->torrent );
136            tr_bencFree( &meta );
137            free( buf );
138            fclose( file );
139            return 1;
140        }
141        fclose( file );
142    }
143    else
144    {
145        snprintf( inf->torrent, MAX_PATH_LENGTH, "%s", path );
146    }
147
148    /* We won't need this anymore */
149    free( buf );
150
151    if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
152    {
153        tr_err( "No \"announce\" entry" );
154        tr_bencFree( &meta );
155        return 1;
156    }
157   
158    /* Skip spaces */
159    s3 = val->val.s.s;
160    while( *s3 && *s3 == ' ' )
161    {
162        s3++;
163    }
164
165    /* Parse announce URL */
166    if( strncmp( s3, "http://", 7 ) )
167    {
168        tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
169        tr_bencFree( &meta );
170        return 1;
171    }
172    s  = strchr( s3 + 7, ':' );
173    s2 = strchr( s3 + 7, '/' );
174    if( s && s < s2 )
175    {
176        memcpy( inf->trackerAddress, s3 + 7,
177                (long) s - (long) s3 - 7 );
178        inf->trackerPort = atoi( s + 1 );
179    }
180    else if( s2 )
181    {
182        memcpy( inf->trackerAddress, s3 + 7,
183                (long) s2 - (long) s3 - 7 );
184        inf->trackerPort = 80;
185    }
186    else
187    {
188        tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
189        tr_bencFree( &meta );
190        return 1;
191    }
192    snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, "%s", s2 );
193   
194    /* Comment info */
195    if( ( val = tr_bencDictFind( &meta, "comment.utf-8" ) ) || ( val = tr_bencDictFind( &meta, "comment" ) ) )
196    {
197        strcatUTF8( inf->comment, val->val.s.s );
198    }
199   
200    /* Creator info */
201    if( ( val = tr_bencDictFind( &meta, "created by.utf-8" ) ) || ( val = tr_bencDictFind( &meta, "created by" ) ) )
202    {
203        strcatUTF8( inf->creator, val->val.s.s );
204    }
205   
206    /* Date created */
207    if( ( val = tr_bencDictFind( &meta, "creation date" ) ) )
208    {
209        inf->dateCreated = val->val.i;
210    }
211    else
212    {
213        inf->dateCreated = 0;
214    }
215
216    /* Piece length */
217    if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
218    {
219        tr_err( "No \"piece length\" entry" );
220        tr_bencFree( &meta );
221        return 1;
222    }
223    inf->pieceSize = val->val.i;
224
225    /* Hashes */
226    val = tr_bencDictFind( beInfo, "pieces" );
227    if( val->val.s.i % SHA_DIGEST_LENGTH )
228    {
229        tr_err( "Invalid \"piece\" string (size is %d)", val->val.s.i );
230        tr_bencFree( &meta );
231        return 1;
232    }
233    inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
234    inf->pieces = (uint8_t *) val->val.s.s; /* Ugly, but avoids a memcpy */
235    val->val.s.s = NULL;
236
237    /* TODO add more tests so we don't crash on weird files */
238
239    inf->totalSize = 0;
240    if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
241    {
242        /* Multi-file mode */
243        int j;
244
245        val = tr_bencDictFind( beInfo, "name.utf-8" );
246        if( NULL == val )
247        {
248            val = tr_bencDictFind( beInfo, "name" );
249        }
250        strcatUTF8( inf->name, val->val.s.s );
251
252        inf->multifile = 1;
253        inf->fileCount = list->val.l.count;
254        inf->files     = calloc( inf->fileCount * sizeof( tr_file_t ), 1 );
255
256        for( i = 0; i < list->val.l.count; i++ )
257        {
258            val = tr_bencDictFind( &list->val.l.vals[i], "path.utf-8" );
259            if( NULL == val )
260            {
261                val = tr_bencDictFind( &list->val.l.vals[i], "path" );
262            }
263            strcatUTF8( inf->files[i].name, inf->name );
264            for( j = 0; j < val->val.l.count; j++ )
265            {
266                strcatUTF8( inf->files[i].name, "/" );
267                strcatUTF8( inf->files[i].name,
268                            val->val.l.vals[j].val.s.s );
269            }
270            val = tr_bencDictFind( &list->val.l.vals[i], "length" );
271            inf->files[i].length  = val->val.i;
272            inf->totalSize       += val->val.i;
273        }
274
275    }
276    else
277    {
278        /* Single-file mode */
279        inf->multifile = 0;
280        inf->fileCount = 1;
281        inf->files     = calloc( sizeof( tr_file_t ), 1 );
282
283        val = tr_bencDictFind( beInfo, "name.utf-8" );
284        if( NULL == val )
285        {
286            val = tr_bencDictFind( beInfo, "name" );
287        }
288        strcatUTF8( inf->files[0].name, val->val.s.s );
289        strcatUTF8( inf->name, val->val.s.s );
290       
291        val = tr_bencDictFind( beInfo, "length" );
292        inf->files[0].length  = val->val.i;
293        inf->totalSize       += val->val.i;
294    }
295
296    if( (uint64_t) inf->pieceCount !=
297        ( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
298    {
299        tr_err( "Size of hashes and files don't match" );
300        free( inf->pieces );
301        tr_bencFree( &meta );
302        return 1;
303    }
304
305    tr_bencFree( &meta );
306    return 0;
307}
308
309void tr_metainfoRemoveSaved( const char * hashString )
310{
311    char file[MAX_PATH_LENGTH];
312
313    snprintf( file, MAX_PATH_LENGTH, "%s/%s",
314              tr_getTorrentsDirectory(), hashString );
315    unlink(file);
316}
317
318/***********************************************************************
319 * strcatUTF8
320 ***********************************************************************
321 * According to the official specification, all strings in the torrent
322 * file are supposed to be UTF-8 encoded. However, there are
323 * non-compliant torrents around... If we encounter an invalid UTF-8
324 * character, we assume it is ISO 8859-1 and convert it to UTF-8.
325 **********************************************************************/
326#define WANTBYTES( want, got ) \
327    if( (want) > (got) ) { return; } else { (got) -= (want); }
328static void _strcatUTF8( char * s, int len, char * append )
329{
330    char * p;
331
332    /* Go to the end of the destination string */
333    while( s[0] )
334    {
335        s++;
336        len--;
337    }
338
339    /* Now start appending, converting on the fly if necessary */
340    for( p = append; p[0]; )
341    {
342        if( !( p[0] & 0x80 ) )
343        {
344            /* ASCII character */
345            WANTBYTES( 1, len );
346            *(s++) = *(p++);
347            continue;
348        }
349
350        if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
351        {
352            /* 2-bytes UTF-8 character */
353            WANTBYTES( 2, len );
354            *(s++) = *(p++); *(s++) = *(p++);
355            continue;
356        }
357
358        if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
359            ( p[2] & 0xC0 ) == 0x80 )
360        {
361            /* 3-bytes UTF-8 character */
362            WANTBYTES( 3, len );
363            *(s++) = *(p++); *(s++) = *(p++);
364            *(s++) = *(p++);
365            continue;
366        }
367
368        if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
369            ( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
370        {
371            /* 4-bytes UTF-8 character */
372            WANTBYTES( 4, len );
373            *(s++) = *(p++); *(s++) = *(p++);
374            *(s++) = *(p++); *(s++) = *(p++);
375            continue;
376        }
377
378        /* ISO 8859-1 -> UTF-8 conversion */
379        WANTBYTES( 2, len );
380        *(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
381        *(s++) = 0x80 | ( *(p++) & 0x3F );
382    }
383}
Note: See TracBrowser for help on using the repository browser.