From 7ef2511ca802c2826df4a414e021d14f3d8b2b6e Mon Sep 17 00:00:00 2001 From: Charles Kerr <charles@transmissionbt.com> Date: Thu, 24 Apr 2008 01:42:53 +0000 Subject: [PATCH] #377: preliminary https support. this commit probably breaks mac and cli and is not for the faint of heart. --- cli/Makefile.am | 16 +- configure.ac | 3 + daemon/Makefile.am | 7 +- gtk/Makefile.am | 2 + gtk/details.c | 9 +- gtk/tr-core.c | 4 +- gtk/tr-window.c | 2 +- libtransmission/Makefile.am | 14 +- libtransmission/ipcparse.c | 24 +- libtransmission/metainfo.c | 249 +++-------- libtransmission/session.c | 15 +- libtransmission/session.h | 30 +- libtransmission/tracker.c | 737 ++++++++++++--------------------- libtransmission/transmission.h | 14 +- libtransmission/trevent.c | 4 +- libtransmission/utils.c | 69 ++- libtransmission/utils.h | 4 +- libtransmission/web.c | 256 ++++++++++++ libtransmission/web.h | 34 ++ 19 files changed, 748 insertions(+), 745 deletions(-) create mode 100644 libtransmission/web.c create mode 100644 libtransmission/web.h diff --git a/cli/Makefile.am b/cli/Makefile.am index c8a6ddbdf..96ecb9fb7 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index f757500df..e74881282 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/daemon/Makefile.am b/daemon/Makefile.am index bbe720ae4..bfd06ede1 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -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) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index f6b7f1782..a42816b2f 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -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 diff --git a/gtk/details.c b/gtk/details.c index eb5d68253..9ead17b40 100644 --- a/gtk/details.c +++ b/gtk/details.c @@ -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 ); diff --git a/gtk/tr-core.c b/gtk/tr-core.c index 7e2a0aca0..aeae38123 100644 --- a/gtk/tr-core.c +++ b/gtk/tr-core.c @@ -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 diff --git a/gtk/tr-window.c b/gtk/tr-window.c index b02102205..26b0866e8 100644 --- a/gtk/tr-window.c +++ b/gtk/tr-window.c @@ -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; diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index d38216db8..09e762ad2 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -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) diff --git a/libtransmission/ipcparse.c b/libtransmission/ipcparse.c index a7e711ede..aac3e8b4f 100644 --- a/libtransmission/ipcparse.c +++ b/libtransmission/ipcparse.c @@ -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; diff --git a/libtransmission/metainfo.c b/libtransmission/metainfo.c index 3a7c1912b..7f34bc76d 100644 --- a/libtransmission/metainfo.c +++ b/libtransmission/metainfo.c @@ -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] ) ); - - /* 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]; - - if( !tr_bencIsList(subval) || 0 >= subval->val.l.count ) - continue; - - 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; - } - - if( !inf->primaryAddress ) { - char buf[1024]; - snprintf( buf, sizeof(buf), "%s:%d", tmp.address, tmp.port ); - inf->primaryAddress = tr_strdup( buf ); + int n; + int i, j; + + n = 0; + for( i=0; i<tiers->val.l.count; ++i ) + n += tiers->val.l.vals[i].val.l.count; + + trackers = tr_new0( tr_tracker_info, n ); + trackerCount = 0; + + 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 );*/ } - - /* 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 ) diff --git a/libtransmission/session.c b/libtransmission/session.c index 4de4c30e8..0131eafaf 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -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; diff --git a/libtransmission/session.h b/libtransmission/session.h index 8fc57f828..c053b5962 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -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 diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c index e7d7ed8df..f31a9b70a 100644 --- a/libtransmission/tracker.c +++ b/libtransmission/tracker.c @@ -17,12 +17,10 @@ #include <libgen.h> /* basename */ #include <event.h> -#include <evhttp.h> #include "transmission.h" #include "bencode.h" #include "completion.h" -#include "list.h" #include "net.h" #include "port-forwarding.h" #include "publish.h" @@ -31,9 +29,12 @@ #include "trcompat.h" /* strlcpy */ #include "trevent.h" #include "utils.h" +#include "web.h" enum { + HTTP_OK = 200, + /* seconds between tracker pulses */ PULSE_INTERVAL_MSEC = 1000, @@ -62,7 +63,7 @@ enum STOP_TIMEOUT_INTERVAL_SEC = 5, /* the value of the 'numwant' argument passed in tracker requests. */ - NUMWANT = 200, + NUMWANT = 150, /* the length of the 'key' argument passed in tracker requests */ KEYLEN = 10 @@ -74,7 +75,11 @@ enum struct tr_tracker { - tr_handle * handle; + unsigned int isRunning : 1; + + uint8_t randOffset; + + tr_session * session; /* these are set from the latest scrape or tracker response */ int announceIntervalSec; @@ -82,11 +87,8 @@ struct tr_tracker int scrapeIntervalSec; int retryScrapeIntervalSec; - tr_tracker_info * redirect; - tr_tracker_info * addresses; - int addressIndex; - int addressCount; - int * tierFronts; + /* index into the torrent's tr_info.trackers array */ + int trackerIndex; /* sent as the "key" argument in tracker requests to verify us if our IP address changes. @@ -97,7 +99,7 @@ struct tr_tracker /* torrent hash string */ uint8_t hash[SHA_DIGEST_LENGTH]; - char escaped[SHA_DIGEST_LENGTH * 3 + 1]; + char escaped[SHA_DIGEST_LENGTH*3 + 1]; char * name; /* corresponds to the peer_id sent as a tracker request parameter. @@ -118,14 +120,10 @@ struct tr_tracker time_t scrapeAt; time_t lastScrapeTime; - char lastScrapeResponse[512]; + long lastScrapeResponse; time_t lastAnnounceTime; - char lastAnnounceResponse[512]; - - int randOffset; - - unsigned int isRunning : 1; + long lastAnnounceResponse; }; /** @@ -144,7 +142,7 @@ myDebug( const char * file, int line, const tr_tracker * t, const char * fmt, .. char * myfile = tr_strdup( file ); evbuffer_add_printf( buf, "[%s] ", tr_getLogTimeStr( timestr, sizeof(timestr) ) ); - if( t != NULL ) + if( t ) evbuffer_add_printf( buf, "%s ", t->name ); va_start( args, fmt ); evbuffer_add_vprintf( buf, fmt, args ); @@ -163,57 +161,38 @@ myDebug( const char * file, int line, const tr_tracker * t, const char * fmt, .. **** ***/ +static const tr_tracker_info * +getCurrentAddressFromTorrent( const tr_tracker * t, const tr_torrent * tor ) +{ + assert( t->trackerIndex >= 0 ); + assert( t->trackerIndex < tor->info.trackerCount ); + return tor->info.trackers + t->trackerIndex; +} + static const tr_tracker_info * getCurrentAddress( const tr_tracker * t ) { - assert( t->addresses != NULL ); - assert( t->addressIndex >= 0 ); - assert( t->addressIndex < t->addressCount ); - - return t->redirect ? t->redirect - : t->addresses + t->addressIndex; + const tr_torrent * torrent; + if(( torrent = tr_torrentFindFromHash( t->session, t->hash ))) + return getCurrentAddressFromTorrent( t, torrent ); + return NULL; } static int -trackerSupportsScrape( const tr_tracker * t ) +trackerSupportsScrape( const tr_tracker * t, const tr_torrent * tor ) { - const tr_tracker_info * info = getCurrentAddress( t ); - - return ( info != NULL ) - && ( info->scrape != NULL ) - && ( info->scrape[0] != '\0' ); + const tr_tracker_info * info = getCurrentAddressFromTorrent( t, tor ); + return info && info->scrape; } /*** **** ***/ -struct torrent_hash -{ - tr_handle * handle; - uint8_t hash[SHA_DIGEST_LENGTH]; -}; - -static struct torrent_hash* -torrentHashNew( tr_handle * handle, const tr_tracker * t ) -{ - struct torrent_hash * data = tr_new( struct torrent_hash, 1 ); - data->handle = handle; - memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH ); - return data; -} - tr_tracker * -findTrackerFromHash( struct torrent_hash * data ) +findTracker( tr_session * session, const uint8_t * hash ) { - tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash ); - return torrent ? torrent->tracker : NULL; -} - -tr_tracker * -findTracker( tr_handle * handle, const uint8_t * hash ) -{ - tr_torrent * torrent = tr_torrentFindFromHash( handle, hash ); + tr_torrent * torrent = tr_torrentFindFromHash( session, hash ); return torrent ? torrent->tracker : NULL; } @@ -226,7 +205,7 @@ static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0, 0 }; static void publishMessage( tr_tracker * t, const char * msg, int type ) { - if( t != NULL ) + if( t ) { tr_tracker_event event = emptyEvent; event.hash = t->hash; @@ -256,7 +235,8 @@ publishWarning( tr_tracker * t, const char * msg ) } static void -publishNewPeers( tr_tracker * t, int allAreSeeds, void * compact, int compactLen ) +publishNewPeers( tr_tracker * t, int allAreSeeds, + void * compact, int compactLen ) { tr_tracker_event event = emptyEvent; event.hash = t->hash; @@ -272,69 +252,31 @@ publishNewPeers( tr_tracker * t, int allAreSeeds, void * compact, int compactLen **** ***/ -static void onReqDone( tr_handle * handle ); - -static void -onStoppedResponse( struct evhttp_request * req UNUSED, void * handle ) -{ - dbgmsg( NULL, "got a response to some `stop' message" ); - onReqDone( handle ); -} - -static int -parseBencResponse( struct evhttp_request * req, tr_benc * setme ) -{ - const unsigned char * body = EVBUFFER_DATA( req->input_buffer ); - const int bodylen = EVBUFFER_LENGTH( req->input_buffer ); - return tr_bencLoad( body, bodylen, setme, NULL ); -} +static void onReqDone( tr_session * session ); static void -updateAddresses( tr_tracker * t, const struct evhttp_request * req, int * tryAgain ) +updateAddresses( tr_tracker * t, long response_code, int * tryAgain ) { int moveToNextAddress = FALSE; + tr_torrent * torrent = tr_torrentFindFromHash( t->session, t->hash ); - if( !req ) /* tracker didn't respond */ + if( !response_code ) /* tracker didn't respond */ { tr_ninf( t->name, _( "Tracker hasn't responded yet. Retrying..." ) ); moveToNextAddress = TRUE; } - else if( req->response_code == HTTP_OK ) + else if( response_code == HTTP_OK ) { - if( t->redirect != NULL ) - { - /* multitracker spec: "if a connection with a tracker is - successful, it will be moved to the front of the tier." */ - const int i = t->addressIndex; - const int j = t->tierFronts[i]; - const tr_tracker_info swap = t->addresses[i]; - t->addresses[i] = t->addresses[j]; - t->addresses[j] = swap; - } - } - else if( ( req->response_code == HTTP_MOVEPERM ) - || ( req->response_code == HTTP_MOVETEMP ) ) - { - const char * loc = evhttp_find_header( req->input_headers, "Location" ); - tr_tracker_info tmp; - if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */ - { - moveToNextAddress = TRUE; - } - else if( req->response_code == HTTP_MOVEPERM ) - { - tr_tracker_info * cur = &t->addresses[t->addressIndex]; - tr_trackerInfoClear( cur ); - *cur = tmp; - } - else if( req->response_code == HTTP_MOVETEMP ) - { - if( t->redirect == NULL ) - t->redirect = tr_new0( tr_tracker_info, 1 ); - else - tr_trackerInfoClear( t->redirect ); - *t->redirect = tmp; - } +#if 0 +/* FIXME */ + /* multitracker spec: "if a connection with a tracker is + successful, it will be moved to the front of the tier." */ + const int i = t->addressIndex; + const int j = t->tierFronts[i]; + const tr_tracker_info swap = t->addresses[i]; + t->addresses[i] = t->addresses[j]; + t->addresses[j] = swap; +#endif } else { @@ -345,15 +287,15 @@ updateAddresses( tr_tracker * t, const struct evhttp_request * req, int * tryAga if( moveToNextAddress ) { - if ( ++t->addressIndex >= t->addressCount ) /* we've tried them all */ + if ( ++t->trackerIndex >= torrent->info.trackerCount ) /* we've tried them all */ { *tryAgain = FALSE; - t->addressIndex = 0; + t->trackerIndex = 0; } else { - const tr_tracker_info * n = getCurrentAddress( t ); - tr_ninf( t->name, _( "Trying next tracker \"%s:%d\"" ), n->address, n->port ); + const tr_tracker_info * n = getCurrentAddressFromTorrent( t, torrent ); + tr_ninf( t->name, _( "Trying tracker \"%s\"" ), n->announce ); } } } @@ -372,23 +314,22 @@ parseOldPeers( tr_benc * bePeers, size_t * byteCount ) for( i=0, walk=compact; i<peerCount; ++i ) { + const char * s; + int64_t itmp; struct in_addr addr; tr_port_t port; - tr_benc * val; tr_benc * peer = &bePeers->val.l.vals[i]; - val = tr_bencDictFind( peer, "ip" ); - if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) ) + if( !tr_bencDictFindStr( peer, "ip", &s ) || tr_netResolve( s, &addr ) ) continue; memcpy( walk, &addr, 4 ); walk += 4; - val = tr_bencDictFind( peer, "port" ); - if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff ) + if( !tr_bencDictFindInt( peer, "port", &itmp ) || itmp<0 || itmp>0xffff ) continue; - port = htons( val->val.i ); + port = htons( itmp ); memcpy( walk, &port, 2 ); walk += 2; } @@ -398,70 +339,72 @@ parseOldPeers( tr_benc * bePeers, size_t * byteCount ) } static void -onTrackerResponse( struct evhttp_request * req, void * vhash ) +onStoppedResponse( tr_session * session, + long responseCode UNUSED, + const void * response UNUSED, + size_t responseLen UNUSED, + void * torrent_hash UNUSED ) +{ + dbgmsg( NULL, "got a response to some `stop' message" ); + onReqDone( session ); +} + +static void +onTrackerResponse( tr_session * session, + long responseCode, + const void * response, + size_t responseLen, + void * torrent_hash ) { int tryAgain; - int responseCode; - struct torrent_hash * torrent_hash = (struct torrent_hash*) vhash; - tr_tracker * t = findTrackerFromHash( torrent_hash ); + tr_tracker * t; - onReqDone( torrent_hash->handle ); + onReqDone( session ); + t = findTracker( session, torrent_hash ); tr_free( torrent_hash ); - - if( t == NULL ) /* tracker has been closed */ + if( !t ) /* tracker's been closed */ return; - dbgmsg( t, "got response from tracker: \"%s\"", - ( req && req->response_code_line ) ? req->response_code_line - : "(null)" ); + dbgmsg( t, "tracker response: %d", responseCode ); + tr_ndbg( t->name, "tracker response: %d", responseCode ); + t->lastAnnounceResponse = responseCode; - *t->lastAnnounceResponse = '\0'; - if( req && req->response_code_line ) - strlcpy( t->lastAnnounceResponse, req->response_code_line, sizeof( t->lastAnnounceResponse ) ); - - tr_ndbg( t->name, "tracker response: %s", - ( req ? req->response_code_line : "(null)") ); - - if( req && ( req->response_code == HTTP_OK ) ) + if( responseCode == HTTP_OK ) { tr_benc benc; - const int bencLoaded = !parseBencResponse( req, &benc ); - + const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, 0 ); publishErrorClear( t ); - - if( bencLoaded && benc.type==TYPE_DICT ) + if( bencLoaded && tr_bencIsDict( &benc ) ) { tr_benc * tmp; + int64_t i; int incomplete = -1; + const char * str; - if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) { - dbgmsg( t, "got failure message [%s]", tmp->val.s.s ); - publishErrorMessageAndStop( t, tmp->val.s.s ); - } + if(( tr_bencDictFindStr( &benc, "failure reason", &str ))) + publishErrorMessageAndStop( t, str ); - if(( tmp = tr_bencDictFind( &benc, "warning message" ))) { - dbgmsg( t, "got warning message [%s]", tmp->val.s.s ); - publishWarning( t, tmp->val.s.s ); - } + if(( tr_bencDictFindStr( &benc, "warning message", &str ))) + publishWarning( t, str ); - if(( tmp = tr_bencDictFind( &benc, "interval" ))) { - dbgmsg( t, "setting interval to %d", tmp->val.i ); - t->announceIntervalSec = tmp->val.i; + if(( tr_bencDictFindInt( &benc, "interval", &i ))) { + dbgmsg( t, "setting interval to %d", (int)i ); + t->announceIntervalSec = i; } - if(( tmp = tr_bencDictFind( &benc, "min interval" ))) { - dbgmsg( t, "setting min interval to %d", tmp->val.i ); - t->announceMinIntervalSec = tmp->val.i; + if(( tr_bencDictFindInt( &benc, "min interval", &i ))) { + dbgmsg( t, "setting min interval to %d", (int)i ); + t->announceMinIntervalSec = i; } - if(( tmp = tr_bencDictFind( &benc, "tracker id" ))) - t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i ); + if(( tr_bencDictFindStr( &benc, "tracker id", &str ))) + t->trackerID = tr_strdup( str ); - if(( tmp = tr_bencDictFind( &benc, "complete" ))) - t->seederCount = tmp->val.i; + if(( tr_bencDictFindInt( &benc, "complete", &i ))) + t->seederCount = i; - if(( tmp = tr_bencDictFind( &benc, "incomplete" ))) - t->leecherCount = incomplete = tmp->val.i; + if(( tr_bencDictFindInt( &benc, "incomplete", &i ))) + t->leecherCount = incomplete = i; if(( tmp = tr_bencDictFind( &benc, "peers" ))) { @@ -485,7 +428,7 @@ onTrackerResponse( struct evhttp_request * req, void * vhash ) tr_bencFree( &benc ); } - updateAddresses( t, req, &tryAgain ); + updateAddresses( t, responseCode, &tryAgain ); /** *** @@ -493,123 +436,101 @@ onTrackerResponse( struct evhttp_request * req, void * vhash ) if( tryAgain ) responseCode = 300; - else if( req ) - responseCode = req->response_code; - else - responseCode = 503; if( 200<=responseCode && responseCode<=299 ) { - dbgmsg( t, "request succeeded. reannouncing in %d seconds", - t->announceIntervalSec ); - t->reannounceAt = time( NULL ) + t->randOffset + t->announceIntervalSec; + const int interval = t->announceIntervalSec + t->randOffset; + dbgmsg( t, "request succeeded. reannouncing in %d seconds", interval ); + t->reannounceAt = time( NULL ) + interval; t->manualAnnounceAllowedAt = time( NULL ) + t->announceMinIntervalSec; } else if( 300<=responseCode && responseCode<=399 ) { - const int interval = 5; - dbgmsg( t, "got a redirect. retrying in %d seconds", interval ); - /* it's a redirect... updateAddresses() has already * parsed the redirect, all that's left is to retry */ + const int interval = 5; + dbgmsg( t, "got a redirect. retrying in %d seconds", interval ); t->reannounceAt = time( NULL ) + interval; t->manualAnnounceAllowedAt = time( NULL ) + t->announceMinIntervalSec; } else if( 400<=responseCode && responseCode<=499 ) { - const char * err = req && req->response_code_line - ? req->response_code_line - : "Unspecified 4xx error from tracker."; - dbgmsg( t, err ); - /* The request could not be understood by the server due to * malformed syntax. The client SHOULD NOT repeat the * request without modifications. */ - publishErrorMessageAndStop( t, err ); + publishErrorMessageAndStop( t, _( "Tracker returned a 4xx message" ) ); t->manualAnnounceAllowedAt = ~(time_t)0; t->reannounceAt = 0; } else if( 500<=responseCode && responseCode<=599 ) { - dbgmsg( t, "Got a 5xx error... retrying in one minute." ); - /* Response status codes beginning with the digit "5" indicate * cases in which the server is aware that it has erred or is * incapable of performing the request. So we pause a bit and * try again. */ - if( req && req->response_code_line ) - publishWarning( t, req->response_code_line ); t->manualAnnounceAllowedAt = ~(time_t)0; t->reannounceAt = time( NULL ) + 60; } else { - dbgmsg( t, "Invalid response from tracker... retrying in two minutes." ); - /* WTF did we get?? */ - if( req && req->response_code_line ) - publishWarning( t, req->response_code_line ); + dbgmsg( t, "Invalid response from tracker... retrying in two minutes." ); t->manualAnnounceAllowedAt = ~(time_t)0; t->reannounceAt = time( NULL ) + t->randOffset + 120; } } static void -onScrapeResponse( struct evhttp_request * req, void * vhash ) +onScrapeResponse( tr_session * session, + long responseCode, + const void * response, + size_t responseLen, + void * torrent_hash ) { int tryAgain; - int responseCode; - struct torrent_hash * torrent_hash = (struct torrent_hash*) vhash; - tr_tracker * t = findTrackerFromHash( torrent_hash ); + tr_tracker * t; - onReqDone( torrent_hash->handle ); + onReqDone( session ); + t = findTracker( session, torrent_hash ); tr_free( torrent_hash ); - - dbgmsg( t, "Got scrape response for '%s': %s (%d)", (t ? t->name : "(null)"), (req ? req->response_code_line : "(no line)"), (req ? req->response_code : -1) ); - - if( t == NULL ) /* tracker's been closed... */ + if( !t ) /* tracker's been closed... */ return; - *t->lastScrapeResponse = '\0'; - if( req && req->response_code_line ) - strlcpy( t->lastScrapeResponse, req->response_code_line, sizeof( t->lastScrapeResponse ) ); - - tr_ndbg( t->name, "Got scrape response: \"%s\"", - ( ( req && req->response_code_line ) ? req->response_code_line : "(null)") ); + dbgmsg( t, "scrape response: %ld\n", responseCode ); + tr_ndbg( t->name, "scrape response: %d", responseCode ); + t->lastScrapeResponse = responseCode; - if( req && ( req->response_code == HTTP_OK ) ) + if( responseCode == HTTP_OK ) { tr_benc benc, *files; - const int bencLoaded = !parseBencResponse( req, &benc ); - - if( bencLoaded - && (( files = tr_bencDictFind( &benc, "files" ) )) - && ( files->type == TYPE_DICT ) ) + const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, 0 ); + if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) ) { int i; for( i=0; i<files->val.l.count; i+=2 ) { + int64_t itmp; const uint8_t* hash = - (const uint8_t*) files->val.l.vals[i].val.s.s; - tr_benc *tmp, *flags; - tr_benc *tordict = &files->val.l.vals[i+1]; + (const uint8_t*) files->val.l.vals[i].val.s.s; + tr_benc * flags; + tr_benc * tordict = &files->val.l.vals[i+1]; if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) ) continue; publishErrorClear( t ); - if(( tmp = tr_bencDictFind( tordict, "complete" ))) - t->seederCount = tmp->val.i; + if(( tr_bencDictFindInt( tordict, "complete", &itmp ))) + t->seederCount = itmp; - if(( tmp = tr_bencDictFind( tordict, "incomplete" ))) - t->leecherCount = tmp->val.i; + if(( tr_bencDictFindInt( tordict, "incomplete", &itmp ))) + t->leecherCount = itmp; - if(( tmp = tr_bencDictFind( tordict, "downloaded" ))) - t->timesDownloaded = tmp->val.i; + if(( tr_bencDictFindInt( tordict, "downloaded", &itmp ))) + t->timesDownloaded = itmp; - if(( flags = tr_bencDictFind( tordict, "flags" ))) - if(( tmp = tr_bencDictFind( flags, "min_request_interval"))) - t->scrapeIntervalSec = tmp->val.i; + if( tr_bencDictFindDict( tordict, "flags", &flags )) + if(( tr_bencDictFindInt( flags, "min_request_interval", &itmp ))) + t->scrapeIntervalSec = i; tr_ndbg( t->name, "Scrape successful. Rescraping in %d seconds.", t->scrapeIntervalSec ); @@ -622,7 +543,7 @@ onScrapeResponse( struct evhttp_request * req, void * vhash ) tr_bencFree( &benc ); } - updateAddresses( t, req, &tryAgain ); + updateAddresses( t, responseCode, &tryAgain ); /** *** @@ -630,10 +551,6 @@ onScrapeResponse( struct evhttp_request * req, void * vhash ) if( tryAgain ) responseCode = 300; - else if( req ) - responseCode = req->response_code; - else - responseCode = 503; if( 200<=responseCode && responseCode<=299 ) { @@ -651,7 +568,8 @@ onScrapeResponse( struct evhttp_request * req, void * vhash ) else { const int interval = t->retryScrapeIntervalSec + t->randOffset; - dbgmsg( t, "Tracker responded to scrape with %d. Retrying in %d seconds.", responseCode, interval ); + dbgmsg( t, "Tracker responded to scrape with %d. Retrying in %d seconds.", + responseCode, interval ); t->retryScrapeIntervalSec *= 2; t->scrapeAt = time( NULL ) + interval; } @@ -673,50 +591,31 @@ enum struct tr_tracker_request { - int port; - int timeout; + char * url; int reqtype; /* TR_REQ_* */ - char * address; - char * uri; - struct evhttp_request * req; uint8_t torrent_hash[SHA_DIGEST_LENGTH]; + tr_web_done_func * done_func; + tr_session * session; }; static void freeRequest( struct tr_tracker_request * req ) { - tr_free( req->address ); - tr_free( req->uri ); + tr_free( req->url ); tr_free( req ); } static void -addCommonHeaders( const tr_tracker * t, - struct evhttp_request * req ) -{ - char buf[1024]; - const tr_tracker_info * address = getCurrentAddress( t ); - snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port ); - evhttp_add_header( req->output_headers, "Host", buf ); - evhttp_add_header( req->output_headers, "Connection", "close" ); - evhttp_add_header( req->output_headers, "User-Agent", - TR_NAME "/" LONG_VERSION_STRING ); -} - -static char* buildTrackerRequestURI( const tr_tracker * t, const tr_torrent * torrent, - const char * eventName ) + const char * eventName, + struct evbuffer * buf ) { const int isStopping = !strcmp( eventName, "stopped" ); const int numwant = isStopping ? 0 : NUMWANT; - struct evbuffer * buf = evbuffer_new( ); - char * ret; - - const char * ann = getCurrentAddress(t)->announce; + const char * ann = getCurrentAddressFromTorrent(t,torrent)->announce; - evbuffer_add_printf( buf, "%s" - "%cinfo_hash=%s" + evbuffer_add_printf( buf, "%cinfo_hash=%s" "&peer_id=%s" "&port=%d" "&uploaded=%"PRIu64 @@ -726,131 +625,111 @@ buildTrackerRequestURI( const tr_tracker * t, "&compact=1" "&numwant=%d" "&key=%s" - "&supportcrypto=1" - "&requirecrypto=%d" "%s%s" "%s%s", - ann, strchr(ann, '?') ? '&' : '?', t->escaped, t->peer_id, - tr_sharedGetPublicPort( t->handle->shared ), + tr_sharedGetPublicPort( t->session->shared ), torrent->uploadedCur, torrent->downloadedCur, torrent->corruptCur, tr_cpLeftUntilComplete( torrent->completion ), numwant, t->key_param, - ( t->handle->encryptionMode==TR_ENCRYPTION_REQUIRED ? 1 : 0 ), ( ( eventName && *eventName ) ? "&event=" : "" ), ( ( eventName && *eventName ) ? eventName : "" ), ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ), ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) ); - - ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) ); - evbuffer_free( buf ); - return ret; } static struct tr_tracker_request* -createRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype ) +createRequest( tr_session * session, const tr_tracker * tracker, int reqtype ) { - static const char* strings[TR_REQ_COUNT] = { "started", "completed", "stopped", "", "err" }; - const tr_torrent * torrent = tr_torrentFindFromHash( handle, tracker->hash ); - const tr_tracker_info * address = getCurrentAddress( tracker ); + static const char* strings[] = { "started", "completed", "stopped", "", "err" }; + const tr_torrent * torrent = tr_torrentFindFromHash( session, tracker->hash ); + const tr_tracker_info * address = getCurrentAddressFromTorrent( tracker, torrent ); const int isStopping = reqtype == TR_REQ_STOPPED; - const char * eventName = strings[reqtype]; struct tr_tracker_request * req; + struct evbuffer * url; + + url = evbuffer_new( ); + evbuffer_add_printf( url, "%s", address->announce ); + buildTrackerRequestURI( tracker, torrent, strings[reqtype], url ); req = tr_new0( struct tr_tracker_request, 1 ); - req->address = tr_strdup( address->address ); - req->port = address->port; - req->uri = buildTrackerRequestURI( tracker, torrent, eventName ); - req->timeout = isStopping ? STOP_TIMEOUT_INTERVAL_SEC : TIMEOUT_INTERVAL_SEC; + req->session = session; req->reqtype = reqtype; - req->req = isStopping - ? evhttp_request_new( onStoppedResponse, handle ) - : evhttp_request_new( onTrackerResponse, torrentHashNew(handle, tracker) ); + req->done_func = isStopping ? onStoppedResponse : onTrackerResponse; + req->url = tr_strdup( ( char * ) EVBUFFER_DATA( url ) ); memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH ); - addCommonHeaders( tracker, req->req ); + evbuffer_free( url ); return req; } static struct tr_tracker_request* -createScrape( tr_handle * handle, const tr_tracker * tracker ) +createScrape( tr_session * session, const tr_tracker * tracker ) { const tr_tracker_info * a = getCurrentAddress( tracker ); struct tr_tracker_request * req; + struct evbuffer * url = evbuffer_new( ); + + evbuffer_add_printf( url, "%s%cinfo_hash=%s", + a->scrape, strchr(a->scrape,'?')?'&':'?', + tracker->escaped ); req = tr_new0( struct tr_tracker_request, 1 ); - req->address = tr_strdup( a->address ); - req->port = a->port; - req->timeout = TIMEOUT_INTERVAL_SEC; - req->req = evhttp_request_new( onScrapeResponse, torrentHashNew( handle, tracker ) ); + req->session = session; req->reqtype = TR_REQ_SCRAPE; - tr_asprintf( &req->uri, "%s%cinfo_hash=%s", a->scrape, strchr(a->scrape,'?')?'&':'?', tracker->escaped ); + req->url = tr_strdup( ( char * ) EVBUFFER_DATA( url ) ); + req->done_func = onScrapeResponse; memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH ); - addCommonHeaders( tracker, req->req ); + evbuffer_free( url ); return req; } struct tr_tracker_handle { - int socketCount; unsigned int isShuttingDown : 1; + int runningCount; tr_timer * pulseTimer; - tr_list * requestQueue; - tr_list * scrapeQueue; }; -static int pulse( void * vhandle ); +static int pulse( void * vsession ); static void -ensureGlobalsExist( tr_handle * handle ) +ensureGlobalsExist( tr_session * session ) { - if( handle->tracker == NULL ) + if( session->tracker == NULL ) { - handle->tracker = tr_new0( struct tr_tracker_handle, 1 ); - handle->tracker->pulseTimer = tr_timerNew( handle, pulse, handle, PULSE_INTERVAL_MSEC ); + session->tracker = tr_new0( struct tr_tracker_handle, 1 ); + session->tracker->pulseTimer = tr_timerNew( session, pulse, session, PULSE_INTERVAL_MSEC ); dbgmsg( NULL, "creating tracker timer" ); } } -static void -freeRequest2( void * req ) -{ - freeRequest( req ); -} - void -tr_trackerShuttingDown( tr_handle * handle ) +tr_trackerShuttingDown( tr_session * session ) { - if( handle->tracker ) - { - /* since we're shutting down, we don't need to scrape anymore... */ - tr_list_free( &handle->tracker->scrapeQueue, freeRequest2 ); - - handle->tracker->isShuttingDown = 1; - } + if( session->tracker ) + session->tracker->isShuttingDown = 1; } static int -maybeFreeGlobals( tr_handle * handle ) +maybeFreeGlobals( tr_session * session ) { - int globalsExist = handle->tracker != NULL; + int globalsExist = session->tracker != NULL; if( globalsExist - && ( handle->tracker->socketCount < 1 ) - && ( handle->tracker->requestQueue == NULL ) - && ( handle->tracker->scrapeQueue == NULL ) - && ( handle->torrentList== NULL ) ) + && ( session->tracker->runningCount < 1 ) + && ( session->torrentList== NULL ) ) { dbgmsg( NULL, "freeing tracker timer" ); - tr_timerFree( &handle->tracker->pulseTimer ); - tr_free( handle->tracker ); - handle->tracker = NULL; + tr_timerFree( &session->tracker->pulseTimer ); + tr_free( session->tracker ); + session->tracker = NULL; globalsExist = FALSE; } @@ -861,159 +740,101 @@ maybeFreeGlobals( tr_handle * handle ) **** ***/ -static int -freeConnection( void * evcon ) -{ - evhttp_connection_free( evcon ); - return FALSE; -} static void -connectionClosedCB( struct evhttp_connection * evcon, void * vhandle ) +invokeRequest( void * vreq ) { - tr_handle * handle = vhandle; - - /* libevent references evcon right after calling this function, - so we can't free it yet... defer it to after this call chain - has played out */ - tr_timerNew( handle, freeConnection, evcon, 100 ); -} + struct tr_tracker_request * req = vreq; + uint8_t * hash; + tr_tracker * t = findTracker( req->session, req->torrent_hash ); -static struct evhttp_connection* -getConnection( tr_handle * handle, const char * address, int port ) -{ - struct evhttp_connection * c = evhttp_connection_new( address, port ); - evhttp_connection_set_closecb( c, connectionClosedCB, handle ); - return c; -} - -static void -invokeRequest( tr_handle * handle, const struct tr_tracker_request * req ) -{ - const time_t now = time( NULL ); - struct evhttp_connection * evcon = getConnection( handle, req->address, req->port ); - tr_tracker * t = findTracker( handle, req->torrent_hash ); - dbgmsg( t, "sending '%s' to tracker %s:%d, timeout is %d", req->uri, req->address, req->port, (int)req->timeout ); - evhttp_connection_set_timeout( evcon, req->timeout ); - ++handle->tracker->socketCount; - - if( t != NULL ) + if( t ) { if( req->reqtype == TR_REQ_SCRAPE ) { - t->lastScrapeTime = now; + t->lastScrapeTime = time( NULL ); t->scrapeAt = 0; } else { - t->lastAnnounceTime = now; + t->lastAnnounceTime = time( NULL ); t->reannounceAt = 0; - t->manualAnnounceAllowedAt = 0; + t->manualAnnounceAllowedAt = ~(time_t)0; } } - if( evhttp_make_request( evcon, req->req, EVHTTP_REQ_GET, req->uri )) - (*req->req->cb)(req->req, req->req->cb_arg); - else - dbgmsg( t, "incremented socket count to %d", handle->tracker->socketCount ); -} + ++req->session->tracker->runningCount; -static void -invokeNextInQueue( tr_handle * handle, tr_list ** list ) -{ - struct tr_tracker_request * req = tr_list_pop_front( list ); - invokeRequest( handle, req ); - freeRequest( req ); -} + hash = tr_new0( uint8_t, SHA_DIGEST_LENGTH ); + memcpy( hash, req->torrent_hash, SHA_DIGEST_LENGTH ); + tr_webRun( req->session, req->url, req->done_func, hash ); -static int -socketIsAvailable( tr_handle * handle ) -{ - const int max = handle->tracker->isShuttingDown - ? MAX_TRACKER_SOCKETS_DURING_SHUTDOWN - : MAX_TRACKER_SOCKETS; - return handle->tracker->socketCount < max; + freeRequest( req ); } -static void ensureGlobalsExist( tr_handle * ); +static void ensureGlobalsExist( tr_session * ); static void -enqueueScrape( tr_handle * handle, const tr_tracker * tracker ) +enqueueScrape( tr_session * session, const tr_tracker * tracker ) { struct tr_tracker_request * req; - ensureGlobalsExist( handle ); - req = createScrape( handle, tracker ); - tr_list_append( &handle->tracker->scrapeQueue, req ); + ensureGlobalsExist( session ); + req = createScrape( session, tracker ); + tr_runInEventThread( session, invokeRequest, req ); } static void -enqueueRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype ) +enqueueRequest( tr_session * session, const tr_tracker * tracker, int reqtype ) { struct tr_tracker_request * req; - ensureGlobalsExist( handle ); - req = createRequest( handle, tracker, reqtype ); - tr_list_append( &handle->tracker->requestQueue, req ); -} - -static void -scrapeSoon( tr_tracker * t ) -{ - if( trackerSupportsScrape( t ) ) - t->scrapeAt = time( NULL ) + t->randOffset; + ensureGlobalsExist( session ); + req = createRequest( session, tracker, reqtype ); + tr_runInEventThread( session, invokeRequest, req ); } static int -pulse( void * vhandle ) +pulse( void * vsession ) { - tr_handle * handle = vhandle; - struct tr_tracker_handle * th = handle->tracker; + tr_session * session = vsession; + struct tr_tracker_handle * th = session->tracker; tr_torrent * tor; const time_t now = time( NULL ); - if( handle->tracker == NULL ) + if( !session->tracker ) return FALSE; - if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) ) - dbgmsg( NULL, "tracker pulse... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) ); + if( th->runningCount ) + dbgmsg( NULL, "tracker pulse... %d running", th->runningCount ); /* upkeep: queue periodic rescrape / reannounce */ - for( tor=handle->torrentList; tor; tor=tor->next ) + for( tor=session->torrentList; tor; tor=tor->next ) { tr_tracker * t = tor->tracker; - if( t->scrapeAt && trackerSupportsScrape( t ) && ( now >= t->scrapeAt ) ) { + if( t->scrapeAt && trackerSupportsScrape( t, tor ) && ( now >= t->scrapeAt ) ) { t->scrapeAt = 0; - enqueueScrape( handle, t ); + enqueueScrape( session, t ); } if( t->reannounceAt && t->isRunning && ( now >= t->reannounceAt ) ) { t->reannounceAt = 0; - enqueueRequest( handle, t, TR_REQ_REANNOUNCE ); + enqueueRequest( session, t, TR_REQ_REANNOUNCE ); } } - if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) ) - dbgmsg( NULL, "tracker pulse after upkeep... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) ); - - /* look for things to do... process all the requests, then process all the scrapes */ - while( th->requestQueue && socketIsAvailable( handle ) ) - invokeNextInQueue( handle, &th->requestQueue ); - while( th->scrapeQueue && socketIsAvailable( handle ) ) - invokeNextInQueue( handle, &th->scrapeQueue ); + if( th->runningCount ) + dbgmsg( NULL, "tracker pulse after upkeep... %d running", th->runningCount ); - if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) ) - dbgmsg( NULL, "tracker pulse done... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) ); - - return maybeFreeGlobals( handle ); + return maybeFreeGlobals( session ); } static void -onReqDone( tr_handle * handle ) +onReqDone( tr_session * session ) { - if( handle->tracker ) + if( session->tracker ) { - pulse( handle ); - --handle->tracker->socketCount; - dbgmsg( NULL, "decrementing socket count to %d", handle->tracker->socketCount ); + --session->tracker->runningCount; + dbgmsg( NULL, "decrementing runningCount to %d", session->tracker->runningCount ); + pulse( session ); } } @@ -1056,56 +877,29 @@ tr_tracker * tr_trackerNew( const tr_torrent * torrent ) { const tr_info * info = &torrent->info; - int i, j, sum, *iwalk; - tr_tracker_info * nwalk; tr_tracker * t; t = tr_new0( tr_tracker, 1 ); - t->handle = torrent->handle; - t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC; - t->retryScrapeIntervalSec = 60; - t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC; - t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC; - generateKeyParam( t->key_param, KEYLEN ); - t->publisher = tr_publisherNew( ); - t->timesDownloaded = -1; - t->seederCount = -1; - t->leecherCount = -1; - t->manualAnnounceAllowedAt = ~(time_t)0; + t->session = torrent->handle; + t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC; + t->retryScrapeIntervalSec = 60; + t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC; + t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC; + t->timesDownloaded = -1; + t->seederCount = -1; + t->leecherCount = -1; + t->manualAnnounceAllowedAt = ~(time_t)0; t->name = tr_strdup( info->name ); t->randOffset = tr_rand( 120 ); memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH ); escape( t->escaped, info->hash, SHA_DIGEST_LENGTH ); + generateKeyParam( t->key_param, KEYLEN ); - for( sum=i=0; i<info->trackerTiers; ++i ) - sum += info->trackerList[i].count; - t->addresses = nwalk = tr_new0( tr_tracker_info, sum ); - t->addressIndex = 0; - t->addressCount = sum; - t->tierFronts = iwalk = tr_new0( int, sum ); - - for( i=0; i<info->trackerTiers; ++i ) - { - const int tierFront = nwalk - t->addresses; - - for( j=0; j<info->trackerList[i].count; ++j ) - { - const tr_tracker_info * src = &info->trackerList[i].list[j]; - nwalk->address = tr_strdup( src->address ); - nwalk->port = src->port; - nwalk->announce = tr_strdup( src->announce ); - nwalk->scrape = tr_strdup( src->scrape ); - ++nwalk; - - *iwalk++ = tierFront; - } - } - - assert( nwalk - t->addresses == sum ); - assert( iwalk - t->tierFronts == sum ); + t->trackerIndex = 0; - scrapeSoon( t ); + if( trackerSupportsScrape( t, torrent ) ) + t->scrapeAt = time( NULL ) + t->randOffset; return t; } @@ -1113,7 +907,6 @@ tr_trackerNew( const tr_torrent * torrent ) static void onTrackerFreeNow( void * vt ) { - int i; tr_tracker * t = vt; tr_publisherFree( &t->publisher ); @@ -1121,18 +914,6 @@ onTrackerFreeNow( void * vt ) tr_free( t->trackerID ); tr_free( t->peer_id ); - /* addresses... */ - for( i=0; i<t->addressCount; ++i ) - tr_trackerInfoClear( &t->addresses[i] ); - tr_free( t->addresses ); - tr_free( t->tierFronts ); - - /* redirect... */ - if( t->redirect ) { - tr_trackerInfoClear( t->redirect ); - tr_free( t->redirect ); - } - tr_free( t ); } @@ -1144,7 +925,7 @@ void tr_trackerFree( tr_tracker * t ) { if( t ) - tr_runInEventThread( t->handle, onTrackerFreeNow, t ); + tr_runInEventThread( t->session, onTrackerFreeNow, t ); } tr_publisher_tag @@ -1202,37 +983,36 @@ tr_trackerGetCounts( const tr_tracker * t, void tr_trackerStart( tr_tracker * t ) { - if( t ) + if( t && !t->isRunning ) { tr_free( t->peer_id ); t->peer_id = tr_peerIdNew( ); - if( t->isRunning == 0 ) { - t->isRunning = 1; - enqueueRequest( t->handle, t, TR_REQ_STARTED ); - } + t->isRunning = 1; + enqueueRequest( t->session, t, TR_REQ_STARTED ); } } void tr_trackerReannounce( tr_tracker * t ) { - enqueueRequest( t->handle, t, TR_REQ_REANNOUNCE ); + enqueueRequest( t->session, t, TR_REQ_REANNOUNCE ); } void tr_trackerCompleted( tr_tracker * t ) { - enqueueRequest( t->handle, t, TR_REQ_COMPLETED ); + enqueueRequest( t->session, t, TR_REQ_COMPLETED ); } void tr_trackerStop( tr_tracker * t ) { +fprintf( stderr, "trackerStop... isRunning %d\n", (int)t->isRunning ); if( t && t->isRunning ) { t->isRunning = 0; t->reannounceAt = t->manualAnnounceAllowedAt = 0; - enqueueRequest( t->handle, t, TR_REQ_STOPPED ); + enqueueRequest( t->session, t, TR_REQ_STOPPED ); } } @@ -1244,21 +1024,22 @@ tr_trackerChangeMyPort( tr_tracker * t ) } void -tr_trackerStat( const tr_tracker * t, +tr_trackerStat( const tr_tracker * t, struct tr_tracker_stat * setme) { assert( t != NULL ); assert( setme != NULL ); - strlcpy( setme->scrapeResponse, - t->lastScrapeResponse, - sizeof( setme->scrapeResponse ) ); + snprintf( setme->scrapeResponse, + sizeof( setme->scrapeResponse ), + "%ld", t->lastScrapeResponse ); setme->lastScrapeTime = t->lastScrapeTime; setme->nextScrapeTime = t->scrapeAt; - strlcpy( setme->announceResponse, - t->lastAnnounceResponse, - sizeof( setme->announceResponse ) ); + snprintf( setme->announceResponse, + sizeof( setme->announceResponse ), + "%ld", t->lastAnnounceResponse ); + setme->lastAnnounceTime = t->lastAnnounceTime; setme->nextAnnounceTime = t->reannounceAt; setme->nextManualAnnounceTime = t->manualAnnounceAllowedAt; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 40a123186..9727cc9cd 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -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; diff --git a/libtransmission/trevent.c b/libtransmission/trevent.c index 34333c86a..e88863010 100644 --- a/libtransmission/trevent.c +++ b/libtransmission/trevent.c @@ -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; diff --git a/libtransmission/utils.c b/libtransmission/utils.c index f605fd008..6f168c105 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -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; + + 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 );*/ + } + } - success = parseURL( url, host, &port, &path ); + err = !host || !path || !protocol || ( strcmp(protocol,"http") && strcmp(protocol,"https") ); - if( success ) { - if( setme_host ) *setme_host = tr_strdup( host ); - if( setme_port ) *setme_port = port; - if( setme_path ) *setme_path = tr_strdup( path ); + if( !err && !port ) { + if( !strcmp(protocol,"http") ) port = 80; + if( !strcmp(protocol,"https") ) port = 443; } - tr_free( url ); + 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; + } - return !success; -} + tr_free( tmp ); + return err; +} diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 7ba27a977..78e64f037 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -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, diff --git a/libtransmission/web.c b/libtransmission/web.c new file mode 100644 index 000000000..8038a4739 --- /dev/null +++ b/libtransmission/web.c @@ -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; +} diff --git a/libtransmission/web.h b/libtransmission/web.h new file mode 100644 index 000000000..bb926c9d8 --- /dev/null +++ b/libtransmission/web.h @@ -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 -- 2.40.0