2008-05-18 16:44:30 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
2008-09-23 19:11:04 +00:00
|
|
|
* so that the bulk of its code can remain under the MIT license.
|
2008-05-18 16:44:30 +00:00
|
|
|
* This exemption does not extend to derived works not owned by
|
|
|
|
* the Transmission project.
|
|
|
|
*
|
2008-05-28 17:17:12 +00:00
|
|
|
* $Id$
|
2008-05-18 16:44:30 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
2008-06-04 17:14:58 +00:00
|
|
|
#include <ctype.h> /* isdigit */
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h> /* strtol */
|
2008-05-18 16:44:30 +00:00
|
|
|
#include <string.h>
|
2008-09-25 05:03:39 +00:00
|
|
|
#include <limits.h> /* INT_MAX */
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h> /* open */
|
2008-05-18 16:44:30 +00:00
|
|
|
|
2008-06-05 16:23:03 +00:00
|
|
|
#include <unistd.h> /* unlink */
|
|
|
|
|
2008-05-19 00:22:04 +00:00
|
|
|
#include <libevent/event.h>
|
2008-09-25 05:03:39 +00:00
|
|
|
#include <libevent/evhttp.h>
|
2008-05-18 16:44:30 +00:00
|
|
|
|
|
|
|
#include "transmission.h"
|
2008-06-30 21:11:53 +00:00
|
|
|
#include "bencode.h"
|
2008-07-16 17:47:20 +00:00
|
|
|
#include "list.h"
|
2008-07-11 04:07:14 +00:00
|
|
|
#include "platform.h"
|
2008-09-05 14:31:58 +00:00
|
|
|
#include "rpcimpl.h"
|
2008-05-18 16:44:30 +00:00
|
|
|
#include "rpc-server.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
2008-06-02 19:57:16 +00:00
|
|
|
#define MY_NAME "RPC Server"
|
2008-08-22 23:04:16 +00:00
|
|
|
#define MY_REALM "Transmission"
|
2008-06-02 19:57:16 +00:00
|
|
|
|
2008-07-16 17:47:20 +00:00
|
|
|
#define ACTIVE_INTERVAL_MSEC 40
|
2008-07-17 14:45:31 +00:00
|
|
|
#define INACTIVE_INTERVAL_MSEC 200
|
2008-05-18 16:44:30 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
|
|
|
|
|
|
|
|
struct acl_addr
|
|
|
|
{
|
|
|
|
char flag;
|
|
|
|
char parts[4][20];
|
|
|
|
};
|
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
struct tr_rpc_server
|
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
unsigned int isEnabled : 1;
|
|
|
|
unsigned int isPasswordEnabled : 1;
|
|
|
|
int port;
|
|
|
|
time_t lastRequestTime;
|
2008-09-25 05:03:39 +00:00
|
|
|
struct evhttp * httpd;
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_handle * session;
|
|
|
|
char * username;
|
|
|
|
char * password;
|
2008-09-25 05:03:39 +00:00
|
|
|
char * acl_str;
|
|
|
|
tr_list * acl_list; /* struct acl_addr */
|
2008-05-18 16:44:30 +00:00
|
|
|
};
|
|
|
|
|
2008-09-23 19:11:04 +00:00
|
|
|
#define dbgmsg( fmt... ) tr_deepLog( __FILE__, __LINE__, MY_NAME, ## fmt )
|
2008-06-05 04:02:46 +00:00
|
|
|
|
2008-06-30 21:11:53 +00:00
|
|
|
static const char*
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_memmem( const char * s1,
|
|
|
|
size_t l1,
|
|
|
|
const char * s2,
|
|
|
|
size_t l2 )
|
2008-06-30 21:11:53 +00:00
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
if( !l2 ) return s1;
|
|
|
|
while( l1 >= l2 )
|
|
|
|
{
|
2008-07-16 17:47:20 +00:00
|
|
|
l1--;
|
2008-09-23 19:11:04 +00:00
|
|
|
if( !memcmp( s1, s2, l2 ) )
|
2008-07-16 17:47:20 +00:00
|
|
|
return s1;
|
|
|
|
s1++;
|
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2008-07-16 17:47:20 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
/****
|
|
|
|
***** ACL UTILITIES
|
|
|
|
****/
|
2008-07-16 17:47:20 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static int
|
|
|
|
parseAddress( const char * addr, struct acl_addr * setme, const char ** end )
|
2008-07-16 17:47:20 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * pch;
|
|
|
|
|
|
|
|
memset( setme, 0, sizeof( struct acl_addr ) );
|
|
|
|
|
|
|
|
if( !addr ) return 0;
|
|
|
|
|
|
|
|
if(!((pch = strchr( addr, '.' )))) return 0;
|
|
|
|
if( pch-addr > 20 ) return 0;
|
|
|
|
memcpy( setme->parts[0], addr, pch-addr );
|
|
|
|
addr = pch + 1;
|
|
|
|
|
|
|
|
if(!((pch = strchr( addr, '.' )))) return 0;
|
|
|
|
if( pch-addr > 20 ) return 0;
|
|
|
|
memcpy( setme->parts[1], addr, pch-addr );
|
|
|
|
addr = pch + 1;
|
|
|
|
|
|
|
|
if(!((pch = strchr( addr, '.' )))) return 0;
|
|
|
|
if( pch-addr > 20 ) return 0;
|
|
|
|
memcpy( setme->parts[2], addr, pch-addr );
|
|
|
|
addr = pch + 1;
|
|
|
|
|
|
|
|
while( *pch && *pch!=',' ) ++pch;
|
|
|
|
if( pch-addr > 20 ) return 0;
|
|
|
|
memcpy( setme->parts[3], addr, pch-addr );
|
|
|
|
|
|
|
|
*end = pch;
|
|
|
|
return 1;
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static int
|
|
|
|
testAddress( const struct acl_addr * ref,
|
|
|
|
const struct acl_addr * testme )
|
2008-07-16 17:47:20 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
int i;
|
2008-07-16 17:47:20 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
for( i=0; i<4; ++i )
|
2008-07-16 17:47:20 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * a = ref->parts[i];
|
|
|
|
const char * b = testme->parts[i];
|
|
|
|
if( strcmp( a, "*" ) && strcmp( a, b ) ) return 0;
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
isAddressAllowed( const tr_rpc_server * server,
|
|
|
|
const char * address )
|
|
|
|
{
|
|
|
|
struct acl_addr tmp;
|
|
|
|
const char * end;
|
|
|
|
const int parsed = parseAddress( address, &tmp, &end );
|
|
|
|
tr_list * l;
|
|
|
|
|
|
|
|
if( !parsed )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for( l=server->acl_list; l; l=l->next ) {
|
|
|
|
const struct acl_addr * a = l->data;
|
|
|
|
if( testAddress( a, &tmp ) ) {
|
|
|
|
return a->flag == '+';
|
|
|
|
}
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
return 0;
|
2008-06-30 21:11:53 +00:00
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static tr_list*
|
|
|
|
parseACL( const char * acl, int * err )
|
2008-07-16 17:47:20 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
tr_list * list = NULL;
|
|
|
|
|
|
|
|
*err = 0;
|
|
|
|
|
|
|
|
for( ;; )
|
|
|
|
{
|
|
|
|
const char flag = *acl++;
|
|
|
|
struct acl_addr tmp;
|
2008-07-16 17:47:20 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( ( flag!='+' && flag!='-' ) || !parseAddress( acl, &tmp, &acl ) )
|
|
|
|
{
|
|
|
|
*err = 1;
|
|
|
|
tr_list_free( &list, tr_free );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp.flag = flag;
|
|
|
|
tr_list_append( &list, tr_memdup( &tmp, sizeof( struct acl_addr ) ) );
|
|
|
|
if( !*acl )
|
|
|
|
return list;
|
|
|
|
++acl;
|
|
|
|
}
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2008-06-30 21:11:53 +00:00
|
|
|
static void
|
2008-09-25 05:03:39 +00:00
|
|
|
handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
|
2008-06-30 21:11:53 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
if( req->type != EVHTTP_REQ_POST )
|
|
|
|
{
|
|
|
|
evhttp_send_reply( req, HTTP_BADREQUEST, "Bad Request", NULL );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-08-15 20:28:43 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * content_type = evhttp_find_header( req->input_headers, "Content-Type" );
|
2008-06-30 21:11:53 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * query = strchr( req->uri, '?' );
|
|
|
|
const int 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 );
|
|
|
|
|
|
|
|
char * boundary =
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_strdup_printf( "--%s", strstr( content_type,
|
|
|
|
"boundary=" ) +
|
|
|
|
strlen( "boundary=" ) );
|
2008-09-25 05:03:39 +00:00
|
|
|
const size_t boundary_len = strlen( boundary );
|
|
|
|
|
|
|
|
const char * delim = tr_memmem( in, inlen, boundary, boundary_len );
|
2008-06-30 21:11:53 +00:00
|
|
|
if( delim ) do
|
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
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 )
|
2008-06-30 21:11:53 +00:00
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
char * text = tr_strndup( part, part_len );
|
|
|
|
if( strstr( text, "filename=\"" ) )
|
2008-06-30 21:11:53 +00:00
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
const char * body = strstr( text, "\r\n\r\n" );
|
|
|
|
if( body )
|
|
|
|
{
|
|
|
|
char * b64, *json, *freeme;
|
|
|
|
int json_len;
|
|
|
|
size_t body_len;
|
|
|
|
tr_benc top, *args;
|
|
|
|
|
|
|
|
body += 4;
|
|
|
|
body_len = part_len - ( body - text );
|
|
|
|
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_bencDictAddInt( args, "paused", paused );
|
|
|
|
json = tr_bencSaveAsJSON( &top, &json_len );
|
|
|
|
freeme =
|
2008-09-25 05:03:39 +00:00
|
|
|
tr_rpc_request_exec_json( server->session,
|
|
|
|
json, json_len,
|
2008-09-23 19:11:04 +00:00
|
|
|
NULL );
|
|
|
|
|
|
|
|
tr_free( freeme );
|
|
|
|
tr_free( json );
|
|
|
|
tr_free( b64 );
|
|
|
|
tr_bencFree( &top );
|
|
|
|
}
|
2008-06-30 21:11:53 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_free( text );
|
2008-06-30 21:11:53 +00:00
|
|
|
}
|
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
while( delim );
|
2008-06-30 21:11:53 +00:00
|
|
|
|
|
|
|
tr_free( boundary );
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
/* use xml here because json responses to file uploads is trouble.
|
|
|
|
* see http://www.malsup.com/jquery/form/#sample7 for details */
|
|
|
|
evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=UTF-8" );
|
|
|
|
evhttp_send_reply( req, HTTP_OK, "Success", NULL );
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
2008-06-30 21:11:53 +00:00
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static const char*
|
|
|
|
mimetype_guess( const char * path )
|
2008-08-06 00:24:05 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
unsigned int i;
|
|
|
|
const struct {
|
|
|
|
const char * suffix;
|
|
|
|
const char * mime_type;
|
|
|
|
} types[] = {
|
|
|
|
/* these are just the ones we need for serving clutch... */
|
|
|
|
{ "css", "text/css" },
|
|
|
|
{ "gif", "image/gif" },
|
|
|
|
{ "html", "text/html" },
|
|
|
|
{ "ico", "image/vnd.microsoft.icon" },
|
|
|
|
{ "js", "application/javascript" },
|
|
|
|
{ "png", "image/png" }
|
|
|
|
};
|
|
|
|
const char * dot = strrchr( path, '.' );
|
|
|
|
|
|
|
|
for( i=0; dot && i<TR_N_ELEMENTS(types); ++i )
|
|
|
|
if( !strcmp( dot+1, types[i].suffix ) )
|
|
|
|
return types[i].mime_type;
|
|
|
|
|
|
|
|
return "application/octet-stream";
|
2008-08-06 00:24:05 +00:00
|
|
|
}
|
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
static void
|
2008-09-25 05:03:39 +00:00
|
|
|
serve_file( struct evhttp_request * req, const char * path )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
if( req->type != EVHTTP_REQ_GET )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
evhttp_add_header(req->output_headers, "Allow", "GET");
|
|
|
|
evhttp_send_reply(req, 405, "Method Not Allowed", NULL);
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
2008-09-25 05:03:39 +00:00
|
|
|
else
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
const int fd = open( path, O_RDONLY, 0 );
|
|
|
|
if( fd != -1 )
|
|
|
|
{
|
|
|
|
char size[12];
|
|
|
|
struct evbuffer * buf = evbuffer_new();
|
2008-05-18 16:44:30 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
evbuffer_read(buf, fd, INT_MAX );
|
|
|
|
evhttp_add_header(req->output_headers, "Content-Type", mimetype_guess( path ) );
|
|
|
|
snprintf(size, sizeof(size), "%zu", EVBUFFER_LENGTH( buf ) );
|
|
|
|
evhttp_add_header(req->output_headers, "Content-Length", size );
|
|
|
|
evhttp_send_reply(req, HTTP_OK, "OK", buf);
|
|
|
|
|
|
|
|
evbuffer_free(buf);
|
|
|
|
close( fd );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
struct evbuffer * buf = evbuffer_new();
|
|
|
|
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
|
|
|
|
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
|
|
|
|
evbuffer_free(buf);
|
|
|
|
}
|
2008-07-16 17:47:20 +00:00
|
|
|
}
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-09-25 05:03:39 +00:00
|
|
|
handle_clutch( struct evhttp_request * req, struct tr_rpc_server * server )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * uri;
|
|
|
|
struct evbuffer * buf = evbuffer_new( );
|
|
|
|
|
|
|
|
assert( !strncmp( req->uri, "/transmission/web/", 18 ) );
|
|
|
|
|
|
|
|
evbuffer_add_printf( buf, "%s%s", tr_getClutchDir( server->session ), TR_PATH_DELIMITER_STR );
|
|
|
|
uri = req->uri + 18;
|
|
|
|
if( !*uri || (*uri=='?') )
|
|
|
|
evbuffer_add_printf( buf, "index.html" );
|
|
|
|
else {
|
|
|
|
const char * pch;
|
|
|
|
if(( pch = strchr( uri, '?' )))
|
|
|
|
evbuffer_add_printf( buf, "%*.*s", (int)(pch-uri), (int)(pch-uri), uri );
|
|
|
|
else
|
|
|
|
evbuffer_add_printf( buf, "%s", uri );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( strstr( (const char *)EVBUFFER_DATA( buf ), ".." ) )
|
|
|
|
serve_file( req, "/dev/null" );
|
2008-06-10 19:25:18 +00:00
|
|
|
else
|
2008-09-25 05:03:39 +00:00
|
|
|
serve_file( req, (const char *)EVBUFFER_DATA( buf ) );
|
2008-05-18 16:44:30 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
evbuffer_free( buf );
|
2008-06-05 16:23:03 +00:00
|
|
|
}
|
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
static void
|
2008-09-25 05:03:39 +00:00
|
|
|
handle_rpc( struct evhttp_request * req, struct tr_rpc_server * server )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
int len = 0;
|
|
|
|
char * response;
|
|
|
|
struct evbuffer * buf;
|
2008-06-05 04:02:46 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( req->type == EVHTTP_REQ_GET )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * q;
|
|
|
|
if(( q = strchr( req->uri, '?' )))
|
|
|
|
response = tr_rpc_request_exec_uri( server->session,
|
|
|
|
q + 1,
|
|
|
|
strlen( q + 1 ),
|
|
|
|
&len );
|
|
|
|
}
|
|
|
|
else if( req->type == EVHTTP_REQ_POST )
|
|
|
|
{
|
|
|
|
response = tr_rpc_request_exec_json( server->session,
|
|
|
|
EVBUFFER_DATA( req->input_buffer ),
|
|
|
|
EVBUFFER_LENGTH( req->input_buffer ),
|
|
|
|
&len );
|
|
|
|
}
|
2008-06-05 16:23:03 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
buf = evbuffer_new( );
|
|
|
|
evbuffer_add( buf, response, len );
|
|
|
|
evhttp_add_header( req->output_headers, "Content-Type", "application/json; charset=UTF-8" );
|
|
|
|
evhttp_send_reply( req, HTTP_OK, "OK", buf );
|
|
|
|
evbuffer_free( buf );
|
|
|
|
}
|
2008-07-17 14:45:31 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static void
|
|
|
|
handle_request( struct evhttp_request * req, void * arg )
|
|
|
|
{
|
|
|
|
struct tr_rpc_server * server = arg;
|
2008-07-17 14:45:31 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if (req && req->evcon )
|
|
|
|
{
|
|
|
|
const char * auth;
|
|
|
|
char * user = NULL;
|
|
|
|
char * pass = NULL;
|
2008-07-17 14:45:31 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
evhttp_add_header( req->output_headers, "Server", "Transmission" );
|
|
|
|
|
|
|
|
auth = evhttp_find_header( req->input_headers, "Authorization" );
|
2008-07-17 14:45:31 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( auth && !strncasecmp( auth, "basic ", 6 ) )
|
|
|
|
{
|
|
|
|
int plen;
|
|
|
|
char * p = tr_base64_decode( auth + 6, 0, &plen );
|
|
|
|
if( p && plen && (( pass = strchr( p, ':' )))) {
|
|
|
|
user = p;
|
|
|
|
*pass++ = '\0';
|
|
|
|
}
|
|
|
|
}
|
2008-08-15 19:45:46 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( server->acl_list && !isAddressAllowed( server, req->remote_host ) )
|
2008-07-16 23:55:49 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
struct evbuffer * buf = evbuffer_new();
|
|
|
|
evbuffer_add_printf(buf, "<h1>Unauthorized IP Address</h1>");
|
|
|
|
evhttp_send_reply(req, 401, "Unauthorized", buf );
|
2008-07-11 17:09:53 +00:00
|
|
|
}
|
2008-09-25 05:03:39 +00:00
|
|
|
else if( server->isPasswordEnabled && ( !pass
|
|
|
|
|| !user
|
|
|
|
|| strcmp( server->username, user )
|
|
|
|
|| strcmp( server->password, pass ) ) )
|
2008-07-16 23:55:49 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
struct evbuffer * buf = evbuffer_new();
|
|
|
|
evhttp_add_header( req->output_headers,
|
|
|
|
"WWW-Authenticate", "Basic realm=\"Transmission\"");
|
|
|
|
evbuffer_add_printf(buf, "<h1>Unauthorized User</h1>");
|
|
|
|
evhttp_send_reply(req, 401, "Unauthorized", buf);
|
2008-06-05 04:02:46 +00:00
|
|
|
}
|
2008-09-25 05:03:39 +00:00
|
|
|
else if( !strcmp( req->uri, "/transmission/web" ) ||
|
|
|
|
!strcmp( req->uri, "/transmission/clutch" ) ||
|
|
|
|
!strcmp( req->uri, "/" ) )
|
2008-07-16 23:55:49 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
evhttp_add_header( req->output_headers, "Location", "/transmission/web/" );
|
|
|
|
evhttp_send_reply(req, HTTP_MOVEPERM, "Moved Permanently", NULL );
|
2008-06-05 16:23:03 +00:00
|
|
|
}
|
2008-09-25 05:03:39 +00:00
|
|
|
else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
|
|
|
|
{
|
|
|
|
handle_clutch( req, server );
|
|
|
|
}
|
|
|
|
else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
|
|
|
|
{
|
|
|
|
handle_rpc( req, server );
|
|
|
|
}
|
|
|
|
else if( !strncmp( req->uri, "/transmission/upload", 20 ) )
|
|
|
|
{
|
|
|
|
handle_upload( req, server );
|
|
|
|
}
|
|
|
|
else
|
2008-09-03 19:59:09 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
struct evbuffer *buf = evbuffer_new( );
|
|
|
|
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
|
|
|
|
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
|
2008-09-03 19:59:09 +00:00
|
|
|
}
|
2008-07-16 23:55:49 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
tr_free( user );
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
static void
|
|
|
|
startServer( tr_rpc_server * server )
|
|
|
|
{
|
|
|
|
dbgmsg( "in startServer; current context is %p", server->httpd );
|
|
|
|
|
|
|
|
if( !server->httpd )
|
|
|
|
if( ( server->httpd = evhttp_start( "0.0.0.0", server->port ) ) )
|
|
|
|
evhttp_set_gencb( server->httpd, handle_request, server );
|
|
|
|
}
|
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
static void
|
|
|
|
stopServer( tr_rpc_server * server )
|
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
if( server->httpd )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
evhttp_free( server->httpd );
|
|
|
|
server->httpd = NULL;
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetEnabled( tr_rpc_server * server,
|
|
|
|
int isEnabled )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-03 19:59:09 +00:00
|
|
|
server->isEnabled = isEnabled != 0;
|
2008-05-18 16:44:30 +00:00
|
|
|
|
2008-09-03 19:59:09 +00:00
|
|
|
if( !isEnabled )
|
|
|
|
stopServer( server );
|
|
|
|
else
|
2008-05-18 16:44:30 +00:00
|
|
|
startServer( server );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
tr_rpcIsEnabled( const tr_rpc_server * server )
|
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
return server->httpd != NULL;
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetPort( tr_rpc_server * server,
|
|
|
|
int port )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
|
|
|
if( server->port != port )
|
|
|
|
{
|
|
|
|
server->port = port;
|
|
|
|
|
2008-09-03 19:59:09 +00:00
|
|
|
if( server->isEnabled )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
|
|
|
stopServer( server );
|
|
|
|
startServer( server );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
tr_rpcGetPort( const tr_rpc_server * server )
|
|
|
|
{
|
|
|
|
return server->port;
|
|
|
|
}
|
|
|
|
|
2008-06-02 19:44:19 +00:00
|
|
|
int
|
2008-06-04 17:14:58 +00:00
|
|
|
tr_rpcTestACL( const tr_rpc_server * server UNUSED,
|
2008-09-23 19:11:04 +00:00
|
|
|
const char * acl,
|
|
|
|
char ** setme_errmsg )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
int err = 0;
|
|
|
|
tr_list * list = parseACL( acl, &err );
|
|
|
|
if( err )
|
|
|
|
*setme_errmsg = tr_strdup( "invalid ACL" );
|
|
|
|
tr_list_free( &list, tr_free );
|
2008-06-04 19:46:37 +00:00
|
|
|
return err;
|
2008-06-04 17:14:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetACL( tr_rpc_server * server,
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * acl_str,
|
2008-09-23 19:11:04 +00:00
|
|
|
char ** setme_errmsg )
|
2008-06-04 17:14:58 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
int err = 0;
|
|
|
|
tr_list * list = parseACL( acl_str, &err );
|
2008-06-04 17:14:58 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( err )
|
2008-06-02 19:44:19 +00:00
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
*setme_errmsg = tr_strdup( "invalid ACL" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tr_free( server->acl_str );
|
|
|
|
tr_list_free( &server->acl_list, tr_free );
|
|
|
|
server->acl_str = tr_strdup( acl_str );
|
|
|
|
server->acl_list = list;
|
2008-06-02 19:44:19 +00:00
|
|
|
}
|
|
|
|
|
2008-06-04 19:46:37 +00:00
|
|
|
return err;
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
|
2008-06-05 16:23:03 +00:00
|
|
|
char*
|
2008-05-18 16:44:30 +00:00
|
|
|
tr_rpcGetACL( const tr_rpc_server * server )
|
|
|
|
{
|
2008-09-25 05:03:39 +00:00
|
|
|
return tr_strdup( server->acl_str ? server->acl_str : "" );
|
2008-06-05 16:23:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/****
|
|
|
|
***** PASSWORD
|
|
|
|
****/
|
|
|
|
|
|
|
|
void
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetUsername( tr_rpc_server * server,
|
|
|
|
const char * username )
|
2008-06-05 16:23:03 +00:00
|
|
|
{
|
|
|
|
tr_free( server->username );
|
|
|
|
server->username = tr_strdup( username );
|
|
|
|
dbgmsg( "setting our Username to [%s]", server->username );
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcGetUsername( const tr_rpc_server * server )
|
2008-06-05 16:23:03 +00:00
|
|
|
{
|
|
|
|
return tr_strdup( server->username ? server->username : "" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetPassword( tr_rpc_server * server,
|
|
|
|
const char * password )
|
2008-06-05 16:23:03 +00:00
|
|
|
{
|
|
|
|
tr_free( server->password );
|
|
|
|
server->password = tr_strdup( password );
|
|
|
|
dbgmsg( "setting our Password to [%s]", server->password );
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcGetPassword( const tr_rpc_server * server )
|
2008-06-05 16:23:03 +00:00
|
|
|
{
|
|
|
|
return tr_strdup( server->password ? server->password : "" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcSetPasswordEnabled( tr_rpc_server * server,
|
|
|
|
int isEnabled )
|
2008-06-05 16:23:03 +00:00
|
|
|
{
|
|
|
|
server->isPasswordEnabled = isEnabled;
|
|
|
|
dbgmsg( "setting 'password enabled' to %d", isEnabled );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
tr_rpcIsPasswordEnabled( const tr_rpc_server * server )
|
|
|
|
{
|
|
|
|
return server->isPasswordEnabled;
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|
|
|
|
|
2008-06-05 16:23:03 +00:00
|
|
|
/****
|
|
|
|
***** LIFE CYCLE
|
|
|
|
****/
|
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
void
|
|
|
|
tr_rpcClose( tr_rpc_server ** ps )
|
|
|
|
{
|
|
|
|
tr_rpc_server * s = *ps;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
*ps = NULL;
|
|
|
|
|
|
|
|
stopServer( s );
|
2008-09-25 05:03:39 +00:00
|
|
|
tr_list_free( &s->acl_list, tr_free );
|
|
|
|
tr_free( s->acl_str );
|
2008-07-16 19:36:46 +00:00
|
|
|
tr_free( s->username );
|
|
|
|
tr_free( s->password );
|
2008-05-18 16:44:30 +00:00
|
|
|
tr_free( s );
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_rpc_server *
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_rpcInit( tr_handle * session,
|
|
|
|
int isEnabled,
|
|
|
|
int port,
|
2008-09-25 05:03:39 +00:00
|
|
|
const char * acl_str,
|
2008-09-23 19:11:04 +00:00
|
|
|
int isPasswordEnabled,
|
|
|
|
const char * username,
|
|
|
|
const char * password )
|
2008-05-18 16:44:30 +00:00
|
|
|
{
|
2008-06-02 19:57:16 +00:00
|
|
|
tr_rpc_server * s;
|
2008-09-25 05:03:39 +00:00
|
|
|
int err = 0;
|
|
|
|
tr_list * list = parseACL( acl_str, &err );
|
2008-06-02 19:57:16 +00:00
|
|
|
|
2008-09-25 05:03:39 +00:00
|
|
|
if( err ) {
|
|
|
|
acl_str = TR_DEFAULT_RPC_ACL;
|
|
|
|
tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl_str );
|
|
|
|
list = parseACL( acl_str, &err );
|
2008-06-02 19:57:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s = tr_new0( tr_rpc_server, 1 );
|
2008-05-18 16:44:30 +00:00
|
|
|
s->session = session;
|
|
|
|
s->port = port;
|
2008-09-25 05:03:39 +00:00
|
|
|
s->acl_str = tr_strdup( acl_str );
|
|
|
|
s->acl_list = list;
|
2008-06-05 16:23:03 +00:00
|
|
|
s->username = tr_strdup( username );
|
|
|
|
s->password = tr_strdup( password );
|
2008-09-03 19:59:09 +00:00
|
|
|
s->isPasswordEnabled = isPasswordEnabled != 0;
|
|
|
|
s->isEnabled = isEnabled != 0;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2008-05-18 16:44:30 +00:00
|
|
|
if( isEnabled )
|
|
|
|
startServer( s );
|
2008-09-23 19:11:04 +00:00
|
|
|
return s;
|
2008-05-18 16:44:30 +00:00
|
|
|
}
|