source: trunk/libtransmission/metainfo.c @ 1122

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

stupid typo

  • Property svn:keywords set to Date Rev Author Id
File size: 11.3 KB
Line 
1/******************************************************************************
2 * $Id: metainfo.c 1122 2006-11-23 02:55:28Z 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
212    /* Piece length */
213    if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
214    {
215        tr_err( "No \"piece length\" entry" );
216        tr_bencFree( &meta );
217        return 1;
218    }
219    inf->pieceSize = val->val.i;
220
221    /* Hashes */
222    val = tr_bencDictFind( beInfo, "pieces" );
223    if( val->val.s.i % SHA_DIGEST_LENGTH )
224    {
225        tr_err( "Invalid \"piece\" string (size is %d)", val->val.s.i );
226        tr_bencFree( &meta );
227        return 1;
228    }
229    inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
230    inf->pieces = (uint8_t *) val->val.s.s; /* Ugly, but avoids a memcpy */
231    val->val.s.s = NULL;
232
233    /* TODO add more tests so we don't crash on weird files */
234
235    inf->totalSize = 0;
236    if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
237    {
238        /* Multi-file mode */
239        int j;
240
241        val = tr_bencDictFind( beInfo, "name.utf-8" );
242        if( NULL == val )
243        {
244            val = tr_bencDictFind( beInfo, "name" );
245        }
246        strcatUTF8( inf->name, val->val.s.s );
247
248        inf->multifile = 1;
249        inf->fileCount = list->val.l.count;
250        inf->files     = calloc( inf->fileCount * sizeof( tr_file_t ), 1 );
251
252        for( i = 0; i < list->val.l.count; i++ )
253        {
254            val = tr_bencDictFind( &list->val.l.vals[i], "path.utf-8" );
255            if( NULL == val )
256            {
257                val = tr_bencDictFind( &list->val.l.vals[i], "path" );
258            }
259            strcatUTF8( inf->files[i].name, inf->name );
260            for( j = 0; j < val->val.l.count; j++ )
261            {
262                strcatUTF8( inf->files[i].name, "/" );
263                strcatUTF8( inf->files[i].name,
264                            val->val.l.vals[j].val.s.s );
265            }
266            val = tr_bencDictFind( &list->val.l.vals[i], "length" );
267            inf->files[i].length  = val->val.i;
268            inf->totalSize       += val->val.i;
269        }
270
271    }
272    else
273    {
274        /* Single-file mode */
275        inf->multifile = 0;
276        inf->fileCount = 1;
277        inf->files     = calloc( sizeof( tr_file_t ), 1 );
278
279        val = tr_bencDictFind( beInfo, "name.utf-8" );
280        if( NULL == val )
281        {
282            val = tr_bencDictFind( beInfo, "name" );
283        }
284        strcatUTF8( inf->files[0].name, val->val.s.s );
285        strcatUTF8( inf->name, val->val.s.s );
286       
287        val = tr_bencDictFind( beInfo, "length" );
288        inf->files[0].length  = val->val.i;
289        inf->totalSize       += val->val.i;
290    }
291
292    if( (uint64_t) inf->pieceCount !=
293        ( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
294    {
295        tr_err( "Size of hashes and files don't match" );
296        free( inf->pieces );
297        tr_bencFree( &meta );
298        return 1;
299    }
300
301    tr_bencFree( &meta );
302    return 0;
303}
304
305void tr_metainfoRemoveSaved( const char * hashString )
306{
307    char file[MAX_PATH_LENGTH];
308
309    snprintf( file, MAX_PATH_LENGTH, "%s/%s",
310              tr_getTorrentsDirectory(), hashString );
311    unlink(file);
312}
313
314/***********************************************************************
315 * strcatUTF8
316 ***********************************************************************
317 * According to the official specification, all strings in the torrent
318 * file are supposed to be UTF-8 encoded. However, there are
319 * non-compliant torrents around... If we encounter an invalid UTF-8
320 * character, we assume it is ISO 8859-1 and convert it to UTF-8.
321 **********************************************************************/
322#define WANTBYTES( want, got ) \
323    if( (want) > (got) ) { return; } else { (got) -= (want); }
324static void _strcatUTF8( char * s, int len, char * append )
325{
326    char * p;
327
328    /* Go to the end of the destination string */
329    while( s[0] )
330    {
331        s++;
332        len--;
333    }
334
335    /* Now start appending, converting on the fly if necessary */
336    for( p = append; p[0]; )
337    {
338        if( !( p[0] & 0x80 ) )
339        {
340            /* ASCII character */
341            WANTBYTES( 1, len );
342            *(s++) = *(p++);
343            continue;
344        }
345
346        if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
347        {
348            /* 2-bytes UTF-8 character */
349            WANTBYTES( 2, len );
350            *(s++) = *(p++); *(s++) = *(p++);
351            continue;
352        }
353
354        if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
355            ( p[2] & 0xC0 ) == 0x80 )
356        {
357            /* 3-bytes UTF-8 character */
358            WANTBYTES( 3, len );
359            *(s++) = *(p++); *(s++) = *(p++);
360            *(s++) = *(p++);
361            continue;
362        }
363
364        if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
365            ( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
366        {
367            /* 4-bytes UTF-8 character */
368            WANTBYTES( 4, len );
369            *(s++) = *(p++); *(s++) = *(p++);
370            *(s++) = *(p++); *(s++) = *(p++);
371            continue;
372        }
373
374        /* ISO 8859-1 -> UTF-8 conversion */
375        WANTBYTES( 2, len );
376        *(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
377        *(s++) = 0x80 | ( *(p++) & 0x3F );
378    }
379}
Note: See TracBrowser for help on using the repository browser.