source: trunk/libtransmission/metainfo.c @ 261

Last change on this file since 261 was 261, checked in by titer, 16 years ago

Updated svn:keywords

  • Property svn:keywords set to Date Rev Author Id
File size: 8.6 KB
Line 
1/******************************************************************************
2 * $Id: metainfo.c 261 2006-05-29 21:27:31Z titer $
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/***********************************************************************
28 * Local prototypes
29 **********************************************************************/
30static void strcatUTF8( char *, char * );
31
32/***********************************************************************
33 * tr_metainfoParse
34 ***********************************************************************
35 *
36 **********************************************************************/
37int tr_metainfoParse( tr_info_t * inf, const char * path )
38{
39    FILE       * file;
40    char       * buf;
41    benc_val_t   meta, * beInfo, * list, * val;
42    char * s, * s2, * s3;
43    int          i;
44    struct stat sb;
45
46    snprintf( inf->torrent, MAX_PATH_LENGTH, "%s", path );
47
48    if( stat( path, &sb ) )
49    {
50        fprintf( stderr, "Could not stat file (%s)\n", path );
51        return 1;
52    }
53    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
54    {
55        fprintf( stderr, "Not a regular file (%s)\n", path );
56        return 1;
57    }
58    if( sb.st_size > 2097152 )
59    {
60        tr_err( "Torrent file is too big (%d bytes)", sb.st_size );
61        return 1;
62    }
63
64    /* Load the torrent file into our buffer */
65    file = fopen( path, "rb" );
66    if( !file )
67    {
68        fprintf( stderr, "Could not open file (%s)\n", path );
69        return 1;
70    }
71    buf = malloc( sb.st_size );
72    fseek( file, 0, SEEK_SET );
73    if( fread( buf, sb.st_size, 1, file ) != 1 )
74    {
75        fprintf( stderr, "Read error (%s)\n", path );
76        free( buf );
77        fclose( file );
78        return 1;
79    }
80    fclose( file );
81
82    /* Parse bencoded infos */
83    if( tr_bencLoad( buf, sb.st_size, &meta, NULL ) )
84    {
85        fprintf( stderr, "Error while parsing bencoded data\n" );
86        free( buf );
87        return 1;
88    }
89
90    /* Get info hash */
91    if( !( beInfo = tr_bencDictFind( &meta, "info" ) ) )
92    {
93        fprintf( stderr, "Could not find \"info\" dictionary\n" );
94        tr_bencFree( &meta );
95        free( buf );
96        return 1;
97    }
98    SHA1( (uint8_t *) beInfo->begin,
99          (long) beInfo->end - (long) beInfo->begin, inf->hash );
100
101    /* No that we got the hash, we won't need this anymore */
102    free( buf );
103
104    if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
105    {
106        fprintf( stderr, "No \"announce\" entry\n" );
107        tr_bencFree( &meta );
108        return 1;
109    }
110   
111    /* Skip spaces */
112    s3 = val->val.s.s;
113    while( *s3 && *s3 == ' ' )
114    {
115        s3++;
116    }
117
118    /* Parse announce URL */
119    if( strncmp( s3, "http://", 7 ) )
120    {
121        fprintf( stderr, "Invalid announce URL (%s)\n",
122                 inf->trackerAddress );
123        tr_bencFree( &meta );
124        return 1;
125    }
126    s  = strchr( s3 + 7, ':' );
127    s2 = strchr( s3 + 7, '/' );
128    if( s && s < s2 )
129    {
130        memcpy( inf->trackerAddress, s3 + 7,
131                (long) s - (long) s3 - 7 );
132        inf->trackerPort = atoi( s + 1 );
133    }
134    else if( s2 )
135    {
136        memcpy( inf->trackerAddress, s3 + 7,
137                (long) s2 - (long) s3 - 7 );
138        inf->trackerPort = 80;
139    }
140    else
141    {
142        fprintf( stderr, "Invalid announce URL (%s)\n",
143                 inf->trackerAddress );
144        tr_bencFree( &meta );
145        return 1;
146    }
147    snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, "%s", s2 );
148
149    /* Piece length */
150    if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
151    {
152        fprintf( stderr, "No \"piece length\" entry\n" );
153        tr_bencFree( &meta );
154        return 1;
155    }
156    inf->pieceSize = val->val.i;
157
158    /* Hashes */
159    val = tr_bencDictFind( beInfo, "pieces" );
160    if( val->val.s.i % SHA_DIGEST_LENGTH )
161    {
162        fprintf( stderr, "Invalid \"piece\" string (size is %d)\n",
163                 val->val.s.i );
164        return 1;
165    }
166    inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
167    inf->pieces = (uint8_t *) val->val.s.s; /* Ugly, but avoids a memcpy */
168    val->val.s.s = NULL;
169
170    /* TODO add more tests so we don't crash on weird files */
171
172    inf->totalSize = 0;
173    if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
174    {
175        /* Multi-file mode */
176        int j;
177
178        val = tr_bencDictFind( beInfo, "name" );
179        strcatUTF8( inf->name, val->val.s.s );
180
181        inf->fileCount = list->val.l.count;
182        inf->files     = calloc( inf->fileCount * sizeof( tr_file_t ), 1 );
183
184        for( i = 0; i < list->val.l.count; i++ )
185        {
186            val = tr_bencDictFind( &list->val.l.vals[i], "path" );
187            strcatUTF8( inf->files[i].name, inf->name );
188            for( j = 0; j < val->val.l.count; j++ )
189            {
190                strcatUTF8( inf->files[i].name, "/" );
191                strcatUTF8( inf->files[i].name,
192                            val->val.l.vals[j].val.s.s );
193            }
194            val = tr_bencDictFind( &list->val.l.vals[i], "length" );
195            inf->files[i].length  = val->val.i;
196            inf->totalSize       += val->val.i;
197        }
198
199    }
200    else
201    {
202        /* Single-file mode */
203        inf->fileCount = 1;
204        inf->files     = calloc( sizeof( tr_file_t ), 1 );
205
206        val = tr_bencDictFind( beInfo, "name" );
207        strcatUTF8( inf->files[0].name, val->val.s.s );
208        strcatUTF8( inf->name, val->val.s.s );
209       
210        val = tr_bencDictFind( beInfo, "length" );
211        inf->files[0].length  = val->val.i;
212        inf->totalSize       += val->val.i;
213    }
214
215    if( (uint64_t) inf->pieceCount !=
216        ( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
217    {
218        fprintf( stderr, "Size of hashes and files don't match\n" );
219        tr_bencFree( &meta );
220        return 1;
221    }
222
223    tr_bencFree( &meta );
224    return 0;
225}
226
227/***********************************************************************
228 * strcatUTF8
229 ***********************************************************************
230 * According to the official specification, all strings in the torrent
231 * file are supposed to be UTF-8 encoded. However, there are
232 * non-compliant torrents around... If we encounter an invalid UTF-8
233 * character, we assume it is ISO 8859-1 and convert it to UTF-8.
234 **********************************************************************/
235static void strcatUTF8( char * s, char * append )
236{
237    char * p;
238
239    /* Go to the end of the destination string */
240    while( s[0] )
241    {
242        s++;
243    }
244
245    /* Now start appending, converting on the fly if necessary */
246    for( p = append; p[0]; )
247    {
248        if( !( p[0] & 0x80 ) )
249        {
250            /* ASCII character */
251            *(s++) = *(p++);
252            continue;
253        }
254
255        if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
256        {
257            /* 2-bytes UTF-8 character */
258            *(s++) = *(p++); *(s++) = *(p++);
259            continue;
260        }
261
262        if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
263            ( p[2] & 0xC0 ) == 0x80 )
264        {
265            /* 3-bytes UTF-8 character */
266            *(s++) = *(p++); *(s++) = *(p++);
267            *(s++) = *(p++);
268            continue;
269        }
270
271        if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
272            ( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
273        {
274            /* 4-bytes UTF-8 character */
275            *(s++) = *(p++); *(s++) = *(p++);
276            *(s++) = *(p++); *(s++) = *(p++);
277            continue;
278        }
279
280        /* ISO 8859-1 -> UTF-8 conversion */
281        *(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
282        *(s++) = 0x80 | ( *(p++) & 0x3F );
283    }
284}
Note: See TracBrowser for help on using the repository browser.