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

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

(trunk libT) more informative 404 error message when we're unable to load a clutch file

  • Property svn:keywords set to Date Rev Author Id
File size: 23.5 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 8340 2009-05-05 22:51:12Z 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            char * tmp = tr_strdup_printf( "%s (%s)", filename, tr_strerror( errno ) );
320            send_simple_response( req, HTTP_NOTFOUND, tmp );
321            tr_free( tmp );
322        }
323        else
324        {
325            struct evbuffer * out;
326
327            errno = error;
328            out = tr_getBuffer( );
329            evhttp_add_header( req->output_headers, "Content-Type",
330                               mimetype_guess( filename ) );
331            add_response( req, server, out, content, content_len );
332            evhttp_send_reply( req, HTTP_OK, "OK", out );
333
334            tr_releaseBuffer( out );
335            tr_free( content );
336        }
337    }
338}
339
340static void
341handle_clutch( struct evhttp_request * req,
342               struct tr_rpc_server *  server )
343{
344    const char * clutchDir = tr_getClutchDir( server->session );
345
346    assert( !strncmp( req->uri, "/transmission/web/", 18 ) );
347
348    if( !clutchDir || !*clutchDir )
349    {
350        send_simple_response( req, HTTP_NOTFOUND,
351            "<p>Couldn't find Transmission's web interface files!</p>"
352            "<p>Users: to tell Transmission where to look, "
353            "set the TRANSMISSION_WEB_HOME environmental "
354            "variable to the folder where the web interface's "
355            "index.html is located.</p>"
356            "<p>Package Builders: to set a custom default at compile time, "
357            "#define PACKAGE_DATA_DIR in libtransmission/platform.c "
358            "or tweak tr_getClutchDir() by hand.</p>" );
359    }
360    else
361    {
362        char * pch;
363        char * subpath;
364
365        subpath = tr_strdup( req->uri + 18 );
366        if(( pch = strchr( subpath, '?' )))
367            *pch = '\0';
368
369        if( strstr( subpath, ".." ) )
370        {
371            send_simple_response( req, HTTP_NOTFOUND, "<p>Tsk, tsk.</p>" );
372        }
373        else
374        {
375            char * filename = tr_strdup_printf( "%s%s%s",
376                                 clutchDir,
377                                 TR_PATH_DELIMITER_STR,
378                                 subpath && *subpath ? subpath : "index.html" );
379            serve_file( req, server, filename );
380            tr_free( filename );
381        }
382
383        tr_free( subpath );
384    }
385}
386
387struct rpc_response_data
388{
389    struct evhttp_request * req;
390    struct tr_rpc_server  * server;
391};
392
393static void
394rpc_response_func( tr_session      * session UNUSED,
395                   const char      * response,
396                   size_t            response_len,
397                   void            * user_data )
398{
399    struct rpc_response_data * data = user_data;
400    struct evbuffer * buf = tr_getBuffer( );
401
402    add_response( data->req, data->server, buf, response, response_len );
403    evhttp_add_header( data->req->output_headers,
404                           "Content-Type", "application/json; charset=UTF-8" );
405    evhttp_send_reply( data->req, HTTP_OK, "OK", buf );
406
407    tr_releaseBuffer( buf );
408    tr_free( data );
409}
410
411
412static void
413handle_rpc( struct evhttp_request * req,
414            struct tr_rpc_server  * server )
415{
416    struct rpc_response_data * data = tr_new0( struct rpc_response_data, 1 );
417
418    data->req = req;
419    data->server = server;
420   
421    if( req->type == EVHTTP_REQ_GET )
422    {
423        const char * q;
424        if( ( q = strchr( req->uri, '?' ) ) )
425            tr_rpc_request_exec_uri( server->session, q+1, -1, rpc_response_func, data );
426    }
427    else if( req->type == EVHTTP_REQ_POST )
428    {
429        tr_rpc_request_exec_json( server->session,
430                                  EVBUFFER_DATA( req->input_buffer ),
431                                  EVBUFFER_LENGTH( req->input_buffer ),
432                                  rpc_response_func, data );
433    }
434
435}
436
437static tr_bool
438isAddressAllowed( const tr_rpc_server * server,
439                  const char *          address )
440{
441    tr_list * l;
442
443    if( !server->isWhitelistEnabled )
444        return TRUE;
445
446    for( l=server->whitelist; l!=NULL; l=l->next )
447        if( tr_wildmat( address, l->data ) )
448            return TRUE;
449
450    return FALSE;
451}
452
453static void
454handle_request( struct evhttp_request * req,
455                void *                  arg )
456{
457    struct tr_rpc_server * server = arg;
458
459    if( req && req->evcon )
460    {
461        const char * auth;
462        char *       user = NULL;
463        char *       pass = NULL;
464
465        evhttp_add_header( req->output_headers, "Server", MY_REALM );
466
467        auth = evhttp_find_header( req->input_headers, "Authorization" );
468
469        if( auth && !strncasecmp( auth, "basic ", 6 ) )
470        {
471            int    plen;
472            char * p = tr_base64_decode( auth + 6, 0, &plen );
473            if( p && plen && ( ( pass = strchr( p, ':' ) ) ) )
474            {
475                user = p;
476                *pass++ = '\0';
477            }
478        }
479
480        if( !isAddressAllowed( server, req->remote_host ) )
481        {
482            send_simple_response( req, 401,
483                "<p>Unauthorized IP Address.</p>"
484                "<p>Either disable the IP address whitelist or add your address to it.</p>"
485                "<p>If you're editing settings.json, see the 'rpc-whitelist' and 'rpc-whitelist-enabled' entries.</p>"
486                "<p>If you're still using ACLs, use a whitelist instead.  See the transmission-daemon manpage for details.</p>" );
487        }
488        else if( server->isPasswordEnabled
489                 && ( !pass || !user || strcmp( server->username, user )
490                                     || !tr_ssha1_matches( server->password,
491                                                           pass ) ) )
492        {
493            evhttp_add_header( req->output_headers,
494                               "WWW-Authenticate",
495                               "Basic realm=\"" MY_REALM "\"" );
496            send_simple_response( req, 401, "Unauthorized User" );
497        }
498        else if( !strcmp( req->uri, "/transmission/web" )
499               || !strcmp( req->uri, "/transmission/clutch" )
500               || !strcmp( req->uri, "/" ) )
501        {
502            const char * protocol = "http";
503            const char * host = evhttp_find_header( req->input_headers, "Host" );
504            const char * uri = "transmission/web/";
505            char * location = tr_strdup_printf( "%s://%s/%s", protocol, host, uri );
506            evhttp_add_header( req->output_headers, "Location", location );
507            send_simple_response( req, HTTP_MOVEPERM, NULL );
508            tr_free( location );
509        }
510        else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
511        {
512            handle_clutch( req, server );
513        }
514        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
515        {
516            handle_rpc( req, server );
517        }
518        else if( !strncmp( req->uri, "/transmission/upload", 20 ) )
519        {
520            handle_upload( req, server );
521        }
522        else
523        {
524            send_simple_response( req, HTTP_NOTFOUND, req->uri );
525        }
526
527        tr_free( user );
528    }
529}
530
531static void
532startServer( void * vserver )
533{
534    tr_rpc_server * server  = vserver;
535    tr_address addr;
536
537    if( !server->httpd )
538    {
539        addr.type = TR_AF_INET;
540        addr.addr.addr4 = server->bindAddress;
541        server->httpd = evhttp_new( tr_eventGetBase( server->session ) );
542        evhttp_bind_socket( server->httpd, tr_ntop_non_ts( &addr ),
543                            server->port );
544        evhttp_set_gencb( server->httpd, handle_request, server );
545
546    }
547}
548
549static void
550stopServer( tr_rpc_server * server )
551{
552    if( server->httpd )
553    {
554        evhttp_free( server->httpd );
555        server->httpd = NULL;
556    }
557}
558
559static void
560onEnabledChanged( void * vserver )
561{
562    tr_rpc_server * server = vserver;
563
564    if( !server->isEnabled )
565        stopServer( server );
566    else
567        startServer( server );
568}
569
570void
571tr_rpcSetEnabled( tr_rpc_server * server,
572                  tr_bool         isEnabled )
573{
574    server->isEnabled = isEnabled;
575
576    tr_runInEventThread( server->session, onEnabledChanged, server );
577}
578
579tr_bool
580tr_rpcIsEnabled( const tr_rpc_server * server )
581{
582    return server->isEnabled;
583}
584
585static void
586restartServer( void * vserver )
587{
588    tr_rpc_server * server = vserver;
589
590    if( server->isEnabled )
591    {
592        stopServer( server );
593        startServer( server );
594    }
595}
596
597void
598tr_rpcSetPort( tr_rpc_server * server,
599               tr_port         port )
600{
601    if( server->port != port )
602    {
603        server->port = port;
604
605        if( server->isEnabled )
606            tr_runInEventThread( server->session, restartServer, server );
607    }
608}
609
610tr_port
611tr_rpcGetPort( const tr_rpc_server * server )
612{
613    return server->port;
614}
615
616void
617tr_rpcSetWhitelist( tr_rpc_server * server,
618                    const char    * whitelistStr )
619{
620    void * tmp;
621    const char * walk;
622
623    /* keep the string */
624    tr_free( server->whitelistStr );
625    server->whitelistStr = tr_strdup( whitelistStr );
626
627    /* clear out the old whitelist entries */
628    while(( tmp = tr_list_pop_front( &server->whitelist )))
629        tr_free( tmp );
630
631    /* build the new whitelist entries */
632    for( walk=whitelistStr; walk && *walk; ) {
633        const char * delimiters = " ,;";
634        const size_t len = strcspn( walk, delimiters );
635        char * token = tr_strndup( walk, len );
636        tr_list_append( &server->whitelist, token );
637        if( strcspn( token, "+-" ) < len )
638            tr_ninf( MY_NAME, "Adding address to whitelist: %s (And it has a '+' or '-'!  Are you using an old ACL by mistake?)", token );
639        else
640            tr_ninf( MY_NAME, "Adding address to whitelist: %s", token );
641       
642        if( walk[len]=='\0' )
643            break;
644        walk += len + 1;
645    }
646}
647
648const char*
649tr_rpcGetWhitelist( const tr_rpc_server * server )
650{
651    return server->whitelistStr ? server->whitelistStr : "";
652}
653
654void
655tr_rpcSetWhitelistEnabled( tr_rpc_server  * server,
656                           tr_bool          isEnabled )
657{
658    server->isWhitelistEnabled = isEnabled != 0;
659}
660
661tr_bool
662tr_rpcGetWhitelistEnabled( const tr_rpc_server * server )
663{
664    return server->isWhitelistEnabled;
665}
666
667/****
668*****  PASSWORD
669****/
670
671void
672tr_rpcSetUsername( tr_rpc_server * server,
673                   const char *    username )
674{
675    tr_free( server->username );
676    server->username = tr_strdup( username );
677    dbgmsg( "setting our Username to [%s]", server->username );
678}
679
680const char*
681tr_rpcGetUsername( const tr_rpc_server * server )
682{
683    return server->username ? server->username : "";
684}
685
686void
687tr_rpcSetPassword( tr_rpc_server * server,
688                   const char *    password )
689{
690    tr_free( server->password );
691    if( *password != '{' )
692        server->password = tr_ssha1( password );
693    else
694        server->password = strdup( password );
695    dbgmsg( "setting our Password to [%s]", server->password );
696}
697
698const char*
699tr_rpcGetPassword( const tr_rpc_server * server )
700{
701    return server->password ? server->password : "" ;
702}
703
704void
705tr_rpcSetPasswordEnabled( tr_rpc_server * server,
706                          tr_bool          isEnabled )
707{
708    server->isPasswordEnabled = isEnabled;
709    dbgmsg( "setting 'password enabled' to %d", (int)isEnabled );
710}
711
712tr_bool
713tr_rpcIsPasswordEnabled( const tr_rpc_server * server )
714{
715    return server->isPasswordEnabled;
716}
717
718const char *
719tr_rpcGetBindAddress( const tr_rpc_server * server )
720{
721    tr_address addr;
722    addr.type = TR_AF_INET;
723    addr.addr.addr4 = server->bindAddress;
724    return tr_ntop_non_ts( &addr );
725}
726
727/****
728*****  LIFE CYCLE
729****/
730
731static void
732closeServer( void * vserver )
733{
734    void * tmp;
735    tr_rpc_server * s = vserver;
736
737    stopServer( s );
738    while(( tmp = tr_list_pop_front( &s->whitelist )))
739        tr_free( tmp );
740#ifdef HAVE_ZLIB
741    deflateEnd( &s->stream );
742#endif
743    tr_free( s->whitelistStr );
744    tr_free( s->username );
745    tr_free( s->password );
746    tr_free( s );
747}
748
749void
750tr_rpcClose( tr_rpc_server ** ps )
751{
752    tr_runInEventThread( ( *ps )->session, closeServer, *ps );
753    *ps = NULL;
754}
755
756tr_rpc_server *
757tr_rpcInit( tr_session  * session,
758            tr_benc * settings )
759{
760    tr_rpc_server * s;
761    tr_bool found;
762    tr_bool boolVal;
763    int64_t i;
764    const char *str;
765    tr_address address;
766
767    s = tr_new0( tr_rpc_server, 1 );
768    s->session = session;
769
770    found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_ENABLED, &boolVal );
771    assert( found );
772    s->isEnabled = boolVal;
773
774    found = tr_bencDictFindInt( settings, TR_PREFS_KEY_RPC_PORT, &i );
775    assert( found );
776    s->port = i;
777
778    found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, &boolVal );
779    assert( found );
780    s->isWhitelistEnabled = boolVal;
781
782    found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal );
783    assert( found );
784    s->isPasswordEnabled = boolVal;
785
786    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_WHITELIST, &str );
787    assert( found );
788    tr_rpcSetWhitelist( s, str ? str : "127.0.0.1" );
789
790    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_USERNAME, &str );
791    assert( found );
792    s->username = tr_strdup( str );
793
794    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_PASSWORD, &str );
795    assert( found );
796    if( *str != '{' )
797        s->password = tr_ssha1( str );
798    else
799        s->password = strdup( str );
800
801    found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, &str );
802    assert( found );
803    if( tr_pton( str, &address ) == NULL ) {
804        tr_err( _( "%s is not a valid address" ), str );
805        address = tr_inaddr_any;
806    } else if( address.type != TR_AF_INET ) {
807        tr_err( _( "%s is not an IPv4 address. RPC listeners must be IPv4" ),
808                   str );
809        address = tr_inaddr_any;
810    }
811    s->bindAddress = address.addr.addr4;
812
813#ifdef HAVE_ZLIB
814    s->stream.zalloc = (alloc_func) Z_NULL;
815    s->stream.zfree = (free_func) Z_NULL;
816    s->stream.opaque = (voidpf) Z_NULL;
817    deflateInit( &s->stream, Z_BEST_COMPRESSION );
818#endif
819
820    if( s->isEnabled )
821    {
822        tr_ninf( MY_NAME, _( "Serving RPC and Web requests on port %d" ), (int) s->port );
823        tr_runInEventThread( session, startServer, s );
824
825        if( s->isWhitelistEnabled )
826            tr_ninf( MY_NAME, _( "Whitelist enabled" ) );
827
828        if( s->isPasswordEnabled )
829            tr_ninf( MY_NAME, _( "Password required" ) );
830    }
831
832    return s;
833}
Note: See TracBrowser for help on using the repository browser.