source: branches/1.5x/libtransmission/rpc-server.c @ 8455

Last change on this file since 8455 was 8455, checked in by charles, 13 years ago

backports for 1.54.

ticketed items:

  • r8390 #1916: phantom 'verifying' appears if another verify in progress
  • r8396 #2061: Toolbar z-index should be less than menus, dialogs
  • r8400 #2073: can't upload new torrent file in web ui
  • r8402 #1451: make it clearer that the statusbar's ratio mode button is a button
  • r8418 #2085: in Web Client, torrents added by URL are always paused, regardless of preferences
  • r8419 #1451: make it clearer that the statusbar's ratio mode button is a button
  • r8428 #1188: comments and announce address are cut off
  • r8432 #2094: Torrent comment box does not scroll, so long comments are partially hidden

nonticketed items:

  • r8415: message #9 is for setting the peer's DHT listening port, not their BitTorrent? listening port.
  • r8427: fix _leftUntilDone not updating when it hits 0
  • r8399: fix small memory leaks in libtransmission and in gtk client
  • r8425: fix intltool build problem
  • r8391: remove dead code found by clang
  • r8412: speed up rpcimpl's torrentGet() by about 75%
  • r8426: in the web client, use the correct filterbar image and style it properly
  • Property svn:keywords set to Date Rev Author Id
File size: 25.7 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: rpc-server.c 8455 2009-05-20 21:05:48Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <string.h> /* memcpy */
16#include <limits.h> /* INT_MAX */
17
18#include <sys/types.h> /* open */
19#include <sys/stat.h>  /* open */
20#include <fcntl.h>     /* open */
21#include <unistd.h>    /* close */
22
23#ifdef HAVE_ZLIB
24 #include <zlib.h>
25#endif
26
27#include <libevent/event.h>
28#include <libevent/evhttp.h>
29
30#include "transmission.h"
31#include "bencode.h"
32#include "crypto.h"
33#include "list.h"
34#include "platform.h"
35#include "ptrarray.h"
36#include "rpcimpl.h"
37#include "rpc-server.h"
38#include "trevent.h"
39#include "utils.h"
40#include "web.h"
41
42/* session-id is used to make cross-site request forgery attacks difficult.
43 * Don't disable this feature unless you really know what you're doing!
44 * http://en.wikipedia.org/wiki/Cross-site_request_forgery
45 * http://shiflett.org/articles/cross-site-request-forgeries
46 * http://www.webappsec.org/lists/websecurity/archive/2008-04/msg00037.html */
47#define REQUIRE_SESSION_ID
48
49#define MY_NAME "RPC Server"
50#define MY_REALM "Transmission"
51#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
52
53#ifdef WIN32
54#define strncasecmp _strnicmp
55#endif
56
57struct tr_rpc_server
58{
59    tr_bool            isEnabled;
60    tr_bool            isPasswordEnabled;
61    tr_bool            isWhitelistEnabled;
62    tr_port            port;
63    struct evhttp *    httpd;
64    tr_session *       session;
65    char *             username;
66    char *             password;
67    char *             whitelistStr;
68    tr_list *          whitelist;
69
70    char *             sessionId;
71    time_t             sessionIdExpiresAt;
72
73#ifdef HAVE_ZLIB
74    z_stream           stream;
75#endif
76};
77
78#define dbgmsg( ... ) \
79    do { \
80        if( tr_deepLoggingIsActive( ) ) \
81            tr_deepLog( __FILE__, __LINE__, MY_NAME, __VA_ARGS__ ); \
82    } while( 0 )
83
84
85/***
86****
87***/
88
89static char*
90get_current_session_id( struct tr_rpc_server * server )
91{
92    const time_t now = time( NULL );
93
94    if( !server->sessionId || ( now >= server->sessionIdExpiresAt ) )
95    {
96        int i;
97        const int n = 48;
98        const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
99        const size_t pool_size = strlen( pool );
100        char * buf = tr_new( char, n+1 );
101
102        for( i=0; i<n; ++i )
103            buf[i] = pool[ tr_cryptoRandInt( pool_size ) ];
104        buf[n] = '\0';
105
106        tr_free( server->sessionId );
107        server->sessionId = buf;
108        server->sessionIdExpiresAt = now + (60*60); /* expire in an hour */
109    }
110
111    return server->sessionId;
112}
113
114
115/**
116***
117**/
118
119static void
120send_simple_response( struct evhttp_request * req,
121                      int                     code,
122                      const char *            text )
123{
124    const char *      code_text = tr_webGetResponseStr( code );
125    struct evbuffer * body = tr_getBuffer( );
126
127    evbuffer_add_printf( body, "<h1>%d: %s</h1>", code, code_text );
128    if( text )
129        evbuffer_add_printf( body, "%s", text );
130    evhttp_send_reply( req, code, code_text, body );
131
132    tr_releaseBuffer( body );
133}
134
135static const char*
136tr_memmem( const char * s1, size_t l1, /* haystack */
137           const char * s2, size_t l2 ) /* needle */
138{
139    if( !l2 ) return s1;
140    while( l1 >= l2 )
141    {
142        l1--;
143        if( !memcmp( s1, s2, l2 ) )
144            return s1;
145        s1++;
146    }
147
148    return NULL;
149}
150
151struct tr_mimepart
152{
153    char * headers;
154    int headers_len;
155    char * body;
156    int body_len;
157};
158
159static void
160tr_mimepart_free( struct tr_mimepart * p )
161{
162    tr_free( p->body );
163    tr_free( p->headers );
164    tr_free( p );
165}
166
167static void
168extract_parts_from_multipart( const struct evkeyvalq * headers,
169                              const struct evbuffer * body,
170                              tr_ptrArray * setme_parts )
171{
172    const char * content_type = evhttp_find_header( headers, "Content-Type" );
173    const char * in = (const char*) EVBUFFER_DATA( body );
174    size_t inlen = EVBUFFER_LENGTH( body );
175
176    const char * boundary_key = "boundary=";
177    const char * boundary_key_begin = strstr( content_type, boundary_key );
178    const char * boundary_val = boundary_key_begin ? boundary_key_begin + strlen( boundary_key ) : "arglebargle";
179    char * boundary = tr_strdup_printf( "--%s", boundary_val );
180    const size_t boundary_len = strlen( boundary );
181
182    const char * delim = tr_memmem( in, inlen, boundary, boundary_len );
183    while( delim )
184    {
185        size_t part_len;
186        const char * part = delim + boundary_len;
187
188        inlen -= ( part - in );
189        in = part;
190
191        delim = tr_memmem( in, inlen, boundary, boundary_len );
192        part_len = delim ? (size_t)( delim - part ) : inlen;
193
194        if( part_len )
195        {
196            const char * rnrn = tr_memmem( part, part_len, "\r\n\r\n", 4 );
197            if( rnrn )
198            {
199                struct tr_mimepart * p = tr_new( struct tr_mimepart, 1 );
200                p->headers_len = rnrn - part;
201                p->headers = tr_strndup( part, p->headers_len );
202                p->body_len = (part+part_len) - (rnrn + 4);
203                p->body = tr_strndup( rnrn+4, p->body_len );
204                tr_ptrArrayAppend( setme_parts, p );
205            }
206        }
207    }
208
209    tr_free( boundary );
210}
211
212static void
213handle_upload( struct evhttp_request * req,
214               struct tr_rpc_server *  server )
215{
216    if( req->type != EVHTTP_REQ_POST )
217    {
218        send_simple_response( req, 405, NULL );
219    }
220    else
221    {
222        int i;
223        int n;
224        tr_bool hasSessionId = FALSE;
225        tr_ptrArray parts = TR_PTR_ARRAY_INIT;
226
227        const char * query = strchr( req->uri, '?' );
228        const tr_bool paused = query && strstr( query + 1, "paused=true" );
229
230        extract_parts_from_multipart( req->input_headers, req->input_buffer, &parts );
231        n = tr_ptrArraySize( &parts );
232
233        /* first look for the session id */
234        for( i=0; i<n; ++i ) {
235            struct tr_mimepart * p = tr_ptrArrayNth( &parts, i );
236            if( tr_memmem( p->headers, p->headers_len, TR_RPC_SESSION_ID_HEADER, strlen( TR_RPC_SESSION_ID_HEADER ) ) )
237                break;
238        }
239        if( i<n ) {
240            const struct tr_mimepart * p = tr_ptrArrayNth( &parts, i );
241            const char * ours = get_current_session_id( server );
242            const int ourlen = strlen( ours );
243            hasSessionId = ourlen<=p->body_len && !memcmp( p->body, ours, ourlen );
244        }
245
246        if( !hasSessionId )
247        {
248            send_simple_response( req, 409, NULL );
249        }
250        else for( i=0; i<n; ++i )
251        {
252            struct tr_mimepart * p = tr_ptrArrayNth( &parts, i );
253            if( strstr( p->headers, "filename=\"" ) )
254            {
255                char * b64;
256                int body_len = p->body_len;
257                tr_benc top, *args;
258                const char * body = p->body;
259                struct evbuffer * json = evbuffer_new( );
260
261                if( body_len >= 2 && !memcmp( &body[body_len - 2], "\r\n", 2 ) )
262                    body_len -= 2;
263
264                tr_bencInitDict( &top, 2 );
265                args = tr_bencDictAddDict( &top, "arguments", 2 );
266                tr_bencDictAddStr( &top, "method", "torrent-add" );
267                b64 = tr_base64_encode( body, body_len, NULL );
268                tr_bencDictAddStr( args, "metainfo", b64 );
269                tr_bencDictAddInt( args, "paused", paused );
270                tr_bencSaveAsJSON( &top, json );
271                tr_rpc_request_exec_json( server->session,
272                                          EVBUFFER_DATA( json ),
273                                          EVBUFFER_LENGTH( json ),
274                                          NULL, NULL );
275
276                evbuffer_free( json );
277                tr_free( b64 );
278                tr_bencFree( &top );
279            }
280        }
281
282        tr_ptrArrayDestruct( &parts, (PtrArrayForeachFunc)tr_mimepart_free );
283
284        /* use xml here because json responses to file uploads is trouble.
285         * see http://www.malsup.com/jquery/form/#sample7 for details */
286        evhttp_add_header( req->output_headers, "Content-Type",
287                           "text/xml; charset=UTF-8" );
288        send_simple_response( req, HTTP_OK, NULL );
289    }
290}
291
292static const char*
293mimetype_guess( const char * path )
294{
295    unsigned int i;
296
297    const struct
298    {
299        const char *    suffix;
300        const char *    mime_type;
301    } types[] = {
302        /* these are just the ones we need for serving clutch... */
303        { "css",  "text/css"                  },
304        { "gif",  "image/gif"                 },
305        { "html", "text/html"                 },
306        { "ico",  "image/vnd.microsoft.icon"  },
307        { "js",   "application/javascript"    },
308        { "png",  "image/png"                 }
309    };
310    const char * dot = strrchr( path, '.' );
311
312    for( i = 0; dot && i < TR_N_ELEMENTS( types ); ++i )
313        if( !strcmp( dot + 1, types[i].suffix ) )
314            return types[i].mime_type;
315
316    return "application/octet-stream";
317}
318
319static void
320add_response( struct evhttp_request * req,
321              struct tr_rpc_server *  server,
322              struct evbuffer *       out,
323              const void *            content,
324              size_t                  content_len )
325{
326#ifndef HAVE_ZLIB
327    evbuffer_add( out, content, content_len );
328#else
329    const char * key = "Accept-Encoding";
330    const char * encoding = evhttp_find_header( req->input_headers, key );
331    const int do_deflate = encoding && strstr( encoding, "deflate" );
332
333    if( !do_deflate )
334    {
335        evbuffer_add( out, content, content_len );
336    }
337    else
338    {
339        int state;
340
341        server->stream.next_in = (Bytef*) content;
342        server->stream.avail_in = content_len;
343
344        /* allocate space for the raw data and call deflate() just once --
345         * we won't use the deflated data if it's longer than the raw data,
346         * so it's okay to let deflate() run out of output buffer space */
347        evbuffer_expand( out, content_len );
348        server->stream.next_out = EVBUFFER_DATA( out );
349        server->stream.avail_out = content_len;
350
351        state = deflate( &server->stream, Z_FINISH );
352
353        if( state == Z_STREAM_END )
354        {
355            EVBUFFER_LENGTH( out ) = content_len - server->stream.avail_out;
356
357            /* http://carsten.codimi.de/gzip.yaws/
358               It turns out that some browsers expect deflated data without
359               the first two bytes (a kind of header) and and the last four
360               bytes (an ADLER32 checksum). This format can of course
361               be produced by simply stripping these off. */
362            if( EVBUFFER_LENGTH( out ) >= 6 ) {
363                EVBUFFER_LENGTH( out ) -= 4;
364                evbuffer_drain( out, 2 );
365            }
366
367#if 0
368            tr_ninf( MY_NAME, _( "Deflated response from %zu bytes to %zu" ),
369                              content_len,
370                              EVBUFFER_LENGTH( out ) );
371#endif
372            evhttp_add_header( req->output_headers,
373                               "Content-Encoding", "deflate" );
374        }
375        else
376        {
377            evbuffer_drain( out, EVBUFFER_LENGTH( out ) );
378            evbuffer_add( out, content, content_len );
379        }
380
381        deflateReset( &server->stream );
382    }
383#endif
384}
385
386static void
387serve_file( struct evhttp_request * req,
388            struct tr_rpc_server *  server,
389            const char *            filename )
390{
391    if( req->type != EVHTTP_REQ_GET )
392    {
393        evhttp_add_header( req->output_headers, "Allow", "GET" );
394        send_simple_response( req, 405, NULL );
395    }
396    else
397    {
398        size_t content_len;
399        uint8_t * content;
400        const int error = errno;
401
402        errno = 0;
403        content_len = 0;
404        content = tr_loadFile( filename, &content_len );
405
406        if( errno )
407        {
408            send_simple_response( req, HTTP_NOTFOUND, filename );
409        }
410        else
411        {
412            struct evbuffer * out;
413
414            errno = error;
415            out = tr_getBuffer( );
416            evhttp_add_header( req->output_headers, "Content-Type",
417                               mimetype_guess( filename ) );
418            add_response( req, server, out, content, content_len );
419            evhttp_send_reply( req, HTTP_OK, "OK", out );
420
421            tr_releaseBuffer( out );
422            tr_free( content );
423        }
424    }
425}
426
427static void
428handle_clutch( struct evhttp_request * req,
429               struct tr_rpc_server *  server )
430{
431    const char * clutchDir = tr_getClutchDir( server->session );
432
433    assert( !strncmp( req->uri, "/transmission/web/", 18 ) );
434
435    if( !clutchDir || !*clutchDir )
436    {
437        send_simple_response( req, HTTP_NOTFOUND,
438            "<p>Couldn't find Transmission's web interface files!</p>"
439            "<p>Users: to tell Transmission where to look, "
440            "set the TRANSMISSION_WEB_HOME environmental "
441            "variable to the folder where the web interface's "
442            "index.html is located.</p>"
443            "<p>Package Builders: to set a custom default at compile time, "
444            "#define PACKAGE_DATA_DIR in libtransmission/platform.c "
445            "or tweak tr_getClutchDir() by hand.</p>" );
446    }
447    else
448    {
449        char * pch;
450        char * subpath;
451
452        subpath = tr_strdup( req->uri + 18 );
453        if(( pch = strchr( subpath, '?' )))
454            *pch = '\0';
455
456        if( strstr( subpath, ".." ) )
457        {
458            send_simple_response( req, HTTP_NOTFOUND, "<p>Tsk, tsk.</p>" );
459        }
460        else
461        {
462            char * filename = tr_strdup_printf( "%s%s%s",
463                                 clutchDir,
464                                 TR_PATH_DELIMITER_STR,
465                                 subpath && *subpath ? subpath : "index.html" );
466            serve_file( req, server, filename );
467            tr_free( filename );
468        }
469
470        tr_free( subpath );
471    }
472}
473
474struct rpc_response_data
475{
476    struct evhttp_request * req;
477    struct tr_rpc_server  * server;
478};
479
480static void
481rpc_response_func( tr_session      * session UNUSED,
482                   const char      * response,
483                   size_t            response_len,
484                   void            * user_data )
485{
486    struct rpc_response_data * data = user_data;
487    struct evbuffer * buf = tr_getBuffer( );
488
489    add_response( data->req, data->server, buf, response, response_len );
490    evhttp_add_header( data->req->output_headers,
491                           "Content-Type", "application/json; charset=UTF-8" );
492    evhttp_send_reply( data->req, HTTP_OK, "OK", buf );
493
494    tr_releaseBuffer( buf );
495    tr_free( data );
496}
497
498
499static void
500handle_rpc( struct evhttp_request * req,
501            struct tr_rpc_server  * server )
502{
503    struct rpc_response_data * data = tr_new0( struct rpc_response_data, 1 );
504
505    data->req = req;
506    data->server = server;
507   
508    if( req->type == EVHTTP_REQ_GET )
509    {
510        const char * q;
511        if( ( q = strchr( req->uri, '?' ) ) )
512            tr_rpc_request_exec_uri( server->session, q+1, -1, rpc_response_func, data );
513    }
514    else if( req->type == EVHTTP_REQ_POST )
515    {
516        tr_rpc_request_exec_json( server->session,
517                                  EVBUFFER_DATA( req->input_buffer ),
518                                  EVBUFFER_LENGTH( req->input_buffer ),
519                                  rpc_response_func, data );
520    }
521
522}
523
524static tr_bool
525isAddressAllowed( const tr_rpc_server * server,
526                  const char *          address )
527{
528    tr_list * l;
529
530    if( !server->isWhitelistEnabled )
531        return TRUE;
532
533    for( l=server->whitelist; l!=NULL; l=l->next )
534        if( tr_wildmat( address, l->data ) )
535            return TRUE;
536
537    return FALSE;
538}
539
540static tr_bool
541test_session_id( struct tr_rpc_server * server, struct evhttp_request * req )
542{
543    const char * ours = get_current_session_id( server );
544    const char * theirs = evhttp_find_header( req->input_headers, TR_RPC_SESSION_ID_HEADER );
545    const tr_bool success =  theirs && !strcmp( theirs, ours );
546    return success;
547}
548
549static void
550handle_request( struct evhttp_request * req, void * arg )
551{
552    struct tr_rpc_server * server = arg;
553
554    if( req && req->evcon )
555    {
556        const char * auth;
557        char * user = NULL;
558        char * pass = NULL;
559
560        evhttp_add_header( req->output_headers, "Server", MY_REALM );
561
562        auth = evhttp_find_header( req->input_headers, "Authorization" );
563        if( auth && !strncasecmp( auth, "basic ", 6 ) )
564        {
565            int    plen;
566            char * p = tr_base64_decode( auth + 6, 0, &plen );
567            if( p && plen && ( ( pass = strchr( p, ':' ) ) ) )
568            {
569                user = p;
570                *pass++ = '\0';
571            }
572        }
573
574        if( !isAddressAllowed( server, req->remote_host ) )
575        {
576            send_simple_response( req, 403,
577                "<p>Unauthorized IP Address.</p>"
578                "<p>Either disable the IP address whitelist or add your address to it.</p>"
579                "<p>If you're editing settings.json, see the 'rpc-whitelist' and 'rpc-whitelist-enabled' entries.</p>"
580                "<p>If you're still using ACLs, use a whitelist instead.  See the transmission-daemon manpage for details.</p>" );
581        }
582        else if( server->isPasswordEnabled
583                 && ( !pass || !user || strcmp( server->username, user )
584                                     || strcmp( server->password, pass ) ) )
585        {
586            evhttp_add_header( req->output_headers,
587                               "WWW-Authenticate",
588                               "Basic realm=\"" MY_REALM "\"" );
589            send_simple_response( req, 401, "Unauthorized User" );
590        }
591        else if( !strcmp( req->uri, "/transmission/web" )
592               || !strcmp( req->uri, "/transmission/clutch" )
593               || !strcmp( req->uri, "/" ) )
594        {
595            const char * protocol = "http";
596            const char * host = evhttp_find_header( req->input_headers, "Host" );
597            const char * uri = "transmission/web/";
598            char * location = tr_strdup_printf( "%s://%s/%s", protocol, host, uri );
599            evhttp_add_header( req->output_headers, "Location", location );
600            send_simple_response( req, HTTP_MOVEPERM, NULL );
601            tr_free( location );
602        }
603        else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
604        {
605            handle_clutch( req, server );
606        }
607        else if( !strncmp( req->uri, "/transmission/upload", 20 ) )
608        {
609            handle_upload( req, server );
610        }
611#ifdef REQUIRE_SESSION_ID
612        else if( !test_session_id( server, req ) )
613        {
614            const char * sessionId = get_current_session_id( server );
615            char * tmp = tr_strdup_printf(
616                "<p>Please add this header to your requests:</p>"
617                "<p><code>%s: %s</code></p>"
618                "<p>This requirement is to make "
619                "<a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a>"
620                " attacks more difficult.</p>",
621                TR_RPC_SESSION_ID_HEADER, sessionId );
622            evhttp_add_header( req->output_headers, TR_RPC_SESSION_ID_HEADER, sessionId );
623            send_simple_response( req, 409, tmp );
624            tr_free( tmp );
625        }
626#endif
627        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
628        {
629            handle_rpc( req, server );
630        }
631        else
632        {
633            send_simple_response( req, HTTP_NOTFOUND, req->uri );
634        }
635
636        tr_free( user );
637    }
638}
639
640static void
641startServer( void * vserver )
642{
643    tr_rpc_server * server  = vserver;
644
645    if( !server->httpd )
646    {
647        server->httpd = evhttp_new( tr_eventGetBase( server->session ) );
648        evhttp_bind_socket( server->httpd, "0.0.0.0", server->port );
649        evhttp_set_gencb( server->httpd, handle_request, server );
650    }
651}
652
653static void
654stopServer( tr_rpc_server * server )
655{
656    if( server->httpd )
657    {
658        evhttp_free( server->httpd );
659        server->httpd = NULL;
660    }
661}
662
663static void
664onEnabledChanged( void * vserver )
665{
666    tr_rpc_server * server = vserver;
667
668    if( !server->isEnabled )
669        stopServer( server );
670    else
671        startServer( server );
672}
673
674void
675tr_rpcSetEnabled( tr_rpc_server * server,
676                  tr_bool         isEnabled )
677{
678    server->isEnabled = isEnabled;
679
680    tr_runInEventThread( server->session, onEnabledChanged, server );
681}
682
683tr_bool
684tr_rpcIsEnabled( const tr_rpc_server * server )
685{
686    return server->isEnabled;
687}
688
689static void
690restartServer( void * vserver )
691{
692    tr_rpc_server * server = vserver;
693
694    if( server->isEnabled )
695    {
696        stopServer( server );
697        startServer( server );
698    }
699}
700
701void
702tr_rpcSetPort( tr_rpc_server * server,
703               tr_port         port )
704{
705    if( server->port != port )
706    {
707        server->port = port;
708
709        if( server->isEnabled )
710            tr_runInEventThread( server->session, restartServer, server );
711    }
712}
713
714tr_port
715tr_rpcGetPort( const tr_rpc_server * server )
716{
717    return server->port;
718}
719
720void
721tr_rpcSetWhitelist( tr_rpc_server * server,
722                    const char    * whitelistStr )
723{
724    void * tmp;
725    const char * walk;
726
727    /* keep the string */
728    tr_free( server->whitelistStr );
729    server->whitelistStr = tr_strdup( whitelistStr );
730
731    /* clear out the old whitelist entries */
732    while(( tmp = tr_list_pop_front( &server->whitelist )))
733        tr_free( tmp );
734
735    /* build the new whitelist entries */
736    for( walk=whitelistStr; walk && *walk; ) {
737        const char * delimiters = " ,;";
738        const size_t len = strcspn( walk, delimiters );
739        char * token = tr_strndup( walk, len );
740        tr_list_append( &server->whitelist, token );
741        if( strcspn( token, "+-" ) < len )
742            tr_ninf( MY_NAME, "Adding address to whitelist: %s (And it has a '+' or '-'!  Are you using an old ACL by mistake?)", token );
743        else
744            tr_ninf( MY_NAME, "Adding address to whitelist: %s", token );
745       
746        if( walk[len]=='\0' )
747            break;
748        walk += len + 1;
749    }
750}
751
752char*
753tr_rpcGetWhitelist( const tr_rpc_server * server )
754{
755    return tr_strdup( server->whitelistStr ? server->whitelistStr : "" );
756}
757
758void
759tr_rpcSetWhitelistEnabled( tr_rpc_server  * server,
760                           tr_bool          isEnabled )
761{
762    server->isWhitelistEnabled = isEnabled != 0;
763}
764
765tr_bool
766tr_rpcGetWhitelistEnabled( const tr_rpc_server * server )
767{
768    return server->isWhitelistEnabled;
769}
770
771/****
772*****  PASSWORD
773****/
774
775void
776tr_rpcSetUsername( tr_rpc_server * server,
777                   const char *    username )
778{
779    tr_free( server->username );
780    server->username = tr_strdup( username );
781    dbgmsg( "setting our Username to [%s]", server->username );
782}
783
784char*
785tr_rpcGetUsername( const tr_rpc_server * server )
786{
787    return tr_strdup( server->username ? server->username : "" );
788}
789
790void
791tr_rpcSetPassword( tr_rpc_server * server,
792                   const char *    password )
793{
794    tr_free( server->password );
795    server->password = tr_strdup( password );
796    dbgmsg( "setting our Password to [%s]", server->password );
797}
798
799char*
800tr_rpcGetPassword( const tr_rpc_server * server )
801{
802    return tr_strdup( server->password ? server->password : "" );
803}
804
805void
806tr_rpcSetPasswordEnabled( tr_rpc_server * server,
807                          tr_bool          isEnabled )
808{
809    server->isPasswordEnabled = isEnabled;
810    dbgmsg( "setting 'password enabled' to %d", (int)isEnabled );
811}
812
813tr_bool
814tr_rpcIsPasswordEnabled( const tr_rpc_server * server )
815{
816    return server->isPasswordEnabled;
817}
818
819/****
820*****  LIFE CYCLE
821****/
822
823static void
824closeServer( void * vserver )
825{
826    void * tmp;
827    tr_rpc_server * s = vserver;
828
829    stopServer( s );
830    while(( tmp = tr_list_pop_front( &s->whitelist )))
831        tr_free( tmp );
832#ifdef HAVE_ZLIB
833    deflateEnd( &s->stream );
834#endif
835    tr_free( s->sessionId );
836    tr_free( s->whitelistStr );
837    tr_free( s->username );
838    tr_free( s->password );
839    tr_free( s );
840}
841
842void
843tr_rpcClose( tr_rpc_server ** ps )
844{
845    tr_runInEventThread( ( *ps )->session, closeServer, *ps );
846    *ps = NULL;
847}
848
849tr_rpc_server *
850tr_rpcInit( tr_session  * session,
851            tr_benc * settings )
852{
853    tr_rpc_server * s;
854    tr_bool found;
855    int64_t i;
856    const char *str;
857
858    s = tr_new0( tr_rpc_server, 1 );
859    s->session = session;
860
861    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_ENABLED, &i );
862    assert( found );
863    s->isEnabled = i != 0;
864
865    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_PORT, &i );
866    assert( found );
867    s->port = i;
868
869    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, &i );
870    assert( found );
871    s->isWhitelistEnabled = i != 0;
872
873    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &i );
874    assert( found );
875    s->isPasswordEnabled = i != 0;
876
877    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_WHITELIST, &str );
878    assert( found );
879    tr_rpcSetWhitelist( s, str ? str : "127.0.0.1" );
880
881    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_USERNAME, &str );
882    assert( found );
883    s->username = tr_strdup( str );
884
885    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_PASSWORD, &str );
886    assert( found );
887    s->password = tr_strdup( str );
888
889#ifdef HAVE_ZLIB
890    s->stream.zalloc = (alloc_func) Z_NULL;
891    s->stream.zfree = (free_func) Z_NULL;
892    s->stream.opaque = (voidpf) Z_NULL;
893    deflateInit( &s->stream, Z_BEST_COMPRESSION );
894#endif
895
896    if( s->isEnabled )
897    {
898        tr_ninf( MY_NAME, _( "Serving RPC and Web requests on port %d" ), (int) s->port );
899        tr_runInEventThread( session, startServer, s );
900
901        if( s->isWhitelistEnabled )
902            tr_ninf( MY_NAME, _( "Whitelist enabled" ) );
903
904        if( s->isPasswordEnabled )
905            tr_ninf( MY_NAME, _( "Password required" ) );
906    }
907
908    return s;
909}
Note: See TracBrowser for help on using the repository browser.