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

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

(1.5x) add a session_id cookie to the rpc server

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