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

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

(trunk libT) fix long-standing wart of a few public API calls returning strdup'ed char*, rather than a const char* like all the others

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