source: trunk/libtransmission/metainfo.c @ 1

Last change on this file since 1 was 1, checked in by root, 15 years ago

Import from 2005-10-26

File size: 8.6 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25/***********************************************************************
26 * Local prototypes
27 **********************************************************************/
28static void strcatUTF8( char *, char * );
29
30/***********************************************************************
31 * tr_metainfoParse
32 ***********************************************************************
33 *
34 **********************************************************************/
35int tr_metainfoParse( tr_info_t * inf, const char * path )
36{
37    FILE       * file;
38    char       * buf;
39    benc_val_t   meta, * beInfo, * list, * val;
40    char * s, * s2, * s3;
41    int          i;
42    struct stat sb;
43
44    snprintf( inf->torrent, MAX_PATH_LENGTH, path );
45
46    if( stat( path, &sb ) )
47    {
48        fprintf( stderr, "Could not stat file (%s)\n", path );
49        return 1;
50    }
51    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
52    {
53        fprintf( stderr, "Not a regular file (%s)\n", path );
54        return 1;
55    }
56    if( sb.st_size > 2097152 )
57    {
58        tr_err( "Torrent file is too big (%d bytes)", sb.st_size );
59        return 1;
60    }
61
62    /* Load the torrent file into our buffer */
63    file = fopen( path, "rb" );
64    if( !file )
65    {
66        fprintf( stderr, "Could not open file (%s)\n", path );
67        return 1;
68    }
69    buf = malloc( sb.st_size );
70    fseek( file, 0, SEEK_SET );
71    if( fread( buf, sb.st_size, 1, file ) != 1 )
72    {
73        fprintf( stderr, "Read error (%s)\n", path );
74        free( buf );
75        fclose( file );
76        return 1;
77    }
78    fclose( file );
79
80    /* Parse bencoded infos */
81    if( tr_bencLoad( buf, &meta, NULL ) )
82    {
83        fprintf( stderr, "Error while parsing bencoded data\n" );
84        free( buf );
85        return 1;
86    }
87
88    /* Get info hash */
89    if( !( beInfo = tr_bencDictFind( &meta, "info" ) ) )
90    {
91        fprintf( stderr, "Could not find \"info\" dictionary\n" );
92        tr_bencFree( &meta );
93        free( buf );
94        return 1;
95    }
96    SHA1( (uint8_t *) beInfo->begin,
97          (long) beInfo->end - (long) beInfo->begin, inf->hash );
98
99    /* No that we got the hash, we won't need this anymore */
100    free( buf );
101
102    if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
103    {
104        fprintf( stderr, "No \"announce\" entry\n" );
105        tr_bencFree( &meta );
106        return 1;
107    }
108   
109    /* Skip spaces */
110    s3 = val->val.s.s;
111    while( *s3 && *s3 == ' ' )
112    {
113        s3++;
114    }
115
116    /* Parse announce URL */
117    if( strncmp( s3, "http://", 7 ) )
118    {
119        fprintf( stderr, "Invalid announce URL (%s)\n",
120                 inf->trackerAddress );
121        tr_bencFree( &meta );
122        return 1;
123    }
124    s  = strchr( s3 + 7, ':' );
125    s2 = strchr( s3 + 7, '/' );
126    if( s && s < s2 )
127    {
128        memcpy( inf->trackerAddress, s3 + 7,
129                (long) s - (long) s3 - 7 );
130        inf->trackerPort = atoi( s + 1 );
131    }
132    else if( s2 )
133    {
134        memcpy( inf->trackerAddress, s3 + 7,
135                (long) s2 - (long) s3 - 7 );
136        inf->trackerPort = 80;
137    }
138    else
139    {
140        fprintf( stderr, "Invalid announce URL (%s)\n",
141                 inf->trackerAddress );
142        tr_bencFree( &meta );
143        return 1;
144    }
145    snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, s2 );
146
147    /* Piece length */
148    if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
149    {
150        fprintf( stderr, "No \"piece length\" entry\n" );
151        tr_bencFree( &meta );
152        return 1;
153    }
154    inf->pieceSize = val->val.i;
155
156    /* Hashes */
157    val = tr_bencDictFind( beInfo, "pieces" );
158    if( val->val.s.i % SHA_DIGEST_LENGTH )
159    {
160        fprintf( stderr, "Invalid \"piece\" string (size is %d)\n",
161                 val->val.s.i );
162        return 1;
163    }
164    inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH;
165    inf->pieces = (uint8_t *) val->val.s.s; /* Ugly, but avoids a memcpy */
166    val->val.s.s = NULL;
167
168    /* TODO add more tests so we don't crash on weird files */
169
170    inf->totalSize = 0;
171    if( ( list = tr_bencDictFind( beInfo, "files" ) ) )
172    {
173        /* Multi-file mode */
174        int j;
175
176        val = tr_bencDictFind( beInfo, "name" );
177        strcatUTF8( inf->name, val->val.s.s );
178
179        inf->fileCount = list->val.l.count;
180        inf->files     = calloc( inf->fileCount * sizeof( tr_file_t ), 1 );
181
182        for( i = 0; i < list->val.l.count; i++ )
183        {
184            val = tr_bencDictFind( &list->val.l.vals[i], "path" );
185            strcatUTF8( inf->files[i].name, inf->name );
186            for( j = 0; j < val->val.l.count; j++ )
187            {
188                strcatUTF8( inf->files[i].name, "/" );
189                strcatUTF8( inf->files[i].name,
190                            val->val.l.vals[j].val.s.s );
191            }
192            val = tr_bencDictFind( &list->val.l.vals[i], "length" );
193            inf->files[i].length  = val->val.i;
194            inf->totalSize       += val->val.i;
195        }
196
197    }
198    else
199    {
200        /* Single-file mode */
201        inf->fileCount = 1;
202        inf->files     = calloc( sizeof( tr_file_t ), 1 );
203
204        val = tr_bencDictFind( beInfo, "name" );
205        strcatUTF8( inf->files[0].name, val->val.s.s );
206        strcatUTF8( inf->name, val->val.s.s );
207       
208        val = tr_bencDictFind( beInfo, "length" );
209        inf->files[0].length  = val->val.i;
210        inf->totalSize       += val->val.i;
211    }
212
213    if( (uint64_t) inf->pieceCount !=
214        ( inf->totalSize + inf->pieceSize - 1 ) / inf->pieceSize )
215    {
216        fprintf( stderr, "Size of hashes and files don't match\n" );
217        tr_bencFree( &meta );
218        return 1;
219    }
220
221    tr_bencFree( &meta );
222    return 0;
223}
224
225/***********************************************************************
226 * strcatUTF8
227 ***********************************************************************
228 * According to the official specification, all strings in the torrent
229 * file are supposed to be UTF-8 encoded. However, there are
230 * non-compliant torrents around... If we encounter an invalid UTF-8
231 * character, we assume it is ISO 8859-1 and convert it to UTF-8.
232 **********************************************************************/
233static void strcatUTF8( char * s, char * append )
234{
235    char * p;
236
237    /* Go to the end of the destination string */
238    while( s[0] )
239    {
240        s++;
241    }
242
243    /* Now start appending, converting on the fly if necessary */
244    for( p = append; p[0]; )
245    {
246        if( !( p[0] & 0x80 ) )
247        {
248            /* ASCII character */
249            *(s++) = *(p++);
250            continue;
251        }
252
253        if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
254        {
255            /* 2-bytes UTF-8 character */
256            *(s++) = *(p++); *(s++) = *(p++);
257            continue;
258        }
259
260        if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
261            ( p[2] & 0xC0 ) == 0x80 )
262        {
263            /* 3-bytes UTF-8 character */
264            *(s++) = *(p++); *(s++) = *(p++);
265            *(s++) = *(p++);
266            continue;
267        }
268
269        if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
270            ( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
271        {
272            /* 4-bytes UTF-8 character */
273            *(s++) = *(p++); *(s++) = *(p++);
274            *(s++) = *(p++); *(s++) = *(p++);
275            continue;
276        }
277
278        /* ISO 8859-1 -> UTF-8 conversion */
279        *(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
280        *(s++) = 0x80 | ( *(p++) & 0x3F );
281    }
282}
Note: See TracBrowser for help on using the repository browser.