2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: magnet.c 11350 2010-10-24 05:52:38Z charles $
14 #include <string.h> /* strchr() */
16 #include "transmission.h"
26 /* this base32 code converted from code by Robert Kaye and Gordon Mohr
27 * and is public domain. see http://bitzi.com/publicdomain for more info */
29 static const int base32Lookup
[] =
31 0xFF,0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, /* '0', '1', '2', '3', '4', '5', '6', '7' */
32 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, /* '8', '9', ':', ';', '<', '=', '>', '?' */
33 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' */
34 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' */
35 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' */
36 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_' */
37 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g' */
38 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' */
39 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' */
40 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF /* 'x', 'y', 'z', '{', '|', '}', '~', 'DEL' */
43 static const int base32LookupLen
= sizeof( base32Lookup
) / sizeof( base32Lookup
[0] );
46 base32_to_sha1( uint8_t * out
, const char * in
, const int inlen
)
48 const int outlen
= 20;
53 assert( inlen
== 32 );
55 for( i
=0, index
=0, offset
=0; i
<inlen
; ++i
)
58 int lookup
= in
[i
] - '0';
60 /* Skip chars outside the lookup table */
61 if( lookup
<0 || lookup
>=base32LookupLen
)
64 /* If this digit is not in the table, ignore it */
65 digit
= base32Lookup
[lookup
];
70 index
= (index
+ 5) % 8;
74 if( offset
>= outlen
)
77 out
[offset
] |= digit
<< (8 - index
);
80 index
= (index
+ 5) % 8;
81 out
[offset
] |= (digit
>> index
);
84 if( offset
>= outlen
)
86 out
[offset
] |= digit
<< (8 - index
);
95 #define MAX_TRACKERS 64
96 #define MAX_WEBSEEDS 64
99 tr_magnetParse( const char * uri
)
101 tr_bool got_checksum
= FALSE
;
104 char * tr
[MAX_TRACKERS
];
105 char * ws
[MAX_WEBSEEDS
];
106 char * displayName
= NULL
;
107 uint8_t sha1
[SHA_DIGEST_LENGTH
];
108 tr_magnet_info
* info
= NULL
;
110 if( ( uri
!= NULL
) && !memcmp( uri
, "magnet:?", 8 ) )
114 for( walk
=uri
+8; walk
&& *walk
; )
116 const char * key
= walk
;
117 const char * delim
= strchr( key
, '=' );
118 const char * val
= delim
== NULL
? NULL
: delim
+ 1;
119 const char * next
= strchr( delim
== NULL
? key
: val
, '&' );
123 keylen
= delim
- key
;
124 else if( next
!= NULL
)
127 keylen
= strlen( key
);
131 else if( next
!= NULL
)
134 vallen
= strlen( val
);
136 if( ( keylen
==2 ) && !memcmp( key
, "xt", 2 ) && val
&& !memcmp( val
, "urn:btih:", 9 ) )
138 const char * hash
= val
+ 9;
139 const int hashlen
= vallen
- 9;
141 if( hashlen
== 40 ) {
142 tr_hex_to_sha1( sha1
, hash
);
145 else if( hashlen
== 32 ) {
146 base32_to_sha1( sha1
, hash
, hashlen
);
151 if( ( keylen
==2 ) && !memcmp( key
, "dn", 2 ) )
152 displayName
= tr_http_unescape( val
, vallen
);
154 if( trCount
< MAX_TRACKERS
) {
156 if( ( keylen
==2 ) && !memcmp( key
, "tr", 2 ) )
157 tr
[trCount
++] = tr_http_unescape( val
, vallen
);
158 else if( ( sscanf( key
, "tr.%d=", &i
) == 1 ) && ( i
> 0 ) ) /* ticket #3341 */
159 tr
[trCount
++] = tr_http_unescape( val
, vallen
);
162 if( ( keylen
==2 ) && !memcmp( key
, "ws", 2 ) && ( wsCount
< MAX_TRACKERS
) )
163 ws
[wsCount
++] = tr_http_unescape( val
, vallen
);
165 walk
= next
!= NULL
? next
+ 1 : NULL
;
171 info
= tr_new0( tr_magnet_info
, 1 );
172 info
->displayName
= displayName
;
173 info
->trackerCount
= trCount
;
174 info
->trackers
= tr_memdup( tr
, sizeof(char*) * trCount
);
175 info
->webseedCount
= wsCount
;
176 info
->webseeds
= tr_memdup( ws
, sizeof(char*) * wsCount
);
177 memcpy( info
->hash
, sha1
, sizeof(uint8_t) * SHA_DIGEST_LENGTH
);
184 tr_magnetFree( tr_magnet_info
* info
)
190 for( i
=0; i
<info
->trackerCount
; ++i
)
191 tr_free( info
->trackers
[i
] );
192 tr_free( info
->trackers
);
194 for( i
=0; i
<info
->webseedCount
; ++i
)
195 tr_free( info
->webseeds
[i
] );
196 tr_free( info
->webseeds
);
198 tr_free( info
->displayName
);
204 tr_magnetCreateMetainfo( const tr_magnet_info
* info
, tr_benc
* top
)
208 tr_bencInitDict( top
, 4 );
211 if( info
->trackerCount
== 1 )
212 tr_bencDictAddStr( top
, "announce", info
->trackers
[0] );
214 tr_benc
* trackers
= tr_bencDictAddList( top
, "announce-list", info
->trackerCount
);
215 for( i
=0; i
<info
->trackerCount
; ++i
)
216 tr_bencListAddStr( tr_bencListAddList( trackers
, 1 ), info
->trackers
[i
] );
220 if( info
->webseedCount
> 0 ) {
221 tr_benc
* urls
= tr_bencDictAddList( top
, "url-list", info
->webseedCount
);
222 for( i
=0; i
<info
->webseedCount
; ++i
)
223 tr_bencListAddStr( urls
, info
->webseeds
[i
] );
226 /* nonstandard keys */
227 d
= tr_bencDictAddDict( top
, "magnet-info", 2 );
228 tr_bencDictAddRaw( d
, "info_hash", info
->hash
, 20 );
229 if( info
->displayName
!= NULL
)
230 tr_bencDictAddStr( d
, "display-name", info
->displayName
);