#377: preliminary https support. this commit probably breaks mac and cli and is not for the faint of heart.

This commit is contained in:
Charles Kerr 2008-04-24 01:42:53 +00:00
parent 3a609ecc87
commit 7ef2511ca8
19 changed files with 747 additions and 744 deletions

View File

@ -1,5 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir) $(LIBEVENT_CPPFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(LIBCURL_CFLAGS) $(PTHREAD_CFLAGS)
bin_PROGRAMS = transmissioncli
@ -8,9 +8,13 @@ dist_man_MANS = transmissioncli.1
transmissioncli_SOURCES = transmissioncli.c
transmissioncli_LDADD = \
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(INTLLIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
$(top_builddir)/libtransmission/libtransmission.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(INTLLIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \
$(PTHREAD_LIBS) \
-lm

View File

@ -10,12 +10,14 @@ AC_CONFIG_SRCDIR(libtransmission/transmission.h)
AM_INIT_AUTOMAKE([1.9 tar-ustar])
AC_PROG_LIBTOOL
CURL_MINIMUM=7.16.0
GIO_MINIMUM=2.15.5
GLIB_MINIMUM=2.6.0
GTK_MINIMUM=2.6.0
WX_MINIMUM=2.6.0
LIBNOTIFY_MINIMUM=0.4.4
DBUS_GLIB_MINIMUM=0.70
AC_SUBST(CURL_MINIMUM)
AC_SUBST(GIO_MINIMUM)
AC_SUBST(GLIB_MINIMUM)
AC_SUBST(GTK_MINIMUM)
@ -41,6 +43,7 @@ ACX_PTHREAD
AC_SEARCH_LIBS([socket], [socket net])
AC_SEARCH_LIBS([gethostbyname], [nsl bind])
PKG_CHECK_MODULES(OPENSSL, [openssl >= 0.9.4])
PKG_CHECK_MODULES(LIBCURL, [libcurl >= 0.9.4])
AC_SYS_LARGEFILE

View File

@ -1,5 +1,5 @@
AM_CPPFLAGS = -I@top_srcdir@ $(LIBEVENT_CPPFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(LIBCURL_CFLAGS) $(PTHREAD_CFLAGS)
noinst_LIBRARIES = libdaemon.a
@ -32,7 +32,10 @@ COMMON_LDADD = \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(INTLLIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
$(INTLLIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \
$(PTHREAD_LIBS) -lm
transmission_daemon_SOURCES = daemon.c server.c torrents.c
transmission_daemon_LDADD = $(COMMON_LDADD)

View File

@ -9,6 +9,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = \
$(GTK_CFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBCURL_CFLAGS) \
$(PTHREAD_CFLAGS) \
$(GIO_CFLAGS) \
$(DBUS_GLIB_CFLAGS) \
@ -78,6 +79,7 @@ transmission_LDADD = \
$(LIBNOTIFY_LIBS) \
$(DBUS_GLIB_LIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \
$(PTHREAD_LIBS) -lm
DESKTOP_IN_FILES=transmission.desktop.in

View File

@ -1119,9 +1119,7 @@ tracker_page_new( TrTorrent * gtor )
GtkWidget * l;
int row = 0;
const char * s;
char * tmp;
struct tracker_page * page = g_new0( struct tracker_page, 1 );
const tr_tracker_info * track;
const tr_info * info = tr_torrent_info (gtor);
page->gtor = gtor;
@ -1147,14 +1145,9 @@ tracker_page_new( TrTorrent * gtor )
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Announce" ) );
track = info->trackerList->list;
tmp = track->port==80
? g_strdup_printf( "http://%s%s", track->address, track->announce )
: g_strdup_printf( "http://%s:%d%s", track->address, track->port, track->announce );
l = gtk_label_new( tmp );
l = gtk_label_new( info->trackers[0].announce );
gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
hig_workarea_add_row (t, &row, _( "Tracker:" ), l, NULL);
g_free( tmp );
s = _( "Last announce at:" );
l = gtk_label_new( NULL );

View File

@ -283,8 +283,8 @@ compareByTracker( GtkTreeModel * model,
const tr_torrent *ta, *tb;
gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
return strcmp( tr_torrentInfo(ta)->primaryAddress,
tr_torrentInfo(tb)->primaryAddress );
return strcmp( tr_torrentInfo(ta)->trackers[0].announce,
tr_torrentInfo(tb)->trackers[0].announce );
}
static void

View File

@ -274,7 +274,7 @@ checkFilterText( filter_text_mode_t filter_text_mode,
break;
case FILTER_TEXT_MODE_TRACKER:
pch = g_ascii_strdown( torInfo->primaryAddress, -1 );
pch = g_ascii_strdown( torInfo->trackers[0].announce, -1 );
ret = !text || ( strstr( pch, text ) != NULL );
g_free( pch );
break;

View File

@ -1,5 +1,5 @@
AM_CPPFLAGS = -I. -I$(top_srcdir) -I$(top_srcdir)/third-party/ -D__TRANSMISSION__ $(LIBEVENT_CPPFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
AM_CFLAGS = $(OPENSSL_CFLAGS) $(LIBCURL_CFLAGS) $(PTHREAD_CFLAGS)
noinst_LIBRARIES = libtransmission.a
@ -37,7 +37,8 @@ libtransmission_a_SOURCES = \
trevent.c \
upnp.c \
utils.c \
verify.c
verify.c \
web.c
noinst_HEADERS = \
bencode.h \
@ -75,7 +76,8 @@ noinst_HEADERS = \
trevent.h \
upnp.h \
utils.h \
verify.h
verify.h \
web.h
bin_PROGRAMS = benc2php
@ -92,7 +94,11 @@ APPS_LDADD = \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(INTLLIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
$(INTLLIBS) \
$(OPENSSL_LIBS) \
$(LIBCURL_LIBS) \
$(PTHREAD_LIBS) \
-lm
benc2php_SOURCES = benc2php.c
benc2php_LDADD = $(APPS_LDADD)

View File

@ -531,8 +531,6 @@ static void
filltracker( tr_benc * val, const tr_tracker_info * tk )
{
tr_bencInitDict( val, 4 );
tr_bencDictAddStr( val, "address", tk->address );
tr_bencDictAddInt( val, "port", tk->port );
tr_bencDictAddStr( val, "announce", tk->announce );
if( tk->scrape )
tr_bencDictAddStr( val, "scrape", tk->scrape );
@ -554,7 +552,7 @@ ipc_addinfo( tr_benc * list,
int types )
{
tr_benc * dict;
int ii, jj, kk;
int ii, jj;
tr_file_index_t ff;
const tr_info * inf = tr_torrentInfo( tor );
@ -628,17 +626,19 @@ ipc_addinfo( tr_benc * list,
case IPC_INF_SIZE:
tr_bencInitInt( item, inf->totalSize );
break;
case IPC_INF_TRACKERS:
tr_bencInitList( item, inf->trackerTiers );
for( jj = 0; inf->trackerTiers > jj; jj++ )
{
tr_benc * tier = tr_bencListAdd( item );
tr_bencInitList( tier, inf->trackerList[jj].count );
for( kk = 0; inf->trackerList[jj].count > kk; kk++ )
filltracker( tr_bencListAdd( tier ),
&inf->trackerList[jj].list[kk] );
case IPC_INF_TRACKERS: {
int prevTier = -1;
tr_benc * tier = NULL;
tr_bencInitList( item, 0 );
for( jj=0; jj<inf->trackerCount; ++jj ) {
if( prevTier != inf->trackers[jj].tier ) {
prevTier = inf->trackers[jj].tier;
tier = tr_bencListAddList( item, 0 );
}
filltracker( tr_bencListAdd( tier ), &inf->trackers[jj] );
}
break;
}
default:
assert( 0 );
break;

View File

@ -32,6 +32,8 @@
#include <sys/stat.h>
#include <unistd.h> /* unlink, stat */
#include <event.h> /* struct evbuffer */
#include "transmission.h"
#include "bencode.h"
#include "crypto.h" /* tr_sha1 */
@ -330,7 +332,7 @@ tr_metainfoParse( const tr_handle * handle,
void tr_metainfoFree( tr_info * inf )
{
tr_file_index_t ff;
int i, j;
int i;
for( ff=0; ff<inf->fileCount; ++ff )
tr_free( inf->files[ff].name );
@ -341,14 +343,12 @@ void tr_metainfoFree( tr_info * inf )
tr_free( inf->creator );
tr_free( inf->torrent );
tr_free( inf->name );
tr_free( inf->primaryAddress );
for( i=0; i<inf->trackerTiers; ++i ) {
for( j=0; j<inf->trackerList[i].count; ++j )
tr_trackerInfoClear( &inf->trackerList[i].list[j] );
tr_free( inf->trackerList[i].list );
for( i=0; i<inf->trackerCount; ++i ) {
tr_free( inf->trackers[i].announce );
tr_free( inf->trackers[i].scrape );
}
tr_free( inf->trackerList );
tr_free( inf->trackers );
memset( inf, '\0', sizeof(tr_info) );
}
@ -411,208 +411,95 @@ getfile( char ** setme, const char * prefix, tr_benc * name )
static int getannounce( tr_info * inf, tr_benc * meta )
{
tr_benc * val, * urlval;
char * address, * announce;
int ii, jj, port, random;
tr_tracker_info * sublist;
void * swapping;
const char * str;
tr_tracker_info * trackers = NULL;
int trackerCount = 0;
tr_benc * tiers;
/* Announce-list */
val = tr_bencDictFind( meta, "announce-list" );
if( tr_bencIsList(val) && 0 < val->val.l.count )
if( tr_bencDictFindList( meta, "announce-list", &tiers ) )
{
inf->trackerTiers = 0;
inf->trackerList = calloc( val->val.l.count,
sizeof( inf->trackerList[0] ) );
int n;
int i, j;
/* iterate through the announce-list's tiers */
for( ii = 0; ii < val->val.l.count; ii++ )
{
int subcount = 0;
tr_benc * subval = &val->val.l.vals[ii];
n = 0;
for( i=0; i<tiers->val.l.count; ++i )
n += tiers->val.l.vals[i].val.l.count;
if( !tr_bencIsList(subval) || 0 >= subval->val.l.count )
continue;
trackers = tr_new0( tr_tracker_info, n );
trackerCount = 0;
sublist = calloc( subval->val.l.count, sizeof( sublist[0] ) );
/* iterate through the tier's items */
for( jj = 0; jj < subval->val.l.count; jj++ )
{
tr_tracker_info tmp;
urlval = &subval->val.l.vals[jj];
if( TYPE_STR != urlval->type ||
tr_trackerInfoInit( &tmp, urlval->val.s.s, urlval->val.s.i ) )
{
continue;
for( i=0; i<tiers->val.l.count; ++i ) {
const tr_benc * tier = &tiers->val.l.vals[i];
for( j=0; tr_bencIsList(tier) && j<tier->val.l.count; ++j ) {
const tr_benc * address = &tier->val.l.vals[j];
if( tr_bencIsString( address ) && tr_httpIsValidURL( address->val.s.s ) ) {
trackers[trackerCount].tier = i;
trackers[trackerCount].announce = tr_strndup( address->val.s.s, address->val.s.i );
trackers[trackerCount++].scrape = announceToScrape( address->val.s.s );
/*fprintf( stderr, "tier %d: %s\n", i, address->val.s.s );*/
}
if( !inf->primaryAddress ) {
char buf[1024];
snprintf( buf, sizeof(buf), "%s:%d", tmp.address, tmp.port );
inf->primaryAddress = tr_strdup( buf );
}
/* place the item info in a random location in the sublist */
random = tr_rand( subcount + 1 );
if( random != subcount )
sublist[subcount] = sublist[random];
sublist[random] = tmp;
subcount++;
}
/* just use sublist as-is if it's full */
if( subcount == subval->val.l.count )
{
inf->trackerList[inf->trackerTiers].list = sublist;
inf->trackerList[inf->trackerTiers].count = subcount;
inf->trackerTiers++;
}
/* if we skipped some of the tier's items then trim the sublist */
else if( 0 < subcount )
{
inf->trackerList[inf->trackerTiers].list = calloc( subcount, sizeof( sublist[0] ) );
memcpy( inf->trackerList[inf->trackerTiers].list, sublist,
sizeof( sublist[0] ) * subcount );
inf->trackerList[inf->trackerTiers].count = subcount;
inf->trackerTiers++;
free( sublist );
}
/* drop the whole sublist if we didn't use any items at all */
else
{
free( sublist );
}
}
/* did we use any of the tiers? */
if( 0 == inf->trackerTiers )
{
if( !trackerCount ) {
tr_inf( _( "Invalid metadata entry \"%s\"" ), "announce-list" );
free( inf->trackerList );
inf->trackerList = NULL;
}
/* trim unused sublist pointers */
else if( inf->trackerTiers < val->val.l.count )
{
swapping = inf->trackerList;
inf->trackerList = calloc( inf->trackerTiers,
sizeof( inf->trackerList[0] ) );
memcpy( inf->trackerList, swapping,
sizeof( inf->trackerList[0] ) * inf->trackerTiers );
free( swapping );
tr_free( trackers );
trackers = NULL;
}
}
/* Regular announce value */
val = tr_bencDictFind( meta, "announce" );
if( !tr_bencIsString( val ) )
if( !trackerCount
&& tr_bencDictFindStr( meta, "announce", &str )
&& tr_httpIsValidURL( str ) )
{
tr_err( _( "Missing metadata entry \"%s\"" ), "announce" );
return TR_EINVALID;
}
if( !inf->trackerTiers )
{
char buf[4096], *pch;
strlcpy( buf, val->val.s.s, sizeof( buf ) );
pch = buf;
while( isspace( *pch ) )
++pch;
if( tr_httpParseURL( pch, -1, &address, &port, &announce ) )
{
tr_err( _( "Invalid announce URL \"%s\"" ), val->val.s.s );
return TR_EINVALID;
}
sublist = calloc( 1, sizeof( sublist[0] ) );
sublist[0].address = address;
sublist[0].port = port;
sublist[0].announce = announce;
sublist[0].scrape = announceToScrape( announce );
inf->trackerList = calloc( 1, sizeof( inf->trackerList[0] ) );
inf->trackerList[0].list = sublist;
inf->trackerList[0].count = 1;
inf->trackerTiers = 1;
if( !inf->primaryAddress ) {
char buf[1024];
snprintf( buf, sizeof(buf), "%s:%d", sublist[0].address, sublist[0].port );
inf->primaryAddress = tr_strdup( buf );
}
trackers = tr_new0( tr_tracker_info, 1 );
trackers[trackerCount].tier = 0;
trackers[trackerCount].announce = tr_strdup( str );
trackers[trackerCount++].scrape = announceToScrape( str );
/*fprintf( stderr, "single announce: [%s]\n", str );*/
}
inf->trackers = trackers;
inf->trackerCount = trackerCount;
return TR_OK;
}
static char * announceToScrape( const char * announce )
static char *
announceToScrape( const char * announce )
{
char old[] = "announce";
int oldlen = 8;
char new[] = "scrape";
int newlen = 6;
char * slash, * scrape;
size_t scrapelen, used;
char * scrape = NULL;
const char * slash;
struct evbuffer * buf;
/* To derive the scrape URL use the following steps:
* Begin with the announce URL. Find the last '/' in it.
* If the text immediately following that '/' isn't 'announce'
* it will be taken as a sign that that tracker doesn't support
* the scrape convention. If it does, substitute 'scrape' for
* 'announce' to find the scrape page. */
/* is the last slash followed by "announce"? */
slash = strrchr( announce, '/' );
if( NULL == slash )
{
if( !slash )
return NULL;
}
slash++;
if( 0 != strncmp( slash, old, oldlen ) )
{
++slash;
if( strncmp( slash, "announce", 8 ) )
return NULL;
}
scrapelen = strlen( announce ) - oldlen + newlen;
scrape = calloc( scrapelen + 1, 1 );
if( NULL == scrape )
{
return NULL;
}
assert( ( size_t )( slash - announce ) < scrapelen );
memcpy( scrape, announce, slash - announce );
used = slash - announce;
strncat( scrape, new, scrapelen - used );
used += newlen;
assert( strlen( scrape ) == used );
if( used < scrapelen )
{
assert( strlen( slash + oldlen ) == scrapelen - used );
strncat( scrape, slash + oldlen, scrapelen - used );
}
/* build the scrape url */
buf = evbuffer_new( );
evbuffer_add( buf, announce, slash-announce );
evbuffer_add( buf, "scrape", 6 );
evbuffer_add_printf( buf, "%s", slash+8 );
scrape = tr_strdup( ( char * ) EVBUFFER_DATA( buf ) );
evbuffer_free( buf );
return scrape;
}
int
tr_trackerInfoInit( tr_tracker_info * info,
const char * address,
int address_len )
{
int ret = tr_httpParseURL( address, address_len,
&info->address,
&info->port,
&info->announce );
if( !ret )
info->scrape = announceToScrape( info->announce );
return ret;
}
void
tr_trackerInfoClear( tr_tracker_info * info )
{
tr_free( info->address );
tr_free( info->announce );
tr_free( info->scrape );
memset( info, '\0', sizeof(tr_tracker_info) );
}
void
tr_metainfoRemoveSaved( const tr_handle * handle,
const tr_info * inf )

View File

@ -47,6 +47,7 @@
#include "tracker.h"
#include "trevent.h"
#include "utils.h"
#include "web.h"
/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
characters, where x is the major version number, y is the
@ -92,22 +93,22 @@ tr_getPeerId( void )
***/
tr_encryption_mode
tr_getEncryptionMode( tr_handle * handle )
tr_getEncryptionMode( tr_session * session )
{
assert( handle != NULL );
assert( session != NULL );
return handle->encryptionMode;
return session->encryptionMode;
}
void
tr_setEncryptionMode( tr_handle * handle, tr_encryption_mode mode )
tr_setEncryptionMode( tr_session * session, tr_encryption_mode mode )
{
assert( handle != NULL );
assert( session != NULL );
assert( mode==TR_ENCRYPTION_PREFERRED
|| mode==TR_ENCRYPTION_REQUIRED
|| mode==TR_PLAINTEXT_PREFERRED );
handle->encryptionMode = mode;
session->encryptionMode = mode;
}
/***
@ -191,6 +192,8 @@ tr_initFull( const char * configDir,
tr_statsInit( h );
h->web = tr_webInit( h );
metainfoLookupRescan( h );
return h;

View File

@ -43,12 +43,6 @@ typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t;
#define FALSE 0
#endif
int tr_trackerInfoInit( struct tr_tracker_info * info,
const char * address,
int address_len );
void tr_trackerInfoClear( struct tr_tracker_info * info );
uint8_t* tr_peerIdNew( void );
const uint8_t* tr_getPeerId( void );
@ -59,13 +53,6 @@ struct tr_metainfo_lookup
char * filename;
};
const char * tr_sessionFindTorrentFile( const tr_handle * h,
const char * hashString );
void tr_sessionSetTorrentFile( tr_handle * h,
const char * hashString,
const char * filename );
struct tr_handle
{
unsigned int isPortSet : 1;
@ -98,6 +85,8 @@ struct tr_handle
struct tr_lock * lock;
struct tr_web * web;
tr_handle_status stats[2];
int statCur;
@ -108,8 +97,17 @@ struct tr_handle
int metainfoLookupCount;
};
void tr_globalLock ( struct tr_handle * );
void tr_globalUnlock ( struct tr_handle * );
int tr_globalIsLocked ( const struct tr_handle * );
typedef struct tr_handle tr_session;
const char * tr_sessionFindTorrentFile( const tr_session * session,
const char * hashString );
void tr_sessionSetTorrentFile( tr_session * session,
const char * hashString,
const char * filename );
void tr_globalLock ( tr_session * );
void tr_globalUnlock ( tr_session * );
int tr_globalIsLocked ( const tr_session * );
#endif

File diff suppressed because it is too large Load Diff

View File

@ -702,8 +702,7 @@ tr_piece;
typedef struct tr_tracker_info
{
char * address;
int port;
int tier;
char * announce;
char * scrape;
}
@ -723,14 +722,9 @@ struct tr_info
unsigned int isPrivate : 1;
unsigned int isMultifile : 1;
/* Tracker info */
struct
{
tr_tracker_info * list;
int count;
} * trackerList;
int trackerTiers;
char * primaryAddress;
/* these trackers are sorted by tier */
tr_tracker_info * trackers;
int trackerCount;
/* Torrent info */
char * comment;

View File

@ -190,7 +190,7 @@ tr_eventInit( tr_handle * handle )
eh = tr_new0( tr_event_handle, 1 );
eh->lock = tr_lockNew( );
eh->h = handle;
eh->pulseInterval = timevalMsec( 100 );
eh->pulseInterval = tr_timevalMsec( 100 );
eh->thread = tr_threadNew( libeventThreadFunc, eh, "libeventThreadFunc" );
}
@ -289,7 +289,7 @@ tr_timerNew( struct tr_handle * handle,
uint64_t timeout_milliseconds )
{
tr_timer * timer = tr_new0( tr_timer, 1 );
timer->tv = timevalMsec( timeout_milliseconds );
timer->tv = tr_timevalMsec( timeout_milliseconds );
timer->func = func;
timer->user_data = user_data;
timer->eh = handle->events;

View File

@ -43,8 +43,6 @@
#include <kernel/OS.h>
#endif
#include <miniupnp/miniwget.h> /* parseURL */
#include "transmission.h"
#include "trcompat.h"
#include "utils.h"
@ -352,7 +350,7 @@ tr_compareUint32( uint32_t a, uint32_t b )
**/
struct timeval
timevalMsec( uint64_t milliseconds )
tr_timevalMsec( uint64_t milliseconds )
{
struct timeval ret;
const uint64_t microseconds = milliseconds * 1000;
@ -954,28 +952,67 @@ tr_sha1_to_hex( char * out, const uint8_t * sha1 )
****
***/
int
tr_httpIsValidURL( const char * url )
{
return !tr_httpParseURL( url, -1, NULL, NULL, NULL );
}
int
tr_httpParseURL( const char * url_in, int len,
char ** setme_host,
int * setme_port,
char ** setme_path )
{
char * url = tr_strndup( url_in, len );
char * path;
char host[4096+1];
unsigned short port;
int success;
int err;
int port = 0;
int n;
char * tmp;
char * pch;
const char * protocol = NULL;
const char * host = NULL;
const char * path = NULL;
success = parseURL( url, host, &port, &path );
if( success ) {
if( setme_host ) *setme_host = tr_strdup( host );
if( setme_port ) *setme_port = port;
if( setme_path ) *setme_path = tr_strdup( path );
tmp = tr_strndup( url_in, len );
if(( pch = strstr( tmp, "://" )))
{
*pch = '\0';
protocol = tmp;
pch += 3;
/*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch );*/
if(( n = strcspn( pch, ":/" )))
{
const int havePort = pch[n] == ':';
host = pch;
pch += n;
*pch++ = '\0';
/*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
if( havePort )
{
char * end;
port = strtol( pch, &end, 10 );
pch = end;
/*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
}
path = pch;
/*fprintf( stderr, "path is [%s]\n", path );*/
}
}
tr_free( url );
err = !host || !path || !protocol || ( strcmp(protocol,"http") && strcmp(protocol,"https") );
return !success;
if( !err && !port ) {
if( !strcmp(protocol,"http") ) port = 80;
if( !strcmp(protocol,"https") ) port = 443;
}
if( !err ) {
if( setme_host) { ((char*)host)[-3]=':'; *setme_host = tr_strdup( protocol ); }
if( setme_path) { ((char*)path)[-1]='/'; *setme_path = tr_strdup( path-1 ); }
if( setme_port) *setme_port = port;
}
tr_free( tmp );
return err;
}

View File

@ -92,7 +92,7 @@ int tr_asprintf( char **strp, const char *fmt, ...);
void tr_buildPath( char* buf, size_t buflen,
const char * first_element, ... );
struct timeval timevalMsec( uint64_t milliseconds );
struct timeval tr_timevalMsec( uint64_t milliseconds );
int tr_ioErrorFromErrno( int err );
@ -160,6 +160,8 @@ int tr_compareUint32( uint32_t a, uint32_t b );
void tr_sha1_to_hex( char * out, const uint8_t * sha1 );
int tr_httpIsValidURL( const char * url );
int tr_httpParseURL( const char * url,
int url_len,
char ** setme_host,

256
libtransmission/web.c Normal file
View File

@ -0,0 +1,256 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.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 <event.h>
#include <curl/curl.h>
#include "transmission.h"
#include "utils.h"
#include "web.h"
struct tr_web
{
CURLM * cm;
tr_session * session;
int remain;
struct event timeout;
};
struct tr_web_task
{
unsigned int tag;
struct evbuffer * response;
tr_web_done_func * done_func;
void * done_func_user_data;
};
static size_t
writeFunc( void * ptr, size_t size, size_t nmemb, void * vtask )
{
const size_t byteCount = size * nmemb;
struct tr_web_task * task = vtask;
evbuffer_add( task->response, ptr, byteCount );
return byteCount;
}
void
tr_webRun( tr_session * session,
const char * url,
tr_web_done_func * done_func,
void * done_func_user_data )
{
static unsigned int tag = 0;
struct tr_web_task * task;
struct tr_web * web = session->web;
CURL * ch;
CURLMcode rc;
task = tr_new0( struct tr_web_task, 1 );
task->done_func = done_func;
task->done_func_user_data = done_func_user_data;
task->tag = ++tag;
task->response = evbuffer_new( );
fprintf( stderr, "new web tag %u [%s]\n", task->tag, url );
++web->remain;
ch = curl_easy_init( );
curl_easy_setopt( ch, CURLOPT_PRIVATE, task );
curl_easy_setopt( ch, CURLOPT_URL, url );
curl_easy_setopt( ch, CURLOPT_WRITEFUNCTION, writeFunc );
curl_easy_setopt( ch, CURLOPT_WRITEDATA, task );
curl_easy_setopt( ch, CURLOPT_USERAGENT, TR_NAME "/" LONG_VERSION_STRING );
curl_multi_add_handle( web->cm, ch );
do {
int tmp;
rc = curl_multi_socket_all( web->cm, &tmp );
} while( rc == CURLM_CALL_MULTI_PERFORM );
}
static void
responseHandler( tr_web * web )
{
int remaining = 0;
do {
CURLMsg * msg = curl_multi_info_read( web->cm, &remaining );
if( msg && ( msg->msg == CURLMSG_DONE ) )
{
CURL * ch;
struct tr_web_task * task;
long response_code;
if( msg->data.result != CURLE_OK )
tr_err( "%s", curl_easy_strerror( msg->data.result ) );
ch = msg->easy_handle;
curl_easy_getinfo( ch, CURLINFO_PRIVATE, &task );
curl_easy_getinfo( ch, CURLINFO_RESPONSE_CODE, &response_code );
fprintf( stderr, "web task %u done\n", task->tag );
task->done_func( web->session,
response_code,
EVBUFFER_DATA(task->response),
EVBUFFER_LENGTH(task->response),
task->done_func_user_data );
curl_multi_remove_handle( web->cm, ch );
curl_easy_cleanup( ch );
evbuffer_free( task->response );
tr_free( task );
}
}
while( remaining );
}
/* libevent says that sock is ready to be processed, so wake up libcurl */
static void
event_callback( int sock, short action, void * vweb )
{
tr_web * web = vweb;
CURLMcode rc;
int mask;
#if 0
static const char *strings[] = {
"NONE","TIMEOUT","READ","TIMEOUT|READ","WRITE","TIMEOUT|WRITE",
"READ|WRITE","TIMEOUT|READ|WRITE","SIGNAL" };
fprintf( stderr, "Event on socket %d (%s)\n", sock, strings[action] );
#endif
switch (action & (EV_READ|EV_WRITE)) {
case EV_READ: mask = CURL_CSELECT_IN; break;
case EV_WRITE: mask = CURL_CSELECT_OUT; break;
case EV_READ|EV_WRITE: mask = CURL_CSELECT_IN|CURL_CSELECT_OUT; break;
default: tr_err( "Unknown event %d\n", (int)action ); return;
}
do
rc = curl_multi_socket_action( web->cm, sock, mask, &web->remain );
while( rc == CURLM_CALL_MULTI_PERFORM );
if ( rc != CURLM_OK )
tr_err( "%s (%d)", curl_multi_strerror(rc), (int)sock );
responseHandler( web );
/* remove timeout if there are no transfers left */
if( !web->remain
&& event_initialized( &web->timeout )
&& event_pending( &web->timeout, EV_TIMEOUT, NULL ) ) {
event_del( &web->timeout );
fprintf( stderr, "Removed timeout\n" );
}
}
/* libcurl wants us to tell it when sock is ready to be processed */
static int
socket_callback( CURL * easy UNUSED,
curl_socket_t sock,
int action,
void * vweb,
void * assigndata )
{
tr_web * web = vweb;
int events = EV_PERSIST;
struct event * ev = assigndata;
if( ev )
event_del( ev );
else {
ev = tr_new0( struct event, 1 );
curl_multi_assign( web->cm, sock, ev );
}
#if 0
{
static const char *actions[] = {"NONE", "IN", "OUT", "INOUT", "REMOVE"};
fprintf( stderr, "Callback on socket %d (%s)\n", (int)sock, actions[action]);
}
#endif
switch (action) {
case CURL_POLL_IN: events |= EV_READ; break;
case CURL_POLL_OUT: events |= EV_WRITE; break;
case CURL_POLL_INOUT: events |= EV_READ|EV_WRITE; break;
case CURL_POLL_REMOVE: tr_free( ev ); /* fallthrough */
case CURL_POLL_NONE: return 0;
default: tr_err( "Unknown socket action %d", action ); return -1;
}
event_set( ev, sock, events, event_callback, web );
event_add( ev, NULL );
return 0;
}
/* libevent says that timeout_ms have passed, so wake up libcurl */
static void
timeout_callback( int socket UNUSED, short action UNUSED, void * vweb )
{
CURLMcode rc;
tr_web * web = vweb;
do
rc = curl_multi_socket( web->cm, CURL_SOCKET_TIMEOUT, &web->remain );
while( rc == CURLM_CALL_MULTI_PERFORM );
if( rc != CURLM_OK )
tr_err( "%s", curl_multi_strerror( rc ) );
}
/* libcurl wants us to tell it when timeout_ms have passed */
static void
timer_callback( CURLM *multi UNUSED, long timeout_ms, void * vweb )
{
tr_web * web = vweb;
struct timeval tv = tr_timevalMsec( timeout_ms );
if( event_initialized( &web->timeout )
&& event_pending( &web->timeout, EV_TIMEOUT, NULL ) )
event_del( &web->timeout );
event_set( &web->timeout, -1, 0, timeout_callback, vweb );
event_add( &web->timeout, &tv );
}
tr_web*
tr_webInit( tr_session * session )
{
static int curlInited = FALSE;
tr_web * web;
/* call curl_global_init if we haven't done it already.
* try to enable ssl for https support; but if that fails,
* try a plain vanilla init */
if( curlInited == FALSE ) {
curlInited = TRUE;
if( curl_global_init( CURL_GLOBAL_SSL ) )
curl_global_init( 0 );
}
web = tr_new0( struct tr_web, 1 );
web->cm = curl_multi_init( );
web->session = session;
web->remain = 0;
curl_multi_setopt( web->cm, CURLMOPT_SOCKETDATA, web );
curl_multi_setopt( web->cm, CURLMOPT_SOCKETFUNCTION, socket_callback );
curl_multi_setopt( web->cm, CURLMOPT_TIMERDATA, web );
curl_multi_setopt( web->cm, CURLMOPT_TIMERFUNCTION, timer_callback );
curl_multi_setopt( web->cm, CURLMOPT_MAXCONNECTS, 20 );
curl_multi_setopt( web->cm, CURLMOPT_PIPELINING, 1 );
return web;
}

34
libtransmission/web.h Normal file
View File

@ -0,0 +1,34 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.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_HTTP_H
#define TR_HTTP_H
struct tr_handle;
typedef struct tr_web tr_web;
tr_web* tr_webInit( tr_handle * session );
typedef void (tr_web_done_func)( tr_handle * session,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data );
void tr_webRun( tr_handle * session,
const char * url,
tr_web_done_func done_func,
void * done_func_user_data );
#endif