From 0545e70f5b7c3b2967eef34c078a6f48043bd3ec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 25 Apr 2008 18:35:48 +0000 Subject: [PATCH] (gtk) use libT's curl wrapper for port testing & getting the blocklist --- gtk/tr-prefs.c | 104 ++++++++++++++------------- libtransmission/web.c | 162 +++++++++++++++++++++++------------------- 2 files changed, 140 insertions(+), 126 deletions(-) diff --git a/gtk/tr-prefs.c b/gtk/tr-prefs.c index 1c2190608..a0161c5bf 100644 --- a/gtk/tr-prefs.c +++ b/gtk/tr-prefs.c @@ -14,9 +14,9 @@ #include #include #include -#include #include #include +#include #include "conf.h" #include "hig.h" #include "tr-core.h" @@ -177,46 +177,48 @@ struct test_port_data gboolean * alive; }; -static gpointer -test_port( gpointer data_gpointer ) +static void +testing_port_done( tr_handle * handle UNUSED, + long response_code UNUSED, + const void * response, + size_t response_len, + void * gdata ) { - struct test_port_data * data = data_gpointer; - + struct test_port_data * data = gdata; if( *data->alive ) { - GObject * o = G_OBJECT( data->label ); - GtkSpinButton * spin = g_object_get_data( o, "tr-port-spin" ); - const int port = gtk_spin_button_get_value_as_int( spin ); - int isOpen; - int size; - char * text; - char url[256]; - - g_usleep( G_USEC_PER_SEC * 3 ); /* give portmapping time to kick in */ - snprintf( url, sizeof(url), "http://portcheck.transmissionbt.com/%d", port ); - text = miniwget( url, &size ); - /*g_message(" got len %d, [%*.*s]", size, size, size, text );*/ - isOpen = text && *text=='1'; - free( text ); - - if( *data->alive ) - gtk_label_set_markup( GTK_LABEL(data->label), isOpen - ? _("Port is open") - : _("Port is closed") ); + const int isOpen = response_len && *(char*)response=='1'; + gtk_label_set_markup( GTK_LABEL( data->label ), isOpen + ? _("Port is open") + : _("Port is closed") ); } +} - g_free( data ); - return NULL; +static gboolean +testing_port_begin( gpointer gdata ) +{ + struct test_port_data * data = gdata; + if( *data->alive ) + { + GtkSpinButton * spin = g_object_get_data( G_OBJECT( data->label ), "tr-port-spin" ); + tr_handle * handle = g_object_get_data( G_OBJECT( data->label ), "handle" ); + const int port = gtk_spin_button_get_value_as_int( spin ); + char url[256]; + snprintf( url, sizeof(url), "http://portcheck.transmissionbt.com/%d", port ); + gtk_label_set_markup( GTK_LABEL( data->label ), _( "Testing port..." ) ); + tr_webRun( handle, url, testing_port_done, data ); + } + return FALSE; } static void testing_port_cb( GtkWidget * unused UNUSED, gpointer l ) { + /* wait three seconds to give the port forwarding time to kick in */ struct test_port_data * data = g_new0( struct test_port_data, 1 ); - data->alive = g_object_get_data( G_OBJECT( l ), "alive" ); data->label = l; - gtk_label_set_markup( GTK_LABEL(l), _( "Testing port..." ) ); - g_thread_create( test_port, data, FALSE, NULL ); + data->alive = g_object_get_data( G_OBJECT( l ), "alive" ); + g_timeout_add( 3000, testing_port_begin, data ); } static void @@ -316,35 +318,30 @@ blocklistDialogAllowClose( gpointer dialog ) return FALSE; } -static gpointer -updateBlocklist( gpointer vdata ) +static void +got_blocklist( tr_handle * handle UNUSED, + long response_code UNUSED, + const void * response, + size_t response_len, + void * gdata ) { - struct blocklist_data * data = vdata; - int size = 0; + struct blocklist_data * data = gdata; + const char * text = response; + int size = response_len; int rules = 0; - const char * url; - char * text = NULL; gchar * filename = NULL; gchar * filename2 = NULL; int fd = -1; int ok = 1; - url = "http://download.m0k.org/transmission/files/level1.gz"; - - if( ok && !data->abortFlag ) + if( !data->abortFlag && ( !text || !size ) ) { + ok = FALSE; g_snprintf( data->secondary, sizeof( data->secondary ), - _( "Retrieving blocklist..." ) ); + _( "Unable to get blocklist." ) ); + g_message( data->secondary ); g_idle_add( blocklistDialogSetSecondary, data ); - text = miniwget( url, &size ); - if( !data->abortFlag && ( !text || !size ) ) { - ok = FALSE; - g_snprintf( data->secondary, sizeof( data->secondary ), - _( "Unable to get blocklist." ) ); - g_message( data->secondary ); - g_idle_add( blocklistDialogSetSecondary, data ); - } - } + } if( ok && !data->abortFlag ) { @@ -389,7 +386,6 @@ updateBlocklist( gpointer vdata ) g_idle_add( updateBlocklistTextFromData, data ); } - free( text ); /* g_free( data ); */ if( filename2 ) { unlink( filename2 ); @@ -399,7 +395,6 @@ updateBlocklist( gpointer vdata ) unlink( filename ); g_free( filename ); } - return NULL; } static void @@ -419,12 +414,16 @@ onUpdateBlocklistCB( GtkButton * w, gpointer gdata ) { GtkWidget * d; struct blocklist_data * data = gdata; + tr_handle * handle = g_object_get_data( G_OBJECT( w ), "handle" ); + const char * url = "http://download.m0k.org/transmission/files/level1.gz"; d = gtk_message_dialog_new( GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( w ) ) ), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_NONE, _( "Updating Blocklist" ) ); + gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( d ), + _( "Retrieving blocklist..." ) ); gtk_dialog_add_buttons( GTK_DIALOG( d ), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, @@ -435,7 +434,8 @@ onUpdateBlocklistCB( GtkButton * w, gpointer gdata ) g_signal_connect( d, "response", G_CALLBACK(onUpdateBlocklistResponseCB), data ); gtk_widget_show( d ); - g_thread_create( updateBlocklist, data, FALSE, NULL ); + + tr_webRun( handle, url, got_blocklist, data ); } static GtkWidget* @@ -462,6 +462,7 @@ peerPage( GObject * core ) data->core = TR_CORE( core ); data->check = w; + g_object_set_data( G_OBJECT( b ), "handle", tr_core_handle( TR_CORE( core ) ) ); g_signal_connect( b, "clicked", G_CALLBACK(onUpdateBlocklistCB), data ); gtk_box_pack_start( GTK_BOX(h), b, FALSE, FALSE, 0 ); g_signal_connect( w, "toggled", G_CALLBACK(target_cb), b ); @@ -533,6 +534,7 @@ networkPage( GObject * core, gpointer alive ) g_object_set_data( G_OBJECT(l), "tr-port-spin", w2 ); g_object_set_data( G_OBJECT(l), "alive", alive ); + g_object_set_data( G_OBJECT(l), "handle", tr_core_handle( TR_CORE( core ) ) ); testing_port_cb( NULL, l ); g_signal_connect( w, "toggled", G_CALLBACK(testing_port_cb), l ); diff --git a/libtransmission/web.c b/libtransmission/web.c index 87dcb6b12..dbc1250ae 100644 --- a/libtransmission/web.c +++ b/libtransmission/web.c @@ -1,4 +1,5 @@ -/* * This file Copyright (C) 2008 Charles Kerr +/* + * This file Copyright (C) 2008 Charles Kerr * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) @@ -15,6 +16,7 @@ #include #include "transmission.h" +#include "trevent.h" #include "utils.h" #include "web.h" @@ -28,9 +30,7 @@ #define USE_CURL_MULTI_SOCKET #else #define PULSE_MSEC 200 -static void pulse( int socket UNUSED, short action UNUSED, void * vweb ); #endif -static void processCompletedTasks( tr_web * ); #define dbgmsg(fmt...) tr_deepLog( __FILE__, __LINE__, "web", ##fmt ) @@ -46,80 +46,24 @@ struct tr_web_task { unsigned long tag; struct evbuffer * response; + char * url; + tr_session * session; tr_web_done_func * done_func; void * done_func_user_data; }; -static size_t -writeFunc( void * ptr, size_t size, size_t nmemb, void * task ) -{ - const size_t byteCount = size * nmemb; - evbuffer_add( ((struct tr_web_task*)task)->response, ptr, byteCount ); - return byteCount; -} - -static void -pump( tr_web * web ) -{ - int unused; - CURLMcode rc; - do -#ifdef USE_CURL_MULTI_SOCKET - rc = curl_multi_socket_all( web->cm, &unused ); -#else - rc = curl_multi_perform( web->cm, &unused ); -#endif - while( rc == CURLM_CALL_MULTI_PERFORM ); - if ( rc == CURLM_OK ) - processCompletedTasks( web ); - else - tr_err( "%s", curl_multi_strerror(rc) ); -} - -void -tr_webRun( tr_session * session, - const char * url, - tr_web_done_func * done_func, - void * done_func_user_data ) -{ - static unsigned long tag = 0; - struct tr_web_task * task; - struct tr_web * web = session->web; - CURL * ch; - - 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( ); - - dbgmsg( "adding task #%lu [%s]", 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_easy_setopt( ch, CURLOPT_SSL_VERIFYPEER, 0 ); - curl_multi_add_handle( web->cm, ch ); - - pump( web ); -} - static void processCompletedTasks( tr_web * web ) { - int more = 0; CURL * easy; CURLMsg * msg; CURLcode res; do { - /* find a completed task. this idea is from the "hiperinfo.c" - * sample that questions the safety of removing an easy handle - * inside the curl_multi_info_read loop */ + /* this convoluted loop is from the "hiperinfo.c" sample which + * hints that removing an easy handle in curl_multi_info_read's + * loop may be unsafe */ + int more; easy = NULL; while(( msg = curl_multi_info_read( web->cm, &more ))) { if( msg->msg == CURLMSG_DONE ) { @@ -133,9 +77,8 @@ processCompletedTasks( tr_web * web ) int response_code; curl_easy_getinfo( easy, CURLINFO_PRIVATE, &task ); curl_easy_getinfo( easy, CURLINFO_RESPONSE_CODE, &response_code ); - --web->remain; - dbgmsg( "task #%lu done (%d tasks remain)", task->tag, web->remain ); + dbgmsg( "task #%lu done (%d remain)", task->tag, web->remain ); task->done_func( web->session, response_code, EVBUFFER_DATA(task->response), @@ -145,16 +88,85 @@ processCompletedTasks( tr_web * web ) curl_multi_remove_handle( web->cm, easy ); curl_easy_cleanup( easy ); evbuffer_free( task->response ); + tr_free( task->url ); tr_free( task ); } } while( easy ); } +static void +pump( tr_web * web ) +{ + int unused; + CURLMcode rc; + do { +#ifdef USE_CURL_MULTI_SOCKET + rc = curl_multi_socket_all( web->cm, &unused ); +#else + rc = curl_multi_perform( web->cm, &unused ); +#endif + } while( rc == CURLM_CALL_MULTI_PERFORM ); + if ( rc == CURLM_OK ) + processCompletedTasks( web ); + else + tr_err( "%s", curl_multi_strerror(rc) ); +} + +static size_t +writeFunc( void * ptr, size_t size, size_t nmemb, void * task ) +{ + const size_t byteCount = size * nmemb; + evbuffer_add( ((struct tr_web_task*)task)->response, ptr, byteCount ); + return byteCount; +} + +static void +addTask( void * vtask ) +{ + struct tr_web_task * task = vtask; + struct tr_web * web = task->session->web; + CURL * ch; + + dbgmsg( "adding task #%lu [%s]", task->tag, task->url ); + ++web->remain; + + ch = curl_easy_init( ); + curl_easy_setopt( ch, CURLOPT_PRIVATE, task ); + curl_easy_setopt( ch, CURLOPT_URL, task->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_easy_setopt( ch, CURLOPT_SSL_VERIFYPEER, 0 ); + curl_multi_add_handle( web->cm, ch ); + + pump( web ); +} + +void +tr_webRun( tr_session * session, + const char * url, + tr_web_done_func * done_func, + void * done_func_user_data ) +{ + static unsigned long tag = 0; + struct tr_web_task * task; + + task = tr_new0( struct tr_web_task, 1 ); + task->session = session; + task->url = tr_strdup( url ); + task->done_func = done_func; + task->done_func_user_data = done_func_user_data; + task->tag = ++tag; + task->response = evbuffer_new( ); + + tr_runInEventThread( session, addTask, task ); +} + #ifdef USE_CURL_MULTI_SOCKET /* libevent says that sock is ready to be processed, so tell libcurl */ static void -event_callback( int sock, short action, void * vweb ) +ev_sock_cb( int sock, short action, void * vweb ) { tr_web * web = vweb; CURLMcode rc; @@ -167,13 +179,14 @@ event_callback( int sock, short action, void * vweb ) default: tr_err( "Unknown event %hd\n", action ); return; } - do rc = curl_multi_socket_action( web->cm, sock, mask, &unused ); - while( rc == CURLM_CALL_MULTI_PERFORM ); - - if ( rc != CURLM_OK ) + do { + rc = curl_multi_socket_action( web->cm, sock, mask, &unused ); + } while( rc == CURLM_CALL_MULTI_PERFORM ); + if ( rc == CURLM_OK ) + processCompletedTasks( web ); + else tr_err( "%s (%d)", curl_multi_strerror(rc), (int)sock ); - processCompletedTasks( web ); } /* CURLMPOPT_SOCKETFUNCTION */ @@ -205,7 +218,7 @@ multi_sock_cb( CURL * easy UNUSED, kind = EV_PERSIST; if( action & CURL_POLL_IN ) kind |= EV_READ; if( action & CURL_POLL_OUT ) kind |= EV_WRITE; - event_set( ev, sock, kind, event_callback, web ); + event_set( ev, sock, kind, ev_sock_cb, web ); event_add( ev, NULL ); } } @@ -245,7 +258,6 @@ pulse( int socket UNUSED, short action UNUSED, void * vweb ) struct timeval tv = tr_timevalMsec( PULSE_MSEC ); pump( web ); - processCompletedTasks( web ); evtimer_del( &web->timer ); evtimer_add( &web->timer, &tv );