1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-24 08:43:27 +00:00

(trunk libT) #2096: add code magnet URL parser and unit tests

This commit is contained in:
Charles Kerr 2009-11-20 04:38:19 +00:00
parent 4b563b7bde
commit 9383a6abfd
9 changed files with 367 additions and 6 deletions

View file

@ -27,6 +27,7 @@ libtransmission_a_SOURCES = \
json.c \
JSON_parser.c \
list.c \
magnet.c \
makemeta.c \
metainfo.c \
natpmp.c \
@ -73,6 +74,7 @@ noinst_HEADERS = \
json.h \
JSON_parser.h \
list.h \
magnet.h \
makemeta.h \
metainfo.h \
natpmp.h \
@ -108,6 +110,7 @@ TESTS = \
bencode-test \
clients-test \
json-test \
magnet-test \
peer-msgs-test \
rpc-test \
test-peer-id \
@ -146,6 +149,10 @@ json_test_SOURCES = json-test.c
json_test_LDADD = ${apps_ldadd}
json_test_LDFLAGS = ${apps_ldflags}
magnet_test_SOURCES = magnet-test.c
magnet_test_LDADD = ${apps_ldadd}
magnet_test_LDFLAGS = ${apps_ldflags}
rpc_test_SOURCES = rpc-test.c
rpc_test_LDADD = ${apps_ldadd}
rpc_test_LDFLAGS = ${apps_ldflags}

View file

@ -0,0 +1,92 @@
#include <stdio.h>
#include <string.h>
#include "transmission.h"
#include "magnet.h"
#include "utils.h"
/* #define VERBOSE */
#undef VERBOSE
static int test = 0;
#ifdef VERBOSE
#define check( A ) \
{ \
++test; \
if( A ){ \
fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
} else { \
fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
return test; \
} \
}
#else
#define check( A ) \
{ \
++test; \
if( !( A ) ){ \
fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
return test; \
} \
}
#endif
static int
test1( void )
{
int i;
const char * uri;
tr_magnet_info * info;
const int dec[] = { 210, 53, 64, 16, 163, 202, 74, 222, 91, 116,
39, 187, 9, 58, 98, 163, 137, 159, 243, 129 };
uri = "magnet:?xt=urn:btih:"
"d2354010a3ca4ade5b7427bb093a62a3899ff381"
"&dn=Display%20Name"
"&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce"
"&tr=http%3A%2F%2Ftracker.opentracker.org%2Fannounce";
info = tr_magnetParse( uri );
check( info != NULL )
check( info->announceCount == 2 );
check( !strcmp( info->announceURLs[0], "http://tracker.openbittorrent.com/announce" ) )
check( !strcmp( info->announceURLs[1], "http://tracker.opentracker.org/announce" ) )
check( !strcmp( info->displayName, "Display Name" ) )
for( i=0; i<20; ++i )
check( info->hash[i] == dec[i] );
tr_magnetFree( info );
info = NULL;
/* same thing but in base32 encoding */
uri = "magnet:?xt=urn:btih:"
"2I2UAEFDZJFN4W3UE65QSOTCUOEZ744B"
"&dn=Display%20Name"
"&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce"
"&tr=http%3A%2F%2Ftracker.opentracker.org%2Fannounce";
info = tr_magnetParse( uri );
check( info != NULL )
check( info->announceCount == 2 );
check( !strcmp( info->announceURLs[0], "http://tracker.openbittorrent.com/announce" ) )
check( !strcmp( info->announceURLs[1], "http://tracker.opentracker.org/announce" ) )
check( !strcmp( info->displayName, "Display Name" ) )
for( i=0; i<20; ++i )
check( info->hash[i] == dec[i] );
tr_magnetFree( info );
info = NULL;
return 0;
}
int
main( void )
{
int i;
if( ( i = test1( ) ) )
return i;
#ifdef VERBOSE
fprintf( stderr, "magnet-test passed\n" );
#endif
return 0;
}

184
libtransmission/magnet.c Normal file
View file

@ -0,0 +1,184 @@
/*
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#include <assert.h>
#include <ctype.h> /* tolower() */
#include <string.h> /* strchr() */
#include "transmission.h"
#include "magnet.h"
#include "utils.h"
#include "web.h"
/***
****
***/
/* this base32 code converted from code by Robert Kaye and Gordon Mohr
* and is public domain. see http://bitzi.com/publicdomain for more info */
static const int base32Lookup[] =
{
0xFF,0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, // '0', '1', '2', '3', '4', '5', '6', '7'
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // '8', '9', ':', ';', '<', '=', '>', '?'
0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_'
0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g'
0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL'
};
static const int base32LookupLen = sizeof( base32Lookup ) / sizeof( base32Lookup[0] );
static void
base32_to_sha1( uint8_t * out, const char * in, const int inlen )
{
const int outlen = 20;
int i, index, offset;
memset( out, 0, 20 );
assert( inlen == 32 );
for( i=0, index=0, offset=0; i<inlen; ++i )
{
int digit;
int lookup = in[i] - '0';
/* Skip chars outside the lookup table */
if( lookup<0 || lookup>=base32LookupLen )
continue;
/* If this digit is not in the table, ignore it */
digit = base32Lookup[lookup];
if( digit == 0xFF )
continue;
if( index <= 3 ) {
index = (index + 5) % 8;
if( index == 0 ) {
out[offset] |= digit;
offset++;
if( offset >= outlen )
break;
} else {
out[offset] |= digit << (8 - index);
}
} else {
index = (index + 5) % 8;
out[offset] |= (digit >> index);
offset++;
if( offset >= outlen )
break;
out[offset] |= digit << (8 - index);
}
}
}
/***
****
***/
#define MAX_TRACKERS 64
tr_magnet_info *
tr_magnetParse( const char * uri )
{
tr_bool got_checksum = FALSE;
int announceCount = 0;
char * announceURLs[MAX_TRACKERS];
char * displayName = NULL;
uint8_t sha1[SHA_DIGEST_LENGTH];
tr_magnet_info * info = NULL;
if( ( uri != NULL ) && !memcmp( uri, "magnet:?", 8 ) )
{
const char * walk;
for( walk=uri+8; walk && *walk; )
{
const char * key = walk;
const char * delim = strchr( key, '=' );
const char * val = delim == NULL ? NULL : delim + 1;
const char * next = strchr( delim == NULL ? key : val, '&' );
int keylen, vallen;
if( delim != NULL )
keylen = delim - key;
else if( next != NULL )
keylen = next - key;
else
keylen = strlen( key );
if( val == NULL )
vallen = 0;
else if( next != NULL )
vallen = next - val;
else
vallen = strlen( val );
if( ( keylen==2 ) && !memcmp( key, "xt", 2 ) && !memcmp( val, "urn:btih:", 9 ) )
{
const char * hash = val + 9;
const int hashlen = vallen - 9;
if( hashlen == 40 ) {
tr_hex_to_sha1( sha1, hash );
got_checksum = TRUE;
}
else if( hashlen == 32 ) {
base32_to_sha1( sha1, hash, hashlen );
got_checksum = TRUE;
}
}
if( ( keylen==2 ) && !memcmp( key, "dn", 2 ) )
displayName = tr_http_unescape( val, vallen );
if( ( keylen==2 ) && !memcmp( key, "tr", 2 ) )
announceURLs[announceCount++] = tr_http_unescape( val, vallen );
walk = next != NULL ? next + 1 : NULL;
}
}
if( got_checksum )
{
info = tr_new0( tr_magnet_info, 1 );
info->displayName = displayName;
info->announceCount = announceCount;
info->announceURLs = tr_memdup( announceURLs, sizeof(char*) * announceCount );
memcpy( info->hash, sha1, sizeof(uint8_t) * SHA_DIGEST_LENGTH );
}
return info;
}
void
tr_magnetFree( tr_magnet_info * info )
{
if( info != NULL )
{
int i;
for( i=0; i<info->announceCount; ++i )
tr_free( info->announceURLs[i] );
tr_free( info->announceURLs );
tr_free( info->displayName );
tr_free( info );
}
}

31
libtransmission/magnet.h Normal file
View file

@ -0,0 +1,31 @@
/*
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#ifndef TR_MAGNET_H
#define TR_MAGNET_H 1
#include "transmission.h"
typedef struct
{
uint8_t hash[20];
char * displayName;
char ** announceURLs;
int announceCount;
}
tr_magnet_info;
tr_magnet_info * tr_magnetParse( const char * uri );
void tr_magnetFree( tr_magnet_info * info );
#endif

View file

@ -8,6 +8,7 @@
#include "utils.h"
#include "crypto.h"
/* #define VERBOSE */
#undef VERBOSE
#define NUM_LOOPS 1
#define SPEED_TEST 0
@ -281,6 +282,22 @@ test_memmem( void )
return 0;
}
static int
test_hex( void )
{
char hex1[41];
char hex2[41];
uint8_t sha1[20];
/*uint8_t sha2[20];*/
memcpy( hex1, "fb5ef5507427b17e04b69cef31fa3379b456735a", 41 );
tr_hex_to_sha1( sha1, hex1 );
tr_sha1_to_hex( hex2, sha1 );
check( !strcmp( hex1, hex2 ) )
return 0;
}
int
main( void )
{
@ -308,6 +325,8 @@ main( void )
tr_free( in );
tr_free( out );
if( ( i = test_hex( ) ) )
return i;
if( ( i = test_lowerbound( ) ) )
return i;
if( ( i = test_strstrip( ) ) )

View file

@ -902,21 +902,35 @@ tr_getRatio( double numerator,
}
void
tr_sha1_to_hex( char * out,
const uint8_t * sha1 )
tr_sha1_to_hex( char * out, const uint8_t * sha1 )
{
int i;
static const char hex[] = "0123456789abcdef";
int i;
for( i = 0; i < 20; i++ )
for( i=0; i<20; ++i )
{
unsigned int val = *sha1++;
const unsigned int val = *sha1++;
*out++ = hex[val >> 4];
*out++ = hex[val & 0xf];
}
*out = '\0';
}
void
tr_hex_to_sha1( uint8_t * out, const char * in )
{
int i;
static const char hex[] = "0123456789abcdef";
for( i=0; i<20; ++i )
{
const int hi = strchr( hex, *in++ ) - hex;
const int lo = strchr( hex, *in++ ) - hex;
*out++ = (uint8_t)( (hi<<4) | lo );
}
}
/***
****
***/

View file

@ -377,9 +377,12 @@ void tr_set_compare( const void * a, size_t aCount,
tr_set_func in_both_cb,
void * userData );
void tr_sha1_to_hex( char * out,
void tr_sha1_to_hex( char * out,
const uint8_t * sha1 ) TR_GNUC_NONNULL(1,2);
void tr_hex_to_sha1( uint8_t * out,
const char * hex ) TR_GNUC_NONNULL(1,2);
tr_bool tr_httpIsValidURL( const char * url ) TR_GNUC_NONNULL(1);

View file

@ -684,3 +684,12 @@ tr_http_escape( struct evbuffer *out, const char *str, int len, int keep_slashes
}
}
}
char*
tr_http_unescape( const char * str, int len )
{
char * tmp = curl_unescape( str, len );
char * ret = tr_strdup( tmp );
curl_free( tmp );
return ret;
}

View file

@ -40,4 +40,6 @@ struct evbuffer;
void tr_http_escape( struct evbuffer *out, const char *str, int len, int noslashes );
char* tr_http_unescape( const char * str, int len );
#endif