source: trunk/libtransmission/metainfo.c @ 1119

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

add support in libT and the mac interface for displaying comment, creator, and date created

  • Property svn:keywords set to Date Rev Author Id
File size: 11.2 KB
Line 
1/******************************************************************************
2 * $Id: metainfo.c 1119 2006-11-23 01:38:18Z 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" ) ) )
196    {
197        strcatUTF8( inf->comment, val->val.s.s );
198    }
199   
200    /* Creator info */
201    if( ( 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.