Changeset 6797


Ignore:
Timestamp:
Sep 25, 2008, 5:03:39 AM (13 years ago)
Author:
charles
Message:

(libT) experimental code to serve clutch/rpc via evhttpd

Location:
trunk
Files:
29 deleted
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/cli/Makefile.am

    r6302 r6797  
    1010transmissioncli_LDADD = \
    1111  $(top_builddir)/libtransmission/libtransmission.a \
    12   $(top_builddir)/third-party/libevent/libevent_core.la \
     12  $(top_builddir)/third-party/libevent/libevent.la \
    1313  $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    1414  $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    15   $(top_builddir)/third-party/shttpd/libshttpd.a \
    1615  $(INTLLIBS) \
    1716  $(OPENSSL_LIBS) \
  • trunk/configure.ac

    r6775 r6797  
    326326                 third-party/miniupnp/Makefile
    327327                 third-party/libnatpmp/Makefile
    328                  third-party/shttpd/Makefile
    329328                 macosx/Makefile
    330329                 wx/Makefile
  • trunk/daemon/Makefile.am

    r6297 r6797  
    1212LDADD = \
    1313    $(top_builddir)/libtransmission/libtransmission.a \
    14     $(top_builddir)/third-party/shttpd/libshttpd.a \
    1514    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    1615    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    17     $(top_builddir)/third-party/libevent/libevent_core.la \
     16    $(top_builddir)/third-party/libevent/libevent.la \
    1817    $(INTLLIBS) \
    1918    $(OPENSSL_LIBS) \
  • trunk/gtk/Makefile.am

    r6727 r6797  
    8383transmission_LDADD = \
    8484    $(top_builddir)/libtransmission/libtransmission.a \
    85     $(top_builddir)/third-party/libevent/libevent_core.la \
     85    $(top_builddir)/third-party/libevent/libevent.la \
    8686    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    8787    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    88     $(top_builddir)/third-party/shttpd/libshttpd.a \
    8988    $(GTK_LIBS) \
    9089    $(GIO_LIBS) \
  • trunk/libtransmission/Makefile.am

    r6703 r6797  
    110110APPS_LDADD = \
    111111    ./libtransmission.a  \
    112     $(top_builddir)/third-party/shttpd/libshttpd.a \
    113112    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    114113    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
  • trunk/libtransmission/rpc-server.c

    r6795 r6797  
    1616#include <stdlib.h> /* strtol */
    1717#include <string.h>
     18#include <limits.h> /* INT_MAX */
     19
     20#include <sys/types.h>
     21#include <sys/stat.h>
     22#include <fcntl.h> /* open */
    1823
    1924#include <unistd.h> /* unlink */
    2025
    2126#include <libevent/event.h>
    22 #include <shttpd/defs.h> /* edit_passwords */
    23 #include <shttpd/shttpd.h>
     27#include <libevent/evhttp.h>
    2428
    2529#include "transmission.h"
     
    3741#define INACTIVE_INTERVAL_MSEC 200
    3842
     43#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
     44
     45struct acl_addr
     46{
     47    char flag;
     48    char parts[4][20];
     49};
     50
    3951struct tr_rpc_server
    4052{
     
    4355    int                  port;
    4456    time_t               lastRequestTime;
    45     struct shttpd_ctx *  ctx;
     57    struct evhttp *      httpd;
    4658    tr_handle *          session;
    47     struct event         timer;
    4859    char *               username;
    4960    char *               password;
    50     char *               acl;
    51     tr_list *            connections;
     61    char *               acl_str;
     62    tr_list *            acl_list; /* struct acl_addr */
    5263};
    5364
     
    7283}
    7384
     85
     86/****
     87*****  ACL UTILITIES
     88****/
     89
     90static int
     91parseAddress( const char * addr, struct acl_addr * setme, const char ** end )
     92{
     93    const char * pch;
     94
     95    memset( setme, 0, sizeof( struct acl_addr ) );
     96
     97    if( !addr ) return 0;
     98
     99    if(!((pch = strchr( addr, '.' )))) return 0;
     100    if( pch-addr > 20 ) return 0;
     101    memcpy( setme->parts[0], addr, pch-addr );
     102    addr = pch + 1;
     103
     104    if(!((pch = strchr( addr, '.' )))) return 0;
     105    if( pch-addr > 20 ) return 0;
     106    memcpy( setme->parts[1], addr, pch-addr );
     107    addr = pch + 1;
     108
     109    if(!((pch = strchr( addr, '.' )))) return 0;
     110    if( pch-addr > 20 ) return 0;
     111    memcpy( setme->parts[2], addr, pch-addr );
     112    addr = pch + 1;
     113
     114    while( *pch && *pch!=',' ) ++pch;
     115    if( pch-addr > 20 ) return 0;
     116    memcpy( setme->parts[3], addr, pch-addr );
     117
     118    *end = pch;
     119    return 1;
     120}
     121
     122static int
     123testAddress( const struct acl_addr * ref,
     124             const struct acl_addr * testme )
     125{
     126    int i;
     127
     128    for( i=0; i<4; ++i )
     129    {
     130        const char * a = ref->parts[i];
     131        const char * b = testme->parts[i];
     132        if( strcmp( a, "*" ) && strcmp( a, b ) ) return 0;
     133    }
     134
     135    return 1;
     136}
     137
     138static int
     139isAddressAllowed( const tr_rpc_server * server,
     140                  const char * address )
     141{
     142    struct acl_addr tmp;
     143    const char * end;
     144    const int parsed = parseAddress( address, &tmp, &end );
     145    tr_list * l;
     146
     147    if( !parsed )
     148        return 0;
     149
     150    for( l=server->acl_list; l; l=l->next ) {
     151        const struct acl_addr * a = l->data;
     152        if( testAddress( a, &tmp ) ) {
     153            return a->flag == '+';
     154        }
     155    }
     156
     157    return 0;
     158}
     159
     160static tr_list*
     161parseACL( const char * acl, int * err )
     162{
     163    tr_list * list = NULL;
     164
     165    *err = 0;
     166
     167    for( ;; )
     168    {
     169        const char flag = *acl++;
     170        struct acl_addr tmp;
     171
     172        if( ( flag!='+' && flag!='-' ) || !parseAddress( acl, &tmp, &acl ) )
     173        {
     174            *err = 1;
     175            tr_list_free( &list, tr_free );
     176            return NULL;
     177        }
     178
     179        tmp.flag = flag;
     180        tr_list_append( &list, tr_memdup( &tmp, sizeof( struct acl_addr ) ) );
     181        if( !*acl )
     182            return list;
     183        ++acl;
     184    }
     185}
     186
    74187/**
    75188***
    76189**/
    77190
    78 struct ConnBuf
    79 {
    80     char *             key;
    81     time_t             lastActivity;
    82     struct evbuffer *  in;
    83     struct evbuffer *  out;
    84 };
    85 
    86 static char*
    87 buildKey( struct shttpd_arg * arg )
    88 {
    89     return tr_strdup_printf( "%s %s",
    90                             shttpd_get_env( arg, "REMOTE_ADDR" ),
    91                             shttpd_get_env( arg, "REQUEST_URI" ) );
    92 }
    93 
    94 static struct ConnBuf*
    95 getBuffer(
    96                         tr_rpc_server * server,
    97     struct shttpd_arg * arg )
    98 {
    99     tr_list *        l;
    100     char *           key = buildKey( arg );
    101     struct ConnBuf * found = NULL;
    102 
    103     for( l = server->connections; l && !found; l = l->next )
    104     {
    105         struct ConnBuf * buf = l->data;
    106         if( !strcmp( key, buf->key ) )
    107             found = buf;
    108     }
    109 
    110     if( found == NULL )
    111     {
    112         found = tr_new0( struct ConnBuf, 1 );
    113         found->lastActivity = time( NULL );
    114         found->key = tr_strdup( key );
    115         found->in = evbuffer_new( );
    116         found->out = evbuffer_new( );
    117         tr_list_append( &server->connections, found );
    118     }
    119 
    120     tr_free( key );
    121     return found;
    122 }
    123 
    124191static void
    125 pruneBuf( tr_rpc_server *  server,
    126           struct ConnBuf * buf )
    127 {
    128     tr_list_remove_data( &server->connections, buf );
    129 
    130     evbuffer_free( buf->in );
    131     evbuffer_free( buf->out );
    132     tr_free( buf->key );
    133     tr_free( buf );
    134 }
    135 
    136 /**
    137 ***
    138 **/
    139 
    140 static void
    141 handle_upload( struct shttpd_arg * arg )
    142 {
    143     struct tr_rpc_server * s;
    144     struct ConnBuf *       cbuf;
    145 
    146     s = arg->user_data;
    147     s->lastRequestTime = time( NULL );
    148     cbuf = getBuffer( s, arg );
    149 
    150     /* if we haven't parsed the POST, do that now */
    151     if( !EVBUFFER_LENGTH( cbuf->out ) )
    152     {
    153         const char * query_string;
    154         const char * content_type;
    155         const char * delim;
    156         const char * in;
    157         size_t       inlen;
    158         char *       boundary;
    159         size_t       boundary_len;
    160         char         buf[64];
    161         int          paused;
    162 
    163         /* if we haven't finished reading the POST, read more now */
    164         evbuffer_add( cbuf->in, arg->in.buf, arg->in.len );
    165         arg->in.num_bytes = arg->in.len;
    166         if( arg->flags & SHTTPD_MORE_POST_DATA )
    167             return;
    168 
    169         query_string = shttpd_get_env( arg, "QUERY_STRING" );
    170         content_type = shttpd_get_header( arg, "Content-Type" );
    171         in = (const char *) EVBUFFER_DATA( cbuf->in );
    172         inlen = EVBUFFER_LENGTH( cbuf->in );
    173         boundary =
     192handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
     193{
     194    if( req->type != EVHTTP_REQ_POST )
     195    {
     196        evhttp_send_reply( req, HTTP_BADREQUEST, "Bad Request", NULL );
     197    }
     198    else
     199    {
     200
     201        const char * content_type = evhttp_find_header( req->input_headers, "Content-Type" );
     202
     203        const char * query = strchr( req->uri, '?' );
     204        const int paused = query && strstr( query+1, "paused=true" );
     205
     206        const char * in = (const char *) EVBUFFER_DATA( req->input_buffer );
     207        size_t inlen = EVBUFFER_LENGTH( req->input_buffer );
     208
     209        char * boundary =
    174210            tr_strdup_printf( "--%s", strstr( content_type,
    175211                                              "boundary=" ) +
    176212                             strlen( "boundary=" ) );
    177         boundary_len = strlen( boundary );
    178         paused = ( query_string != NULL )
    179                  && ( shttpd_get_var( "paused", query_string,
    180                                      strlen( query_string ), buf,
    181                                      sizeof( buf ) ) == 4 )
    182                  && ( !strcmp( buf, "true" ) );
    183 
    184         delim = tr_memmem( in, inlen, boundary, boundary_len );
     213        const size_t boundary_len = strlen( boundary );
     214
     215        const char * delim = tr_memmem( in, inlen, boundary, boundary_len );
    185216        if( delim ) do
    186217            {
     
    220251                            json = tr_bencSaveAsJSON( &top, &json_len );
    221252                            freeme =
    222                                 tr_rpc_request_exec_json( s->session, json,
    223                                                           json_len,
     253                                tr_rpc_request_exec_json( server->session,
     254                                                          json, json_len,
    224255                                                          NULL );
    225256
     
    235266            while( delim );
    236267
    237         evbuffer_drain( cbuf->in, EVBUFFER_LENGTH( cbuf->in ) );
    238268        tr_free( boundary );
    239269
    240         {
    241             /* use xml here because json responses to file uploads is trouble.
    242              * see http://www.malsup.com/jquery/form/#sample7 for details */
    243             const char * response = "<result>success</result>";
    244             const int    len = strlen( response );
    245             evbuffer_add_printf(
    246                 cbuf->out, "HTTP/1.1 200 OK\r\n"
    247                            "Content-Type: text/xml; charset=UTF-8\r\n"
    248                            "Content-Length: %d\r\n"
    249                            "\r\n"
    250                            "%s\r\n", len, response );
    251         }
    252     }
    253 
    254     if( EVBUFFER_LENGTH( cbuf->out ) )
    255     {
    256         const int n = MIN( ( int )EVBUFFER_LENGTH(
    257                               cbuf->out ), arg->out.len );
    258         memcpy( arg->out.buf, EVBUFFER_DATA( cbuf->out ), n );
    259         evbuffer_drain( cbuf->out, n );
    260         arg->out.num_bytes = n;
    261     }
    262 
    263     if( !EVBUFFER_LENGTH( cbuf->out ) )
    264     {
    265         arg->flags |= SHTTPD_END_OF_OUTPUT;
    266         pruneBuf( s, cbuf );
    267     }
     270        /* use xml here because json responses to file uploads is trouble.
     271           * see http://www.malsup.com/jquery/form/#sample7 for details */
     272        evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=UTF-8" );
     273        evhttp_send_reply( req, HTTP_OK, "Success", NULL );
     274    }
     275}
     276
     277static const char*
     278mimetype_guess( const char * path )
     279{
     280    unsigned int i;
     281    const struct {
     282        const char * suffix;
     283        const char * mime_type;
     284    } types[] = {
     285        /* these are just the ones we need for serving clutch... */
     286        { "css", "text/css" },
     287        { "gif", "image/gif" },
     288        { "html", "text/html" },
     289        { "ico", "image/vnd.microsoft.icon" },
     290        { "js", "application/javascript" },
     291        { "png", "image/png" }
     292    };
     293    const char * dot = strrchr( path, '.' );
     294
     295    for( i=0; dot && i<TR_N_ELEMENTS(types); ++i )
     296        if( !strcmp( dot+1, types[i].suffix ) )
     297            return types[i].mime_type;
     298
     299    return "application/octet-stream";
    268300}
    269301
    270302static void
    271 handle_root( struct shttpd_arg * arg )
    272 {
    273     const char * redirect = "HTTP/1.1 200 OK" "\r\n"
    274                                               "Content-Type: text/html; charset=UTF-8"
    275                                               "\r\n"
    276                                               "\r\n"
    277                                               "<html><head>"
    278                                               "\r\n"
    279                                               "  <meta http-equiv=\"Refresh\" content=\"2; url=/transmission/web/\">"
    280                                               "\r\n"
    281                                               "</head><body>"
    282                                               "\r\n"
    283                                               "  <p>redirecting to <a href=\"/transmission/web\">/transmission/web/</a></p>"
    284                                               "\r\n"
    285                                               "</body></html>"
    286                                               "\r\n";
    287     const size_t n = strlen( redirect );
    288 
    289     memcpy( arg->out.buf, redirect, n );
    290     arg->in.num_bytes = arg->in.len;
    291     arg->out.num_bytes = n;
    292     arg->flags |= SHTTPD_END_OF_OUTPUT;
     303serve_file( struct evhttp_request * req, const char * path )
     304{
     305    if( req->type != EVHTTP_REQ_GET )
     306    {
     307        evhttp_add_header(req->output_headers, "Allow", "GET");
     308        evhttp_send_reply(req, 405, "Method Not Allowed", NULL);
     309    }
     310    else
     311    {
     312        const int fd = open( path, O_RDONLY, 0 );
     313        if( fd != -1 )
     314        {
     315            char size[12];
     316            struct evbuffer * buf = evbuffer_new();
     317
     318            evbuffer_read(buf, fd, INT_MAX );
     319            evhttp_add_header(req->output_headers, "Content-Type", mimetype_guess( path ) );
     320            snprintf(size, sizeof(size), "%zu", EVBUFFER_LENGTH( buf ) );
     321            evhttp_add_header(req->output_headers, "Content-Length", size );
     322            evhttp_send_reply(req, HTTP_OK, "OK", buf);
     323
     324            evbuffer_free(buf);
     325            close( fd );
     326        }
     327        else
     328        {
     329            struct evbuffer * buf = evbuffer_new();
     330            evbuffer_add_printf(buf, "<h1>Not Found</h1>");
     331            evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
     332            evbuffer_free(buf);
     333        }
     334    }
    293335}
    294336
    295337static void
    296 handle_rpc( struct shttpd_arg * arg )
    297 {
    298     struct tr_rpc_server * s;
    299     struct ConnBuf *       cbuf;
    300 
    301     s = arg->user_data;
    302     s->lastRequestTime = time( NULL );
    303     cbuf = getBuffer( s, arg );
    304 
    305     if( !EVBUFFER_LENGTH( cbuf->out ) )
    306     {
    307         int          len = 0;
    308         char *       response = NULL;
    309         const char * request_method = shttpd_get_env( arg, "REQUEST_METHOD" );
    310         const char * query_string = shttpd_get_env( arg, "QUERY_STRING" );
    311 
    312         if( query_string && *query_string )
    313             response = tr_rpc_request_exec_uri( s->session,
    314                                                 query_string,
    315                                                 strlen( query_string ),
     338handle_clutch( struct evhttp_request * req, struct tr_rpc_server * server )
     339{
     340    const char * uri;
     341    struct evbuffer * buf = evbuffer_new( );
     342
     343    assert( !strncmp( req->uri, "/transmission/web/", 18 ) );
     344
     345    evbuffer_add_printf( buf, "%s%s", tr_getClutchDir( server->session ), TR_PATH_DELIMITER_STR );
     346    uri = req->uri + 18;
     347    if( !*uri || (*uri=='?') )
     348        evbuffer_add_printf( buf, "index.html" );
     349    else {
     350        const char * pch;
     351        if(( pch = strchr( uri, '?' )))
     352            evbuffer_add_printf( buf, "%*.*s", (int)(pch-uri), (int)(pch-uri), uri );
     353        else
     354            evbuffer_add_printf( buf, "%s", uri );
     355    }
     356                             
     357    if( strstr( (const char *)EVBUFFER_DATA( buf ), ".." ) )
     358        serve_file( req, "/dev/null" );
     359    else
     360        serve_file( req, (const char *)EVBUFFER_DATA( buf ) );
     361
     362    evbuffer_free( buf );
     363}
     364
     365static void
     366handle_rpc( struct evhttp_request * req, struct tr_rpc_server * server )
     367{
     368    int len = 0;
     369    char * response;
     370    struct evbuffer * buf;
     371
     372    if( req->type == EVHTTP_REQ_GET )
     373    {
     374        const char * q;
     375        if(( q = strchr( req->uri, '?' )))
     376            response = tr_rpc_request_exec_uri( server->session,
     377                                                q + 1,
     378                                                strlen( q + 1 ),
    316379                                                &len );
    317         else if( !strcmp( request_method, "POST" ) )
    318         {
    319             evbuffer_add( cbuf->in, arg->in.buf, arg->in.len );
    320             arg->in.num_bytes = arg->in.len;
    321             if( arg->flags & SHTTPD_MORE_POST_DATA )
    322                 return;
    323             response = tr_rpc_request_exec_json( s->session,
    324                                                  EVBUFFER_DATA( cbuf->in ),
    325                                                  EVBUFFER_LENGTH( cbuf->in ),
    326                                                  &len );
    327             evbuffer_drain( cbuf->in, EVBUFFER_LENGTH( cbuf->in ) );
    328         }
    329 
    330         evbuffer_add_printf(
    331             cbuf->out, "HTTP/1.1 200 OK\r\n"
    332                        "Content-Type: application/json; charset=UTF-8\r\n"
    333                        "Content-Length: %d\r\n"
    334                        "\r\n"
    335                        "%*.*s", len, len, len, response );
    336         tr_free( response );
    337     }
    338 
    339     if( EVBUFFER_LENGTH( cbuf->out ) )
    340     {
    341         const int n = MIN( ( int )EVBUFFER_LENGTH(
    342                               cbuf->out ), arg->out.len );
    343         memcpy( arg->out.buf, EVBUFFER_DATA( cbuf->out ), n );
    344         evbuffer_drain( cbuf->out, n );
    345         arg->out.num_bytes = n;
    346     }
    347 
    348     if( !EVBUFFER_LENGTH( cbuf->out ) )
    349     {
    350         arg->flags |= SHTTPD_END_OF_OUTPUT;
    351         pruneBuf( s, cbuf );
    352     }
     380    }
     381    else if( req->type == EVHTTP_REQ_POST )
     382    {
     383        response = tr_rpc_request_exec_json( server->session,
     384                                             EVBUFFER_DATA( req->input_buffer ),
     385                                             EVBUFFER_LENGTH( req->input_buffer ),
     386                                             &len );
     387    }
     388
     389    buf = evbuffer_new( );
     390    evbuffer_add( buf, response, len );
     391    evhttp_add_header( req->output_headers, "Content-Type", "application/json; charset=UTF-8" );
     392    evhttp_send_reply( req, HTTP_OK, "OK", buf );
     393    evbuffer_free( buf );
    353394}
    354395
    355396static void
    356 rpcPulse( int socket   UNUSED,
    357           short action UNUSED,
    358           void *       vserver )
    359 {
    360     int             interval;
    361     struct timeval  tv;
    362     tr_rpc_server * server = vserver;
    363     const time_t    now = time( NULL );
    364 
    365     assert( server );
    366 
    367     if( server->ctx )
    368         shttpd_poll( server->ctx, 1 );
    369 
    370     /* set a timer for the next pulse */
    371     if( now - server->lastRequestTime < 300 )
    372         interval = ACTIVE_INTERVAL_MSEC;
    373     else
    374         interval = INACTIVE_INTERVAL_MSEC;
    375     tv = tr_timevalMsec( interval );
    376     evtimer_add( &server->timer, &tv );
    377 }
    378 
    379 static void
    380 getPasswordFile( tr_rpc_server * server,
    381                  char *          buf,
    382                  int             buflen )
    383 {
    384     tr_buildPath( buf, buflen, tr_sessionGetConfigDir( server->session ),
    385                   "htpasswd",
    386                   NULL );
     397handle_request( struct evhttp_request * req, void * arg )
     398{
     399    struct tr_rpc_server * server = arg;
     400
     401    if (req && req->evcon )
     402    {
     403        const char * auth;
     404        char * user = NULL;
     405        char * pass = NULL;
     406
     407        evhttp_add_header( req->output_headers, "Server", "Transmission" );
     408       
     409        auth = evhttp_find_header( req->input_headers, "Authorization" );
     410
     411        if( auth && !strncasecmp( auth, "basic ", 6 ) )
     412        {
     413            int plen;
     414            char * p = tr_base64_decode( auth + 6, 0, &plen );
     415            if( p && plen && (( pass = strchr( p, ':' )))) {
     416                user = p;
     417                *pass++ = '\0';
     418            }
     419        }
     420
     421        if( server->acl_list && !isAddressAllowed( server, req->remote_host ) )
     422        {
     423            struct evbuffer * buf = evbuffer_new();
     424            evbuffer_add_printf(buf, "<h1>Unauthorized IP Address</h1>");
     425            evhttp_send_reply(req, 401, "Unauthorized", buf );
     426        }
     427        else if( server->isPasswordEnabled && (    !pass
     428                                                || !user
     429                                                || strcmp( server->username, user )
     430                                                || strcmp( server->password, pass ) ) )
     431        {
     432            struct evbuffer * buf = evbuffer_new();
     433            evhttp_add_header( req->output_headers,
     434                "WWW-Authenticate", "Basic realm=\"Transmission\"");
     435            evbuffer_add_printf(buf, "<h1>Unauthorized User</h1>");
     436            evhttp_send_reply(req, 401, "Unauthorized", buf);
     437        }
     438        else if( !strcmp( req->uri, "/transmission/web" ) ||
     439                 !strcmp( req->uri, "/transmission/clutch" ) ||
     440                 !strcmp( req->uri, "/" ) )
     441        {
     442            evhttp_add_header( req->output_headers, "Location", "/transmission/web/" );
     443            evhttp_send_reply(req, HTTP_MOVEPERM, "Moved Permanently", NULL );
     444        }
     445        else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
     446        {
     447            handle_clutch( req, server );
     448        }
     449        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
     450        {
     451            handle_rpc( req, server );
     452        }
     453        else if( !strncmp( req->uri, "/transmission/upload", 20 ) )
     454        {
     455            handle_upload( req, server );
     456        }
     457        else
     458        {
     459            struct evbuffer *buf = evbuffer_new( );
     460            evbuffer_add_printf(buf, "<h1>Not Found</h1>");
     461            evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
     462        }
     463
     464        tr_free( user );
     465    }
    387466}
    388467
     
    390469startServer( tr_rpc_server * server )
    391470{
    392     dbgmsg( "in startServer; current context is %p", server->ctx );
    393 
    394     if( !server->ctx )
    395     {
    396         int            i;
    397         int            argc = 0;
    398         char *         argv[100];
    399         char           passwd[MAX_PATH_LENGTH];
    400         const char *   clutchDir = tr_getClutchDir( server->session );
    401         struct timeval tv = tr_timevalMsec( INACTIVE_INTERVAL_MSEC );
    402 
    403         getPasswordFile( server, passwd, sizeof( passwd ) );
    404         if( !server->isPasswordEnabled )
    405             unlink( passwd );
    406         else
    407             shttpd_edit_passwords( passwd, MY_REALM, server->username,
    408                                    server->password );
    409 
    410         argv[argc++] = tr_strdup( "appname-unused" );
    411 
    412         argv[argc++] = tr_strdup( "-ports" );
    413         argv[argc++] = tr_strdup_printf( "%d", server->port );
    414 
    415         argv[argc++] = tr_strdup( "-dir_list" );
    416         argv[argc++] = tr_strdup( "0" );
    417 
    418         argv[argc++] = tr_strdup( "-auth_realm" );
    419         argv[argc++] = tr_strdup( MY_REALM );
    420 
    421         argv[argc++] = tr_strdup( "-root" );
    422         argv[argc++] = tr_strdup( "/dev/null" );
    423 
    424         if( server->acl )
    425         {
    426             argv[argc++] = tr_strdup( "-acl" );
    427             argv[argc++] = tr_strdup( server->acl );
    428         }
    429         if( server->isPasswordEnabled )
    430         {
    431             argv[argc++] = tr_strdup( "-protect" );
    432             argv[argc++] = tr_strdup_printf( "/transmission=%s", passwd );
    433         }
    434         if( clutchDir && *clutchDir )
    435         {
    436             tr_inf( _(
    437                         "Serving the web interface files from \"%s\"" ),
    438                     clutchDir );
    439             argv[argc++] = tr_strdup( "-aliases" );
    440             argv[argc++] = tr_strdup_printf( "%s=%s,%s=%s",
    441                                              "/transmission/clutch",
    442                                              clutchDir,
    443                                              "/transmission/web",
    444                                              clutchDir );
    445         }
    446 
    447         argv[argc] = NULL; /* shttpd_init() wants it null-terminated */
    448 
    449         if( ( server->ctx = shttpd_init( argc, argv ) ) )
    450         {
    451             shttpd_register_uri( server->ctx, "/transmission/rpc",
    452                                  handle_rpc,
    453                                  server );
    454             shttpd_register_uri( server->ctx, "/transmission/upload",
    455                                  handle_upload,
    456                                  server );
    457             shttpd_register_uri( server->ctx, "/", handle_root, server );
    458 
    459             evtimer_set( &server->timer, rpcPulse, server );
    460             evtimer_add( &server->timer, &tv );
    461         }
    462 
    463         for( i = 0; i < argc; ++i )
    464             tr_free( argv[i] );
    465     }
     471    dbgmsg( "in startServer; current context is %p", server->httpd );
     472
     473    if( !server->httpd )
     474        if( ( server->httpd = evhttp_start( "0.0.0.0", server->port ) ) )
     475            evhttp_set_gencb( server->httpd, handle_request, server );
    466476}
    467477
     
    469479stopServer( tr_rpc_server * server )
    470480{
    471     if( server->ctx )
    472     {
    473         char passwd[MAX_PATH_LENGTH];
    474         getPasswordFile( server, passwd, sizeof( passwd ) );
    475         unlink( passwd );
    476 
    477         evtimer_del( &server->timer );
    478         shttpd_fini( server->ctx );
    479         server->ctx = NULL;
     481    if( server->httpd )
     482    {
     483        evhttp_free( server->httpd );
     484        server->httpd = NULL;
    480485    }
    481486}
     
    496501tr_rpcIsEnabled( const tr_rpc_server * server )
    497502{
    498     return server->ctx != NULL;
     503    return server->httpd != NULL;
    499504}
    500505
     
    519524{
    520525    return server->port;
    521 }
    522 
    523 /****
    524 *****  ACL
    525 ****/
    526 
    527 /*
    528  * FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
    529  * shttpd, written by Sergey Lyubka under this license:
    530  * "THE BEER-WARE LICENSE" (Revision 42):
    531  * Sergey Lyubka wrote this file.  As long as you retain this notice you
    532  * can do whatever you want with this stuff. If we meet some day, and you think
    533  * this stuff is worth it, you can buy me a beer in return.
    534  */
    535 
    536 #define FOR_EACH_WORD_IN_LIST( s, len )                                    \
    537     for( ; s != NULL && ( len = strcspn( s, DELIM_CHARS ) ) != 0;       \
    538                          s += len, s += strspn( s, DELIM_CHARS ) )
    539 
    540 static int
    541 isbyte( int n ) { return n >= 0 && n <= 255;  }
    542 
    543 static char*
    544 testACL( const char * s )
    545 {
    546     int len;
    547 
    548     FOR_EACH_WORD_IN_LIST( s, len )
    549     {
    550         char flag;
    551         int  a, b, c, d, n, mask;
    552 
    553         if( sscanf( s, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n ) != 5 )
    554             return tr_strdup_printf( _(
    555                                          "[%s]: subnet must be [+|-]x.x.x.x[/x]" ),
    556                                      s );
    557         if( flag != '+' && flag != '-' )
    558             return tr_strdup_printf( _( "[%s]: flag must be + or -" ), s );
    559         if( !isbyte( a ) || !isbyte( b ) || !isbyte( c ) || !isbyte( d ) )
    560             return tr_strdup_printf( _( "[%s]: bad ip address" ), s );
    561         if( sscanf( s + n, "/%d", &mask ) == 1 && ( mask < 0 || mask > 32 ) )
    562             return tr_strdup_printf( _( "[%s]: bad subnet mask %d" ), s, n );
    563     }
    564 
    565     return NULL;
    566 }
    567 
    568 /* 192.*.*.* --> 192.0.0.0/8
    569    192.64.*.* --> 192.64.0.0/16
    570    192.64.1.* --> 192.64.1.0/24
    571    192.64.1.2 --> 192.64.1.2/32 */
    572 static void
    573 cidrizeOne( const char *      in,
    574             int               len,
    575             struct evbuffer * out )
    576 {
    577     int          stars = 0;
    578     const char * pch;
    579     const char * end;
    580     char         zero = '0';
    581     char         huh = '?';
    582 
    583     for( pch = in, end = pch + len; pch != end; ++pch )
    584     {
    585         if( stars && isdigit( *pch ) )
    586             evbuffer_add( out, &huh, 1 );
    587         else if( *pch != '*' )
    588             evbuffer_add( out, pch, 1 );
    589         else
    590         {
    591             evbuffer_add( out, &zero, 1 );
    592             ++stars;
    593         }
    594     }
    595 
    596     evbuffer_add_printf( out, "/%d", ( 32 - ( stars * 8 ) ) );
    597 }
    598 
    599 char*
    600 cidrize( const char * acl )
    601 {
    602     int               len;
    603     const char *      walk = acl;
    604     char *            ret;
    605     struct evbuffer * out = evbuffer_new( );
    606 
    607     FOR_EACH_WORD_IN_LIST( walk, len )
    608     {
    609         cidrizeOne( walk, len, out );
    610         evbuffer_add_printf( out, "," );
    611     }
    612 
    613     /* the -1 is to eat the final ", " */
    614     ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) - 1 );
    615     evbuffer_free( out );
    616     return ret;
    617526}
    618527
     
    622531               char **                       setme_errmsg )
    623532{
    624     int    err = 0;
    625     char * cidr = cidrize( acl );
    626     char * errmsg = testACL( cidr );
    627 
    628     if( errmsg )
    629     {
    630         if( setme_errmsg )
    631             *setme_errmsg = errmsg;
    632         else
    633             tr_free( errmsg );
    634         err = -1;
    635     }
    636     tr_free( cidr );
     533    int err = 0;
     534    tr_list * list = parseACL( acl, &err );
     535    if( err )
     536        *setme_errmsg = tr_strdup( "invalid ACL" );
     537    tr_list_free( &list, tr_free );
    637538    return err;
    638539}
     
    640541int
    641542tr_rpcSetACL( tr_rpc_server * server,
    642               const char *    acl,
     543              const char *    acl_str,
    643544              char **         setme_errmsg )
    644545{
    645     char *    cidr = cidrize( acl );
    646     const int err = tr_rpcTestACL( server, cidr, setme_errmsg );
    647 
    648     if( !err )
    649     {
    650         const int isEnabled = server->isEnabled;
    651 
    652         if( isEnabled )
    653             stopServer( server );
    654 
    655         tr_free( server->acl );
    656         server->acl = tr_strdup( cidr );
    657         dbgmsg( "setting our ACL to [%s]", server->acl );
    658 
    659         if( isEnabled )
    660             startServer( server );
    661     }
    662     tr_free( cidr );
     546    int err = 0;
     547    tr_list * list = parseACL( acl_str, &err );
     548
     549    if( err )
     550    {
     551        *setme_errmsg = tr_strdup( "invalid ACL" );
     552    }
     553    else
     554    {
     555        tr_free( server->acl_str );
     556        tr_list_free( &server->acl_list, tr_free );
     557        server->acl_str = tr_strdup( acl_str );
     558        server->acl_list = list;
     559    }
    663560
    664561    return err;
     
    668565tr_rpcGetACL( const tr_rpc_server * server )
    669566{
    670     return tr_strdup( server->acl ? server->acl : "" );
     567    return tr_strdup( server->acl_str ? server->acl_str : "" );
    671568}
    672569
     
    679576                   const char *    username )
    680577{
    681     const int isEnabled = server->isEnabled;
    682 
    683     if( isEnabled )
    684         stopServer( server );
    685 
    686578    tr_free( server->username );
    687579    server->username = tr_strdup( username );
    688580    dbgmsg( "setting our Username to [%s]", server->username );
    689 
    690     if( isEnabled )
    691         startServer( server );
    692581}
    693582
     
    702591                   const char *    password )
    703592{
    704     const int isEnabled = server->isEnabled;
    705 
    706     if( isEnabled )
    707         stopServer( server );
    708 
    709593    tr_free( server->password );
    710594    server->password = tr_strdup( password );
    711595    dbgmsg( "setting our Password to [%s]", server->password );
    712 
    713     if( isEnabled )
    714         startServer( server );
    715596}
    716597
     
    725606                          int             isEnabled )
    726607{
    727     const int wasEnabled = server->isEnabled;
    728 
    729     if( wasEnabled )
    730         stopServer( server );
    731 
    732608    server->isPasswordEnabled = isEnabled;
    733609    dbgmsg( "setting 'password enabled' to %d", isEnabled );
    734 
    735     if( isEnabled )
    736         startServer( server );
    737610}
    738611
     
    755628
    756629    stopServer( s );
     630    tr_list_free( &s->acl_list, tr_free );
     631    tr_free( s->acl_str );
    757632    tr_free( s->username );
    758633    tr_free( s->password );
    759     tr_free( s->acl );
    760634    tr_free( s );
    761635}
     
    765639            int          isEnabled,
    766640            int          port,
    767             const char * acl,
     641            const char * acl_str,
    768642            int          isPasswordEnabled,
    769643            const char * username,
    770644            const char * password )
    771645{
    772     char *          errmsg;
    773646    tr_rpc_server * s;
    774 
    775     if( ( errmsg = testACL ( acl ) ) )
    776     {
    777         tr_nerr( MY_NAME, errmsg );
    778         tr_free( errmsg );
    779         acl = TR_DEFAULT_RPC_ACL;
    780         tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl );
     647    int err = 0;
     648    tr_list * list = parseACL( acl_str, &err );
     649
     650    if( err ) {
     651        acl_str = TR_DEFAULT_RPC_ACL;
     652        tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl_str );
     653        list = parseACL( acl_str, &err );
    781654    }
    782655
     
    784657    s->session = session;
    785658    s->port = port;
    786     s->acl = tr_strdup( acl );
     659    s->acl_str = tr_strdup( acl_str );
     660    s->acl_list = list;
    787661    s->username = tr_strdup( username );
    788662    s->password = tr_strdup( password );
     
    794668    return s;
    795669}
    796 
  • trunk/libtransmission/rpc-test.c

    r6795 r6797  
    2525    }
    2626
     27#if 0
    2728extern char* cidrize( const char * in );
    2829
     
    4344    return ok;
    4445}
     46#endif
    4547
    4648static int
    4749test_acl( void )
    4850{
     51#if 0
    4952    int    err;
    5053    char * errmsg = NULL;
     
    6871    check( !err );
    6972    check( !errmsg );
     73#endif
    7074
    7175    return 0;
  • trunk/third-party/Makefile.am

    r5822 r6797  
    22    libevent \
    33    libnatpmp \
    4     miniupnp \
    5     shttpd
     4    miniupnp
    65
    76EXTRA_DIST = \
  • trunk/wx/Makefile.am

    r6792 r6797  
    2323xmission_LDADD = \
    2424    $(top_builddir)/libtransmission/libtransmission.a \
    25     $(top_builddir)/third-party/libevent/libevent_core.la \
     25    $(top_builddir)/third-party/libevent/libevent.la \
    2626    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    2727    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    28     $(top_builddir)/third-party/shttpd/libshttpd.a \
    2928    $(WX_LIBS) \
    3029    $(OPENSSL_LIBS) \
Note: See TracChangeset for help on using the changeset viewer.