mirror of
https://github.com/transmission/transmission
synced 2025-01-03 05:25:52 +00:00
(trunk) revert r8351; it's not a sufficient fix
This commit is contained in:
parent
90cc4628e8
commit
872465d12e
4 changed files with 41 additions and 150 deletions
116
daemon/remote.c
116
daemon/remote.c
|
@ -132,7 +132,6 @@ static int reqCount = 0;
|
||||||
static int debug = 0;
|
static int debug = 0;
|
||||||
static char * auth = NULL;
|
static char * auth = NULL;
|
||||||
static char * netrc = NULL;
|
static char * netrc = NULL;
|
||||||
static char * cookies = NULL;
|
|
||||||
|
|
||||||
static char*
|
static char*
|
||||||
tr_getcwd( void )
|
tr_getcwd( void )
|
||||||
|
@ -1274,109 +1273,56 @@ processResponse( const char * host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* very basic handling of cookies: when we get Set-Cookie, throw out all
|
|
||||||
* the previous cookies... T only uses one cookie (session_id) */
|
|
||||||
static size_t
|
|
||||||
parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
|
|
||||||
{
|
|
||||||
const char * line = ptr;
|
|
||||||
const size_t linelen = size * nmemb;
|
|
||||||
const char * lineend = line + linelen;
|
|
||||||
const char * key = "Set-Cookie: ";
|
|
||||||
const size_t keylen = strlen( key );
|
|
||||||
if( ( linelen >= keylen ) && !memcmp( line, key, keylen ) ) {
|
|
||||||
const char * begin = line + keylen;
|
|
||||||
const char * end = begin;
|
|
||||||
while(( end!=lineend ) && !strchr("\r\n",*end))
|
|
||||||
++end;
|
|
||||||
tr_free( cookies );
|
|
||||||
cookies = tr_strndup( begin, end-begin );
|
|
||||||
}
|
|
||||||
|
|
||||||
return linelen;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CURL*
|
|
||||||
tr_curl_easy_init( struct evbuffer * writebuf )
|
|
||||||
{
|
|
||||||
CURL * curl = curl_easy_init( );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, parseResponseHeader );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_POST, 1 );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60L );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
|
|
||||||
#ifdef HAVE_ZLIB
|
|
||||||
curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
|
|
||||||
#endif
|
|
||||||
if( cookies )
|
|
||||||
curl_easy_setopt( curl, CURLOPT_COOKIE, cookies );
|
|
||||||
if( netrc )
|
|
||||||
curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
|
|
||||||
if( auth )
|
|
||||||
curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
|
|
||||||
return curl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
processRequests( const char * host,
|
processRequests( const char * host,
|
||||||
int port,
|
int port,
|
||||||
const char ** reqs,
|
const char ** reqs,
|
||||||
int reqCount )
|
int reqCount )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
CURL * curl = NULL;
|
CURL * curl;
|
||||||
struct evbuffer * buf = evbuffer_new( );
|
struct evbuffer * buf = evbuffer_new( );
|
||||||
char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port );
|
char * url = tr_strdup_printf(
|
||||||
|
"http://%s:%d/transmission/rpc", host, port );
|
||||||
|
|
||||||
for( i=0; i<reqCount; ++i )
|
curl = curl_easy_init( );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
|
||||||
|
#endif
|
||||||
|
curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_POST, 1 );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_URL, url );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
|
||||||
|
curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60L );
|
||||||
|
if( netrc )
|
||||||
|
curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
|
||||||
|
if( auth )
|
||||||
|
curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
|
||||||
|
|
||||||
|
for( i = 0; i < reqCount; ++i )
|
||||||
{
|
{
|
||||||
CURLcode res;
|
CURLcode res;
|
||||||
evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
|
|
||||||
|
|
||||||
if( curl == NULL )
|
|
||||||
{
|
|
||||||
curl = tr_curl_easy_init( buf );
|
|
||||||
curl_easy_setopt( curl, CURLOPT_URL, url );
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
|
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
|
||||||
|
|
||||||
if( debug )
|
if( debug )
|
||||||
fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
|
fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
|
||||||
if( ( res = curl_easy_perform( curl ) ) )
|
if( ( res = curl_easy_perform( curl ) ) )
|
||||||
tr_nerr( MY_NAME, "(%s:%d) %s", host, port, curl_easy_strerror( res ) );
|
tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
|
||||||
else {
|
curl_easy_strerror( res ) );
|
||||||
long response;
|
else
|
||||||
curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
|
processResponse( host, port, EVBUFFER_DATA(
|
||||||
switch( response ) {
|
buf ), EVBUFFER_LENGTH( buf ) );
|
||||||
case 200:
|
|
||||||
processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
|
evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
|
||||||
break;
|
|
||||||
case 409:
|
|
||||||
/* session_id cookie expired. by the time we reach line this our
|
|
||||||
* curl header func has already found the new session_id, so make
|
|
||||||
* a new CURL* and try again... */
|
|
||||||
curl_easy_cleanup( curl );
|
|
||||||
curl = NULL;
|
|
||||||
--i;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cleanup */
|
/* cleanup */
|
||||||
tr_free( url );
|
tr_free( url );
|
||||||
evbuffer_free( buf );
|
evbuffer_free( buf );
|
||||||
if( curl != NULL )
|
curl_easy_cleanup( curl );
|
||||||
curl_easy_cleanup( curl );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -56,7 +56,6 @@ struct tr_rpc_server
|
||||||
struct in_addr bindAddress;
|
struct in_addr bindAddress;
|
||||||
struct evhttp * httpd;
|
struct evhttp * httpd;
|
||||||
tr_session * session;
|
tr_session * session;
|
||||||
char * sessionId;
|
|
||||||
char * username;
|
char * username;
|
||||||
char * password;
|
char * password;
|
||||||
char * whitelistStr;
|
char * whitelistStr;
|
||||||
|
@ -451,48 +450,22 @@ isAddressAllowed( const tr_rpc_server * server,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char*
|
|
||||||
session_id_new( void )
|
|
||||||
{
|
|
||||||
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; i<n; ++i )
|
|
||||||
buf[i] = pool[ tr_cryptoRandInt( pool_size ) ];
|
|
||||||
buf[n] = '\0';
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static tr_bool
|
|
||||||
test_session_id( struct tr_rpc_server * server, struct evhttp_request * req )
|
|
||||||
{
|
|
||||||
char * needle = tr_strdup_printf( "session_id=%s", server->sessionId );
|
|
||||||
const char * haystack = evhttp_find_header( req->input_headers, "Cookie" );
|
|
||||||
const tr_bool success = (haystack!=NULL) && (strstr(haystack,needle)!=NULL);
|
|
||||||
tr_free( needle );
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_request( struct evhttp_request * req, void * arg )
|
handle_request( struct evhttp_request * req,
|
||||||
|
void * arg )
|
||||||
{
|
{
|
||||||
struct tr_rpc_server * server = arg;
|
struct tr_rpc_server * server = arg;
|
||||||
|
|
||||||
if( req && req->evcon )
|
if( req && req->evcon )
|
||||||
{
|
{
|
||||||
const char * auth;
|
const char * auth;
|
||||||
char * user = NULL;
|
char * user = NULL;
|
||||||
char * pass = NULL;
|
char * pass = NULL;
|
||||||
char * cookie;
|
|
||||||
|
|
||||||
evhttp_add_header( req->output_headers, "Server", MY_REALM );
|
evhttp_add_header( req->output_headers, "Server", MY_REALM );
|
||||||
cookie = tr_strdup_printf( "session_id=%s;Path=/;Discard", server->sessionId );
|
|
||||||
evhttp_add_header( req->output_headers, "Set-Cookie", cookie );
|
|
||||||
tr_free( cookie );
|
|
||||||
|
|
||||||
auth = evhttp_find_header( req->input_headers, "Authorization" );
|
auth = evhttp_find_header( req->input_headers, "Authorization" );
|
||||||
|
|
||||||
if( auth && !strncasecmp( auth, "basic ", 6 ) )
|
if( auth && !strncasecmp( auth, "basic ", 6 ) )
|
||||||
{
|
{
|
||||||
int plen;
|
int plen;
|
||||||
|
@ -506,7 +479,7 @@ handle_request( struct evhttp_request * req, void * arg )
|
||||||
|
|
||||||
if( !isAddressAllowed( server, req->remote_host ) )
|
if( !isAddressAllowed( server, req->remote_host ) )
|
||||||
{
|
{
|
||||||
send_simple_response( req, 403,
|
send_simple_response( req, 401,
|
||||||
"<p>Unauthorized IP Address.</p>"
|
"<p>Unauthorized IP Address.</p>"
|
||||||
"<p>Either disable the IP address whitelist or add your address to it.</p>"
|
"<p>Either disable the IP address whitelist or add your address to it.</p>"
|
||||||
"<p>If you're editing settings.json, see the 'rpc-whitelist' and 'rpc-whitelist-enabled' entries.</p>"
|
"<p>If you're editing settings.json, see the 'rpc-whitelist' and 'rpc-whitelist-enabled' entries.</p>"
|
||||||
|
@ -538,10 +511,6 @@ handle_request( struct evhttp_request * req, void * arg )
|
||||||
{
|
{
|
||||||
handle_clutch( req, server );
|
handle_clutch( req, server );
|
||||||
}
|
}
|
||||||
else if( !test_session_id( server, req ) )
|
|
||||||
{
|
|
||||||
send_simple_response( req, 409, "<p>Invalid session_id cookie.</p>" );
|
|
||||||
}
|
|
||||||
else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
|
else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
|
||||||
{
|
{
|
||||||
handle_rpc( req, server );
|
handle_rpc( req, server );
|
||||||
|
@ -797,7 +766,6 @@ tr_rpcInit( tr_session * session,
|
||||||
|
|
||||||
s = tr_new0( tr_rpc_server, 1 );
|
s = tr_new0( tr_rpc_server, 1 );
|
||||||
s->session = session;
|
s->session = session;
|
||||||
s->sessionId = session_id_new( );
|
|
||||||
|
|
||||||
found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_ENABLED, &boolVal );
|
found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_ENABLED, &boolVal );
|
||||||
assert( found );
|
assert( found );
|
||||||
|
|
|
@ -541,11 +541,7 @@ Session :: exec( const char * request )
|
||||||
QHttpRequestHeader header( "POST", path );
|
QHttpRequestHeader header( "POST", path );
|
||||||
header.setValue( "User-Agent", QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING );
|
header.setValue( "User-Agent", QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING );
|
||||||
header.setValue( "Content-Type", "application/json; charset=UTF-8" );
|
header.setValue( "Content-Type", "application/json; charset=UTF-8" );
|
||||||
if( !myCookies.isEmpty( ) )
|
myHttp.request( header, data, &myBuffer );
|
||||||
header.setValue( "Cookie", myCookies );
|
|
||||||
QBuffer * buf = new QBuffer;
|
|
||||||
buf->setData( data );
|
|
||||||
myHttp.request( header, buf, &myBuffer );
|
|
||||||
#ifdef DEBUG_HTTP
|
#ifdef DEBUG_HTTP
|
||||||
std::cerr << "sending " << qPrintable(header.toString()) << "\nBody:\n" << request << std::endl;
|
std::cerr << "sending " << qPrintable(header.toString()) << "\nBody:\n" << request << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
@ -565,9 +561,6 @@ Session :: onRequestFinished( int id, bool error )
|
||||||
{
|
{
|
||||||
Q_UNUSED( id );
|
Q_UNUSED( id );
|
||||||
|
|
||||||
QHttpResponseHeader response = myHttp.lastResponse();
|
|
||||||
QIODevice * sourceDevice = myHttp.currentSourceDevice( );
|
|
||||||
|
|
||||||
#ifdef DEBUG_HTTP
|
#ifdef DEBUG_HTTP
|
||||||
std::cerr << "http request " << id << " ended.. response header: "
|
std::cerr << "http request " << id << " ended.. response header: "
|
||||||
<< qPrintable( myHttp.lastResponse().toString() )
|
<< qPrintable( myHttp.lastResponse().toString() )
|
||||||
|
@ -576,32 +569,17 @@ Session :: onRequestFinished( int id, bool error )
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// very basic handling of cookies: when we get Set-Cookie, throw out all
|
if( error )
|
||||||
// the previous cookies... T only uses one cookie (session_id)
|
|
||||||
const QString responseCookies = response.value( "Set-Cookie" );
|
|
||||||
if( !responseCookies.isEmpty( ) )
|
|
||||||
myCookies = responseCookies;
|
|
||||||
|
|
||||||
if( response.statusCode() == 409 )
|
|
||||||
{
|
|
||||||
// we got a 409 telling us our session cookie has expired.
|
|
||||||
// now that we've updated our cookie, try again.
|
|
||||||
exec( qobject_cast<QBuffer*>(sourceDevice)->buffer().constData( ) );
|
|
||||||
}
|
|
||||||
else if( error )
|
|
||||||
{
|
|
||||||
std::cerr << "http error: " << qPrintable(myHttp.errorString()) << std::endl;
|
std::cerr << "http error: " << qPrintable(myHttp.errorString()) << std::endl;
|
||||||
}
|
else {
|
||||||
else
|
|
||||||
{
|
|
||||||
const QByteArray& response( myBuffer.buffer( ) );
|
const QByteArray& response( myBuffer.buffer( ) );
|
||||||
const char * json( response.constData( ) );
|
const char * json( response.constData( ) );
|
||||||
int jsonLength( response.size( ) );
|
int jsonLength( response.size( ) );
|
||||||
if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
|
if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
|
||||||
|
|
||||||
parseResponse( json, jsonLength );
|
parseResponse( json, jsonLength );
|
||||||
}
|
}
|
||||||
|
|
||||||
delete sourceDevice;
|
|
||||||
myBuffer.buffer( ).clear( );
|
myBuffer.buffer( ).clear( );
|
||||||
myBuffer.reset( );
|
myBuffer.reset( );
|
||||||
assert( myBuffer.bytesAvailable( ) < 1 );
|
assert( myBuffer.bytesAvailable( ) < 1 );
|
||||||
|
|
|
@ -132,7 +132,6 @@ class Session: public QObject
|
||||||
Prefs& myPrefs;
|
Prefs& myPrefs;
|
||||||
tr_session * mySession;
|
tr_session * mySession;
|
||||||
QString myConfigDir;
|
QString myConfigDir;
|
||||||
QString myCookies;
|
|
||||||
QUrl myUrl;
|
QUrl myUrl;
|
||||||
QBuffer myBuffer;
|
QBuffer myBuffer;
|
||||||
QHttp myHttp;
|
QHttp myHttp;
|
||||||
|
|
Loading…
Reference in a new issue