Patch is extracted from transmission svn to already applied upstream Index: libtransmission/rpc-server.c =================================================================== --- libtransmission/rpc-server.c (revision 8399) +++ libtransmission/rpc-server.c (revision 8400) @@ -32,6 +32,7 @@ #include "crypto.h" #include "list.h" #include "platform.h" +#include "ptrarray.h" #include "rpcimpl.h" #include "rpc-server.h" #include "trevent.h" @@ -83,6 +84,36 @@ } while( 0 ) +/*** +**** +***/ + +static char* +get_current_session_id( struct tr_rpc_server * server ) +{ + const time_t now = time( NULL ); + + if( !server->sessionId || ( now >= server->sessionIdExpiresAt ) ) + { + int i; + const int n = 48; + const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const size_t pool_size = strlen( pool ); + char * buf = tr_new( char, n+1 ); + + for( i=0; isessionId ); + server->sessionId = buf; + server->sessionIdExpiresAt = now + (60*60); /* expire in an hour */ + } + + return server->sessionId; +} + + /** *** **/ @@ -104,10 +135,8 @@ } static const char* -tr_memmem( const char * s1, - size_t l1, - const char * s2, - size_t l2 ) +tr_memmem( const char * s1, size_t l1, /* haystack */ + const char * s2, size_t l2 ) /* needle */ { if( !l2 ) return s1; while( l1 >= l2 ) @@ -121,7 +150,68 @@ return NULL; } +struct tr_mimepart +{ + char * headers; + int headers_len; + char * body; + int body_len; +}; + static void +tr_mimepart_free( struct tr_mimepart * p ) +{ + tr_free( p->body ); + tr_free( p->headers ); + tr_free( p ); +} + +static void +extract_parts_from_multipart( const struct evkeyvalq * headers, + const struct evbuffer * body, + tr_ptrArray * setme_parts ) +{ + const char * content_type = evhttp_find_header( headers, "Content-Type" ); + const char * in = (const char*) EVBUFFER_DATA( body ); + size_t inlen = EVBUFFER_LENGTH( body ); + + const char * boundary_key = "boundary="; + const char * boundary_key_begin = strstr( content_type, boundary_key ); + const char * boundary_val = boundary_key_begin ? boundary_key_begin + strlen( boundary_key ) : "arglebargle"; + char * boundary = tr_strdup_printf( "--%s", boundary_val ); + const size_t boundary_len = strlen( boundary ); + + const char * delim = tr_memmem( in, inlen, boundary, boundary_len ); + while( delim ) + { + size_t part_len; + const char * part = delim + boundary_len; + + inlen -= ( part - in ); + in = part; + + delim = tr_memmem( in, inlen, boundary, boundary_len ); + part_len = delim ? (size_t)( delim - part ) : inlen; + + if( part_len ) + { + const char * rnrn = tr_memmem( part, part_len, "\r\n\r\n", 4 ); + if( rnrn ) + { + struct tr_mimepart * p = tr_new( struct tr_mimepart, 1 ); + p->headers_len = rnrn - part; + p->headers = tr_strndup( part, p->headers_len ); + p->body_len = (part+part_len) - (rnrn + 4); + p->body = tr_strndup( rnrn+4, p->body_len ); + tr_ptrArrayAppend( setme_parts, p ); + } + } + } + + tr_free( boundary ); +} + +static void handle_upload( struct evhttp_request * req, struct tr_rpc_server * server ) { @@ -131,76 +221,67 @@ } else { - const char * content_type = evhttp_find_header( req->input_headers, - "Content-Type" ); + int i; + int n; + tr_bool hasSessionId = FALSE; + tr_ptrArray parts = TR_PTR_ARRAY_INIT; const char * query = strchr( req->uri, '?' ); - const int paused = query && strstr( query + 1, "paused=true" ); + const tr_bool paused = query && strstr( query + 1, "paused=true" ); - const char * in = (const char *) EVBUFFER_DATA( req->input_buffer ); - size_t inlen = EVBUFFER_LENGTH( req->input_buffer ); + extract_parts_from_multipart( req->input_headers, req->input_buffer, &parts ); + n = tr_ptrArraySize( &parts ); - const char * boundary_key = "boundary="; - const char * boundary_key_begin = strstr( content_type, - boundary_key ); - const char * boundary_val = - boundary_key_begin ? boundary_key_begin + - strlen( boundary_key ) : "arglebargle"; + /* first look for the session id */ + for( i=0; iheaders, p->headers_len, TR_RPC_SESSION_ID_HEADER, strlen( TR_RPC_SESSION_ID_HEADER ) ) ) + break; + } + if( ibody_len && !memcmp( p->body, ours, ourlen ); + } - char * boundary = tr_strdup_printf( "--%s", boundary_val ); - const size_t boundary_len = strlen( boundary ); - - const char * delim = tr_memmem( in, inlen, boundary, boundary_len ); - while( delim ) + if( !hasSessionId ) { - size_t part_len; - const char * part = delim + boundary_len; - inlen -= ( part - in ); - in = part; - delim = tr_memmem( in, inlen, boundary, boundary_len ); - part_len = delim ? (size_t)( delim - part ) : inlen; - - if( part_len ) + send_simple_response( req, 409, NULL ); + } + else for( i=0; iheaders, "filename=\"" ) ) { - char * text = tr_strndup( part, part_len ); - if( strstr( text, "filename=\"" ) ) - { - const char * body = strstr( text, "\r\n\r\n" ); - if( body ) - { - char * b64; - size_t body_len; - tr_benc top, *args; - struct evbuffer * json = tr_getBuffer( ); + char * b64; + int body_len = p->body_len; + tr_benc top, *args; + const char * body = p->body; + struct evbuffer * json = evbuffer_new( ); - body += 4; /* walk past the \r\n\r\n */ - body_len = part_len - ( body - text ); - if( body_len >= 2 - && !memcmp( &body[body_len - 2], "\r\n", 2 ) ) - body_len -= 2; + if( body_len >= 2 && !memcmp( &body[body_len - 2], "\r\n", 2 ) ) + body_len -= 2; - tr_bencInitDict( &top, 2 ); - args = tr_bencDictAddDict( &top, "arguments", 2 ); - tr_bencDictAddStr( &top, "method", "torrent-add" ); - b64 = tr_base64_encode( body, body_len, NULL ); - tr_bencDictAddStr( args, "metainfo", b64 ); - tr_bencDictAddBool( args, "paused", paused ); - tr_bencSaveAsJSON( &top, json ); - tr_rpc_request_exec_json( server->session, - EVBUFFER_DATA( json ), - EVBUFFER_LENGTH( json ), - NULL, NULL ); + tr_bencInitDict( &top, 2 ); + args = tr_bencDictAddDict( &top, "arguments", 2 ); + tr_bencDictAddStr( &top, "method", "torrent-add" ); + b64 = tr_base64_encode( body, body_len, NULL ); + tr_bencDictAddStr( args, "metainfo", b64 ); + tr_bencDictAddBool( args, "paused", paused ); + tr_bencSaveAsJSON( &top, json ); + tr_rpc_request_exec_json( server->session, + EVBUFFER_DATA( json ), + EVBUFFER_LENGTH( json ), + NULL, NULL ); - tr_releaseBuffer( json ); - tr_free( b64 ); - tr_bencFree( &top ); - } - } - tr_free( text ); + evbuffer_free( json ); + tr_free( b64 ); + tr_bencFree( &top ); } } - tr_free( boundary ); + tr_ptrArrayDestruct( &parts, (PtrArrayForeachFunc)tr_mimepart_free ); /* use xml here because json responses to file uploads is trouble. * see http://www.malsup.com/jquery/form/#sample7 for details */ @@ -473,32 +554,6 @@ return FALSE; } -static char* -get_current_session_id( struct tr_rpc_server * server ) -{ - const time_t now = time( NULL ); - - if( !server->sessionId || ( now >= server->sessionIdExpiresAt ) ) - { - int i; - const int n = 48; - const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const size_t pool_size = strlen( pool ); - char * buf = tr_new( char, n+1 ); - - for( i=0; isessionId ); - server->sessionId = buf; - server->sessionIdExpiresAt = now + (60*60); /* expire in an hour */ - } - - return server->sessionId; -} - - static tr_bool test_session_id( struct tr_rpc_server * server, struct evhttp_request * req ) { @@ -567,6 +622,10 @@ { handle_clutch( req, server ); } + else if( !strncmp( req->uri, "/transmission/upload", 20 ) ) + { + handle_upload( req, server ); + } #ifdef REQUIRE_SESSION_ID else if( !test_session_id( server, req ) ) { @@ -593,10 +652,6 @@ { handle_rpc( req, server ); } - else if( !strncmp( req->uri, "/transmission/upload", 20 ) ) - { - handle_upload( req, server ); - } else { send_simple_response( req, HTTP_NOTFOUND, req->uri ); Index: web/javascript/transmission.js =================================================================== --- web/javascript/transmission.js (revision 8399) +++ web/javascript/transmission.js (revision 8400) @@ -1247,6 +1247,7 @@ } else { args.url = '/transmission/upload?paused=' + (this[Prefs._AutoStart] ? 'false' : 'true'); args.type = 'POST'; + args.data = { 'X-Transmission-Session-Id' : tr.remote._token }; args.dataType = 'xml'; args.iframe = true; args.success = function( data ) {