source: trunk/libtransmission/rpc-server.c @ 8351

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

(trunk libT) add a session_id cookie to the rpc server

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