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

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

(trunk 1.5x) make sure that 1.5x's 409 response message reads the same as 1.6x's. reported by blueluna

  • Property svn:keywords set to Date Rev Author Id
File size: 26.3 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 8491 2009-05-23 14:50:04Z 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 HTTP requests:</p>"
617                "<p style=\"padding-left: 20pt;\"><code>%s: %s</code></p>"
618                "<p><b>RPC Application Developers:</b></p>"
619                "<p style=\"padding-left: 20pt;\">As of Transmission 1.53 and 1.61, RPC clients "
620                "need to look for this 409 response containing the phrase \"invalid session-id\".  "
621                "It occurs when the request's "TR_RPC_SESSION_ID_HEADER" header was missing "
622                "(such as during bootstrapping) or expired. "
623                "Either way, you can parse this response's headers for the new session-id.</p>"
624                "<p style=\"padding-left: 20pt;\">This requirement has been added to make "
625                "<a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF</a>"
626                " attacks more difficult.</p>",
627                TR_RPC_SESSION_ID_HEADER, sessionId );
628            evhttp_add_header( req->output_headers, TR_RPC_SESSION_ID_HEADER, sessionId );
629            send_simple_response( req, 409, tmp );
630            tr_free( tmp );
631        }
632#endif
633        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
634        {
635            handle_rpc( req, server );
636        }
637        else
638        {
639            send_simple_response( req, HTTP_NOTFOUND, req->uri );
640        }
641
642        tr_free( user );
643    }
644}
645
646static void
647startServer( void * vserver )
648{
649    tr_rpc_server * server  = vserver;
650
651    if( !server->httpd )
652    {
653        server->httpd = evhttp_new( tr_eventGetBase( server->session ) );
654        evhttp_bind_socket( server->httpd, "0.0.0.0", server->port );
655        evhttp_set_gencb( server->httpd, handle_request, server );
656    }
657}
658
659static void
660stopServer( tr_rpc_server * server )
661{
662    if( server->httpd )
663    {
664        evhttp_free( server->httpd );
665        server->httpd = NULL;
666    }
667}
668
669static void
670onEnabledChanged( void * vserver )
671{
672    tr_rpc_server * server = vserver;
673
674    if( !server->isEnabled )
675        stopServer( server );
676    else
677        startServer( server );
678}
679
680void
681tr_rpcSetEnabled( tr_rpc_server * server,
682                  tr_bool         isEnabled )
683{
684    server->isEnabled = isEnabled;
685
686    tr_runInEventThread( server->session, onEnabledChanged, server );
687}
688
689tr_bool
690tr_rpcIsEnabled( const tr_rpc_server * server )
691{
692    return server->isEnabled;
693}
694
695static void
696restartServer( void * vserver )
697{
698    tr_rpc_server * server = vserver;
699
700    if( server->isEnabled )
701    {
702        stopServer( server );
703        startServer( server );
704    }
705}
706
707void
708tr_rpcSetPort( tr_rpc_server * server,
709               tr_port         port )
710{
711    if( server->port != port )
712    {
713        server->port = port;
714
715        if( server->isEnabled )
716            tr_runInEventThread( server->session, restartServer, server );
717    }
718}
719
720tr_port
721tr_rpcGetPort( const tr_rpc_server * server )
722{
723    return server->port;
724}
725
726void
727tr_rpcSetWhitelist( tr_rpc_server * server,
728                    const char    * whitelistStr )
729{
730    void * tmp;
731    const char * walk;
732
733    /* keep the string */
734    tr_free( server->whitelistStr );
735    server->whitelistStr = tr_strdup( whitelistStr );
736
737    /* clear out the old whitelist entries */
738    while(( tmp = tr_list_pop_front( &server->whitelist )))
739        tr_free( tmp );
740
741    /* build the new whitelist entries */
742    for( walk=whitelistStr; walk && *walk; ) {
743        const char * delimiters = " ,;";
744        const size_t len = strcspn( walk, delimiters );
745        char * token = tr_strndup( walk, len );
746        tr_list_append( &server->whitelist, token );
747        if( strcspn( token, "+-" ) < len )
748            tr_ninf( MY_NAME, "Adding address to whitelist: %s (And it has a '+' or '-'!  Are you using an old ACL by mistake?)", token );
749        else
750            tr_ninf( MY_NAME, "Adding address to whitelist: %s", token );
751       
752        if( walk[len]=='\0' )
753            break;
754        walk += len + 1;
755    }
756}
757
758char*
759tr_rpcGetWhitelist( const tr_rpc_server * server )
760{
761    return tr_strdup( server->whitelistStr ? server->whitelistStr : "" );
762}
763
764void
765tr_rpcSetWhitelistEnabled( tr_rpc_server  * server,
766                           tr_bool          isEnabled )
767{
768    server->isWhitelistEnabled = isEnabled != 0;
769}
770
771tr_bool
772tr_rpcGetWhitelistEnabled( const tr_rpc_server * server )
773{
774    return server->isWhitelistEnabled;
775}
776
777/****
778*****  PASSWORD
779****/
780
781void
782tr_rpcSetUsername( tr_rpc_server * server,
783                   const char *    username )
784{
785    tr_free( server->username );
786    server->username = tr_strdup( username );
787    dbgmsg( "setting our Username to [%s]", server->username );
788}
789
790char*
791tr_rpcGetUsername( const tr_rpc_server * server )
792{
793    return tr_strdup( server->username ? server->username : "" );
794}
795
796void
797tr_rpcSetPassword( tr_rpc_server * server,
798                   const char *    password )
799{
800    tr_free( server->password );
801    server->password = tr_strdup( password );
802    dbgmsg( "setting our Password to [%s]", server->password );
803}
804
805char*
806tr_rpcGetPassword( const tr_rpc_server * server )
807{
808    return tr_strdup( server->password ? server->password : "" );
809}
810
811void
812tr_rpcSetPasswordEnabled( tr_rpc_server * server,
813                          tr_bool          isEnabled )
814{
815    server->isPasswordEnabled = isEnabled;
816    dbgmsg( "setting 'password enabled' to %d", (int)isEnabled );
817}
818
819tr_bool
820tr_rpcIsPasswordEnabled( const tr_rpc_server * server )
821{
822    return server->isPasswordEnabled;
823}
824
825/****
826*****  LIFE CYCLE
827****/
828
829static void
830closeServer( void * vserver )
831{
832    void * tmp;
833    tr_rpc_server * s = vserver;
834
835    stopServer( s );
836    while(( tmp = tr_list_pop_front( &s->whitelist )))
837        tr_free( tmp );
838#ifdef HAVE_ZLIB
839    deflateEnd( &s->stream );
840#endif
841    tr_free( s->sessionId );
842    tr_free( s->whitelistStr );
843    tr_free( s->username );
844    tr_free( s->password );
845    tr_free( s );
846}
847
848void
849tr_rpcClose( tr_rpc_server ** ps )
850{
851    tr_runInEventThread( ( *ps )->session, closeServer, *ps );
852    *ps = NULL;
853}
854
855tr_rpc_server *
856tr_rpcInit( tr_session  * session,
857            tr_benc * settings )
858{
859    tr_rpc_server * s;
860    tr_bool found;
861    int64_t i;
862    const char *str;
863
864    s = tr_new0( tr_rpc_server, 1 );
865    s->session = session;
866
867    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_ENABLED, &i );
868    assert( found );
869    s->isEnabled = i != 0;
870
871    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_PORT, &i );
872    assert( found );
873    s->port = i;
874
875    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, &i );
876    assert( found );
877    s->isWhitelistEnabled = i != 0;
878
879    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &i );
880    assert( found );
881    s->isPasswordEnabled = i != 0;
882
883    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_WHITELIST, &str );
884    assert( found );
885    tr_rpcSetWhitelist( s, str ? str : "127.0.0.1" );
886
887    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_USERNAME, &str );
888    assert( found );
889    s->username = tr_strdup( str );
890
891    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_PASSWORD, &str );
892    assert( found );
893    s->password = tr_strdup( str );
894
895#ifdef HAVE_ZLIB
896    s->stream.zalloc = (alloc_func) Z_NULL;
897    s->stream.zfree = (free_func) Z_NULL;
898    s->stream.opaque = (voidpf) Z_NULL;
899    deflateInit( &s->stream, Z_BEST_COMPRESSION );
900#endif
901
902    if( s->isEnabled )
903    {
904        tr_ninf( MY_NAME, _( "Serving RPC and Web requests on port %d" ), (int) s->port );
905        tr_runInEventThread( session, startServer, s );
906
907        if( s->isWhitelistEnabled )
908            tr_ninf( MY_NAME, _( "Whitelist enabled" ) );
909
910        if( s->isPasswordEnabled )
911            tr_ninf( MY_NAME, _( "Password required" ) );
912    }
913
914    return s;
915}
Note: See TracBrowser for help on using the repository browser.