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

Last change on this file since 8149 was 8149, checked in by charles, 12 years ago

(trunk) If avahi's zeroconf implementation is available, use it to advertise our web ui

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