source: trunk/daemon/server.c @ 3578

Last change on this file since 3578 was 3578, checked in by joshe, 15 years ago

Add IPC messages to set and retrieve the encryption mode.
Implement encryption mode messages in -daemon and -remote.

  • Property svn:keywords set to Date Rev Author Id
File size: 28.1 KB
Line 
1/******************************************************************************
2 * $Id: server.c 3578 2007-10-26 03:43:27Z joshe $
3 *
4 * Copyright (c) 2007 Joshua Elsasser
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/time.h>
28#include <sys/un.h>
29#include <assert.h>
30#include <errno.h>
31#include <event.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include <libtransmission/bsdtree.h>
40#include <libtransmission/bencode.h>
41#include <libtransmission/ipcparse.h>
42
43#include "errors.h"
44#include "misc.h"
45#include "server.h"
46#include "torrents.h"
47
48/* time out clients after this many seconds */
49#define CLIENT_TIMEOUT          ( 60 )
50
51struct client
52{
53    int                  fd;
54    struct bufferevent * ev;
55    struct ipc_info    * ipc;
56    RB_ENTRY( client )   link;
57};
58
59RB_HEAD( allclients, client );
60
61static void newclient( int, short, void * );
62static void noop     ( struct bufferevent *, void * );
63static void byebye   ( struct bufferevent *, short, void * );
64static void doread   ( struct bufferevent *, void * );
65static int  queuemsg ( struct client *, uint8_t *, size_t );
66static int  msgresp  ( struct client *, int64_t, enum ipc_msg );
67static void defmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
68static void noopmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
69static void addmsg1  ( enum ipc_msg, benc_val_t *, int64_t, void * );
70static void addmsg2  ( enum ipc_msg, benc_val_t *, int64_t, void * );
71static void quitmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
72static void intmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
73static void strmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
74static void infomsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
75static int  addinfo  ( benc_val_t *, int, int );
76static int  addstat  ( benc_val_t *, int, int );
77static void tormsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
78static void lookmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
79static void prefmsg  ( enum ipc_msg, benc_val_t *, int64_t, void * );
80static void supmsg   ( enum ipc_msg, benc_val_t *, int64_t, void * );
81static int  clientcmp( struct client *, struct client * );
82
83RB_GENERATE_STATIC( allclients, client, link, clientcmp )
84INTCMP_FUNC( clientcmp, client, ev )
85
86static struct event_base * gl_base    = NULL;
87static struct ipc_funcs  * gl_tree    = NULL;
88static int                 gl_debug   = 0;
89static int                 gl_exiting = 0;
90static struct allclients   gl_clients = RB_INITIALIZER( &gl_clients );
91
92int
93server_init( struct event_base * base )
94{
95    assert( NULL == gl_base && NULL == gl_tree );
96    gl_base = base;
97    gl_tree = ipc_initmsgs();
98    if( NULL == gl_tree )
99    {
100        return -1;
101    }
102
103    if( 0 > ipc_addmsg( gl_tree, IPC_MSG_ADDMANYFILES, addmsg1 ) ||
104        0 > ipc_addmsg( gl_tree, IPC_MSG_ADDONEFILE,   addmsg2 ) ||
105        0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOMAP,      intmsg  ) ||
106        0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOSTART,    intmsg  ) ||
107        0 > ipc_addmsg( gl_tree, IPC_MSG_CRYPTO,       strmsg  ) ||
108        0 > ipc_addmsg( gl_tree, IPC_MSG_DOWNLIMIT,    intmsg  ) ||
109        0 > ipc_addmsg( gl_tree, IPC_MSG_DIR,          strmsg  ) ||
110        0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOMAP,   prefmsg ) ||
111        0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOSTART, prefmsg ) ||
112        0 > ipc_addmsg( gl_tree, IPC_MSG_GETCRYPTO,    prefmsg ) ||
113        0 > ipc_addmsg( gl_tree, IPC_MSG_GETDOWNLIMIT, prefmsg ) ||
114        0 > ipc_addmsg( gl_tree, IPC_MSG_GETDIR,       prefmsg ) ||
115        0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFO,      infomsg ) ||
116        0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFOALL,   infomsg ) ||
117        0 > ipc_addmsg( gl_tree, IPC_MSG_GETPEX,       prefmsg ) ||
118        0 > ipc_addmsg( gl_tree, IPC_MSG_GETPORT,      prefmsg ) ||
119        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTAT,      infomsg ) ||
120        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTATALL,   infomsg ) ||
121        0 > ipc_addmsg( gl_tree, IPC_MSG_GETUPLIMIT,   prefmsg ) ||
122        0 > ipc_addmsg( gl_tree, IPC_MSG_GETSUP,       supmsg  ) ||
123        0 > ipc_addmsg( gl_tree, IPC_MSG_LOOKUP,       lookmsg ) ||
124        0 > ipc_addmsg( gl_tree, IPC_MSG_NOOP,         noopmsg ) ||
125        0 > ipc_addmsg( gl_tree, IPC_MSG_PEX,          intmsg  ) ||
126        0 > ipc_addmsg( gl_tree, IPC_MSG_PORT,         intmsg  ) ||
127        0 > ipc_addmsg( gl_tree, IPC_MSG_QUIT,         quitmsg ) ||
128        0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVE,       tormsg  ) ||
129        0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVEALL,    tormsg  ) ||
130        0 > ipc_addmsg( gl_tree, IPC_MSG_START,        tormsg  ) ||
131        0 > ipc_addmsg( gl_tree, IPC_MSG_STARTALL,     tormsg  ) ||
132        0 > ipc_addmsg( gl_tree, IPC_MSG_STOP,         tormsg  ) ||
133        0 > ipc_addmsg( gl_tree, IPC_MSG_STOPALL,      tormsg  ) ||
134        0 > ipc_addmsg( gl_tree, IPC_MSG_UPLIMIT,      intmsg  ) )
135    {
136        return -1;
137    }
138
139    ipc_setdefmsg( gl_tree, defmsg );
140
141    return 0;
142}
143
144void
145server_debug( int enable )
146{
147    gl_debug = enable;
148}
149
150int
151server_listen( int fd )
152{
153    struct event * ev;
154    int flags;
155
156    assert( NULL != gl_base );
157
158    flags = fcntl( fd, F_GETFL );
159    if( 0 > flags )
160    {
161        errnomsg( "failed to get flags on socket" );
162        return -1;
163    }
164    if( 0 > fcntl( fd, F_SETFL, flags | O_NONBLOCK ) )
165    {
166        errnomsg( "failed to set flags on socket" );
167        return -1;
168    }
169
170    if( 0 > listen( fd, 5 ) )
171    {
172        errnomsg( "failed to listen on socket" );
173        return -1;
174    }
175
176    ev = malloc( sizeof *ev );
177    if( NULL == ev )
178    {
179        mallocmsg( sizeof *ev );
180        return -1;
181    }
182
183    event_set( ev, fd, EV_READ | EV_PERSIST, newclient, ev );
184    event_base_set( gl_base, ev );
185    event_add( ev, NULL );
186
187    return 0;
188}
189
190void
191server_quit( void )
192{
193    struct client * ii, * next;
194
195    if(gl_exiting)
196        return;
197
198    torrent_exit( 0 );
199    gl_exiting = 1;
200
201    for( ii = RB_MIN( allclients, &gl_clients ); NULL != ii; ii = next )
202    {
203        next = RB_NEXT( allclients, &gl_clients, ii );
204        byebye( ii->ev, EVBUFFER_EOF, NULL );
205    }
206}
207
208void
209newclient( int fd, short event UNUSED, void * arg )
210{
211    struct sockaddr_un   sa;
212    struct client      * client, * old;
213    socklen_t            socklen;
214    struct bufferevent * clev;
215    int                  clfd;
216    size_t               buflen;
217    uint8_t            * buf;
218
219    if( gl_exiting )
220    {
221        event_del( arg );
222        return;
223    }
224
225    for( ;; )
226    {
227        client = calloc( 1, sizeof *client );
228        if( NULL == client )
229        {
230            mallocmsg( sizeof *client );
231            return;
232        }
233
234        socklen = sizeof sa;
235        clfd = accept( fd, ( struct sockaddr * )&sa, &socklen );
236        if( 0 > clfd )
237        {
238            if( EWOULDBLOCK != errno && EAGAIN != errno &&
239                ECONNABORTED != errno )
240            {
241                errnomsg( "failed to accept ipc connection" );
242            }
243            free( client );
244            break;
245        }
246
247        client->ipc = ipc_newcon( gl_tree );
248        if( NULL == client->ipc )
249        {
250            mallocmsg( sizeof *client->ipc );
251            close( clfd );
252            free( client );
253            return;
254        }
255
256        clev = bufferevent_new( clfd, doread, noop, byebye, client );
257        if( NULL == clev )
258        {
259            errnomsg( "failed to create bufferevent" );
260            close( clfd );
261            ipc_freecon( client->ipc );
262            free( client );
263            return;
264        }
265        bufferevent_base_set( gl_base, clev );
266        bufferevent_settimeout( clev, CLIENT_TIMEOUT, CLIENT_TIMEOUT );
267
268        client->fd      = clfd;
269        client->ev      = clev;
270        old = RB_INSERT( allclients, &gl_clients, client );
271        assert( NULL == old );
272
273        if( gl_debug )
274        {
275            printf( "*** new client %i\n", clfd );
276        }
277
278        bufferevent_enable( clev, EV_READ );
279        buf = ipc_mkvers( &buflen, "Transmission daemon " LONG_VERSION_STRING );
280        if( 0 > queuemsg( client, buf, buflen ) )
281        {
282            free( buf );
283            return;
284        }
285        free( buf );
286    }
287}
288
289void
290noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
291{
292    /* libevent prior to 1.2 couldn't handle a NULL write callback */
293}
294
295void
296byebye( struct bufferevent * ev, short what, void * arg UNUSED )
297{
298    struct client * client, key;
299
300    if( !( EVBUFFER_EOF & what ) )
301    {
302        if( EVBUFFER_TIMEOUT & what )
303        {
304            errmsg( "client connection timed out" );
305        }
306        else if( EVBUFFER_READ & what )
307        {
308            errmsg( "read error on client connection" );
309        }
310        else if( EVBUFFER_WRITE & what )
311        {
312            errmsg( "write error on client connection" );
313        }
314        else if( EVBUFFER_ERROR & what )
315        {
316            errmsg( "error on client connection" );
317        }
318        else
319        {
320            errmsg( "unknown error on client connection: 0x%x", what );
321        }
322    }
323
324    memset( &key, 0, sizeof key );
325    key.ev = ev;
326    client = RB_FIND( allclients, &gl_clients, &key );
327    assert( NULL != client );
328    RB_REMOVE( allclients, &gl_clients, client );
329    bufferevent_free( ev );
330    close( client->fd );
331    ipc_freecon( client->ipc );
332    if( gl_debug )
333    {
334        printf( "*** client %i went bye-bye\n", client->fd );
335    }
336    free( client );
337}
338
339void
340doread( struct bufferevent * ev, void * arg )
341{
342    struct client * client = arg;
343    ssize_t         res;
344    uint8_t       * buf;
345    size_t          len;
346
347    assert( !gl_exiting );
348
349    buf = EVBUFFER_DATA( EVBUFFER_INPUT( ev ) );
350    len = EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) );
351
352    if( gl_debug )
353    {
354        printf( "<<< %zu bytes from client %i: ", len, client->fd );
355        fwrite( buf, 1, len, stdout );
356        putc( '\n', stdout );
357    }
358
359    if( IPC_MIN_MSG_LEN > len )
360    {
361        return;
362    }
363
364    res = ipc_parse( client->ipc, buf, len, client );
365
366    if( gl_exiting )
367    {
368        return;
369    }
370
371    if( 0 > res )
372    {
373        switch( errno )
374        {
375            case EPERM:
376                errmsg( "unsupported protocol version" );
377                break;
378            case EINVAL:
379                errmsg( "protocol parse error" );
380                break;
381            default:
382                errnomsg( "parsing failed" );
383                break;
384        }
385        byebye( ev, EVBUFFER_ERROR, NULL );
386    }
387    else if( 0 < res )
388    {
389        evbuffer_drain( EVBUFFER_INPUT( ev ), res );
390    }
391}
392
393int
394queuemsg( struct client * client, uint8_t * buf, size_t buflen )
395{
396    if( NULL == buf )
397    {
398        if( EPERM != errno )
399        {
400            errnomsg( "failed to build message" );
401            byebye( client->ev, EVBUFFER_EOF, NULL );
402        }
403        return -1;
404    }
405
406    if( gl_debug )
407    {
408        printf( ">>> %zu bytes to client %i: ", buflen, client->fd );
409        fwrite( buf, 1, buflen, stdout );
410        putc( '\n', stdout );
411    }
412
413    if( 0 > bufferevent_write( client->ev, buf, buflen ) )
414    {
415        errnomsg( "failed to buffer %zd bytes of data for write", buflen );
416        return -1;
417    }
418
419    return 0;
420}
421
422int
423msgresp( struct client * client, int64_t tag, enum ipc_msg id )
424{
425    uint8_t * buf;
426    size_t    buflen;
427    int       ret;
428
429    if( 0 >= tag )
430    {
431        return 0;
432    }
433
434    buf = ipc_mkempty( client->ipc, &buflen, id, tag );
435    ret = queuemsg( client, buf, buflen );
436    free( buf );
437
438    return ret;
439}
440
441void
442defmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
443        void * arg )
444{
445    struct client * client = arg;
446
447    msgresp( client, tag, IPC_MSG_NOTSUP );
448}
449
450void
451noopmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
452         void * arg )
453{
454    struct client * client = arg;
455
456    msgresp( client, tag, IPC_MSG_OK );
457}
458
459void
460addmsg1( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
461{
462    struct client * client = arg;
463    benc_val_t      pk, * added, * file;
464    int             ii, tor;
465    size_t          buflen;
466    uint8_t       * buf;
467
468    if( NULL == val || TYPE_LIST != val->type )
469    {
470        msgresp( client, tag, IPC_MSG_BAD );
471        return;
472    }
473
474    added = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
475    if( NULL == added )
476    {
477        errnomsg( "failed to build message" );
478        byebye( client->ev, EVBUFFER_EOF, NULL );
479        return;
480    }
481
482    for( ii = 0; ii < val->val.l.count; ii++ )
483    {
484        file = &val->val.l.vals[ii];
485        if( TYPE_STR != file->type )
486        {
487            continue;
488        }
489        /* XXX need to somehow inform client of skipped or failed files */
490        tor = torrent_add_file( file->val.s.s, NULL, -1 );
491        if( TORRENT_ID_VALID( tor ) )
492        {
493            const tr_info * inf = torrent_info( tor );
494            if( 0 > ipc_addinfo( added, tor, inf, 0 ) )
495            {
496                errnomsg( "failed to build message" );
497                tr_bencFree( &pk );
498                byebye( client->ev, EVBUFFER_EOF, NULL );
499                return;
500            }
501        }
502    }
503
504    buf = ipc_mkval( &pk, &buflen );
505    tr_bencFree( &pk );
506    queuemsg( client, buf, buflen );
507    free( buf );
508}
509
510void
511addmsg2( enum ipc_msg id UNUSED, benc_val_t * dict, int64_t tag, void * arg )
512{
513    struct client * client = arg;
514    benc_val_t    * val, pk;
515    int             tor, start;
516    size_t          buflen;
517    uint8_t       * buf;
518    const char    * dir;
519
520    if( NULL == dict || TYPE_DICT != dict->type )
521    {
522        msgresp( client, tag, IPC_MSG_BAD );
523        return;
524    }
525
526    val   = tr_bencDictFind( dict, "directory" );
527    dir   = ( NULL == val || TYPE_STR != val->type ? NULL : val->val.s.s );
528    val   = tr_bencDictFind( dict, "autostart" );
529    start = ( NULL == val || TYPE_INT != val->type ? -1 :
530              ( val->val.i ? 1 : 0 ) );
531    val   = tr_bencDictFind( dict, "data" );
532    if( NULL != val && TYPE_STR == val->type )
533    {
534        /* XXX detect duplicates and return a message indicating so */
535        tor = torrent_add_data( ( uint8_t * )val->val.s.s, val->val.s.i,
536                                dir, start );
537    }
538    else
539    {
540        val = tr_bencDictFind( dict, "file" );
541        if( NULL == val || TYPE_STR != val->type )
542        {
543            msgresp( client, tag, IPC_MSG_BAD );
544            return;
545        }
546        /* XXX detect duplicates and return a message indicating so */
547        tor = torrent_add_file( val->val.s.s, dir, start );
548    }
549
550    if( TORRENT_ID_VALID( tor ) )
551    {
552        const tr_info * inf;
553        val = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
554        if( NULL == val )
555        {
556            errnomsg( "failed to build message" );
557            byebye( client->ev, EVBUFFER_EOF, NULL );
558            return;
559        }
560        inf = torrent_info( tor );
561        if( 0 > ipc_addinfo( val, tor, inf, 0 ) )
562        {
563            errnomsg( "failed to build message" );
564            tr_bencFree( &pk );
565            byebye( client->ev, EVBUFFER_EOF, NULL );
566            return;
567        }
568        buf = ipc_mkval( &pk, &buflen );
569        tr_bencFree( &pk );
570        queuemsg( client, buf, buflen );
571        free( buf );
572    }
573    else
574    {
575        msgresp( client, tag, IPC_MSG_FAIL );
576    }
577}
578
579void
580quitmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag UNUSED,
581         void * arg UNUSED )
582{
583    server_quit();
584}
585
586void
587intmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
588{
589    struct client * client = arg;
590    int             num;
591
592    if( NULL == val || TYPE_INT != val->type )
593    {
594        msgresp( client, tag, IPC_MSG_BAD );
595        return;
596    }
597
598    num = MAX( INT_MIN, MIN( INT_MAX, val->val.i ) );
599    switch( id )
600    {
601        case IPC_MSG_AUTOMAP:
602            torrent_enable_port_mapping( num ? 1 : 0 );
603            break;
604        case IPC_MSG_AUTOSTART:
605            torrent_set_autostart( num ? 1 : 0 );
606            break;
607        case IPC_MSG_DOWNLIMIT:
608            torrent_set_downlimit( num );
609            break;
610        case IPC_MSG_PEX:
611            torrent_set_pex( num ? 1 : 0 );
612            break;
613        case IPC_MSG_PORT:
614            torrent_set_port( num );
615            break;
616        case IPC_MSG_UPLIMIT:
617            torrent_set_uplimit( num );
618            break;
619        default:
620            assert( 0 );
621            return;
622    }
623
624    msgresp( client, tag, IPC_MSG_OK );
625}
626
627void
628strmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
629{
630    struct client * client = arg;
631
632    if( NULL == val || TYPE_STR != val->type )
633    {
634        msgresp( client, tag, IPC_MSG_BAD );
635        return;
636    }
637
638    switch( id )
639    {
640        case IPC_MSG_CRYPTO:
641            if(!strcasecmp(val->val.s.s, "preferred"))
642                torrent_set_encryption(TR_ENCRYPTION_PREFERRED);
643            else if(!strcasecmp(val->val.s.s, "required"))
644                torrent_set_encryption(TR_ENCRYPTION_REQUIRED);
645            else
646            {
647                msgresp(client, tag, IPC_MSG_BAD);
648                return;
649            }
650            break;
651        case IPC_MSG_DIR:
652            torrent_set_directory( val->val.s.s );
653            break;
654        default:
655            assert( 0 );
656            return;
657    }
658
659    msgresp( client, tag, IPC_MSG_OK );
660}
661
662void
663infomsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
664{
665    struct client * client = arg;
666    uint8_t       * buf;
667    size_t          buflen;
668    benc_val_t      pk, * pkinf, * typelist, * idlist, * idval;
669    int             all, types, ii, tor;
670    void          * iter;
671    enum ipc_msg    respid;
672    int         ( * addfunc )( benc_val_t *, int, int );
673
674    all = 0;
675    switch( id )
676    {
677        case IPC_MSG_GETINFOALL:
678            all = 1;
679            /* FALLTHROUGH; */
680        case IPC_MSG_GETINFO:
681            respid = IPC_MSG_INFO;
682            addfunc = addinfo;
683            break;
684        case IPC_MSG_GETSTATALL:
685            all = 1;
686            /* FALLTHROUGH */
687        case IPC_MSG_GETSTAT:
688            respid = IPC_MSG_STAT;
689            addfunc = addstat;
690            break;
691        default:
692            assert( 0 );
693            return;
694    }
695
696    /* initialize packet */
697    pkinf = ipc_initval( client->ipc, respid, tag, &pk, TYPE_LIST );
698    if( NULL == pkinf )
699    {
700        errnomsg( "failed to build message" );
701        byebye( client->ev, EVBUFFER_EOF, NULL );
702        return;
703    }
704
705    /* add info/status for all torrents */
706    if( all )
707    {
708        if( NULL == val || TYPE_LIST != val->type )
709        {
710            msgresp( client, tag, IPC_MSG_BAD );
711            tr_bencFree( &pk );
712            return;
713        }
714        types = ipc_infotypes( respid, val );
715        iter = NULL;
716        while( NULL != ( iter = torrent_iter( iter, &tor ) ) )
717        {
718            if( 0 > addfunc( pkinf, tor, types ) )
719            {
720                errnomsg( "failed to build message" );
721                tr_bencFree( &pk );
722                byebye( client->ev, EVBUFFER_EOF, NULL );
723                return;
724            }
725        }
726    }
727    /* add info/status for the requested IDs */
728    else
729    {
730        if( NULL == val || TYPE_DICT != val->type )
731        {
732            msgresp( client, tag, IPC_MSG_BAD );
733            tr_bencFree( &pk );
734            return;
735        }
736        typelist = tr_bencDictFind( val, "type" );
737        idlist   = tr_bencDictFind( val, "id" );
738        if( NULL == typelist || TYPE_LIST != typelist->type ||
739            NULL == idlist   || TYPE_LIST != idlist->type )
740        {
741            msgresp( client, tag, IPC_MSG_BAD );
742            tr_bencFree( &pk );
743            return;
744        }
745        types = ipc_infotypes( respid, typelist );
746        for( ii = 0; idlist->val.l.count > ii; ii++ )
747        {
748            idval = &idlist->val.l.vals[ii];
749            if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
750            {
751                continue;
752            }
753            tor = idval->val.i;
754            if( 0 > addfunc( pkinf, idval->val.i, types ) )
755            {
756                errnomsg( "failed to build message" );
757                tr_bencFree( &pk );
758                byebye( client->ev, EVBUFFER_EOF, NULL );
759                return;
760            }
761        }
762    }
763
764    /* generate packet data and send it */
765    buf = ipc_mkval( &pk, &buflen );
766    tr_bencFree( &pk );
767    queuemsg( client, buf, buflen );
768    free( buf );
769}
770
771int
772addinfo( benc_val_t * list, int id, int types )
773{
774    const tr_info * inf = torrent_info( id );
775    return inf ? ipc_addinfo( list, id, inf, types ) : 0;
776}
777
778int
779addstat( benc_val_t * list, int id, int types )
780{
781    const tr_stat * st = torrent_stat( id );
782    return st ? ipc_addstat( list, id, st, types ) : 0;
783}
784
785void
786tormsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
787{
788    struct client * client = arg;
789    benc_val_t    * idval;
790    int             ii, all;
791    void          * iter;
792    void        ( * func )( int );
793
794    all = 0;
795    switch( id )
796    {
797        case IPC_MSG_REMOVEALL:
798            all = 1;
799            /* FALLTHROUGH */
800        case IPC_MSG_REMOVE:
801            func = torrent_remove;
802            break;
803        case IPC_MSG_STARTALL:
804            all = 1;
805            /* FALLTHROUGH */
806        case IPC_MSG_START:
807            func = torrent_start;
808            break;
809        case IPC_MSG_STOPALL:
810            all = 1;
811            /* FALLTHROUGH */
812        case IPC_MSG_STOP:
813            func = torrent_stop;
814            break;
815        default:
816            assert( 0 );
817            return;
818    }
819
820    /* remove/start/stop all torrents */
821    if( all )
822    {
823        iter = NULL;
824        while( NULL != ( iter = torrent_iter( iter, &ii ) ) )
825        {
826            func( ii );
827            if( torrent_remove == func )
828            {
829                iter = NULL;
830            }
831        }
832    }
833    /* remove/start/stop requested list of torrents */
834    else
835    {
836        if( NULL == val || TYPE_LIST != val->type )
837        {
838            msgresp( client, tag, IPC_MSG_BAD );
839            return;
840        }
841        for( ii = 0; val->val.l.count > ii; ii++ )
842        {
843            idval = &val->val.l.vals[ii];
844            if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
845            {
846                continue;
847            }
848            func( idval->val.i );
849        }
850    }
851
852    msgresp( client, tag, IPC_MSG_OK );
853}
854
855void
856lookmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
857{
858    struct client * client = arg;
859    uint8_t       * buf;
860    size_t          buflen;
861    int             ii;
862    benc_val_t    * hash, pk, * pkinf;
863    int64_t         found;
864
865    if( NULL == val || TYPE_LIST != val->type )
866    {
867        msgresp( client, tag, IPC_MSG_BAD );
868        return;
869    }
870
871    pkinf = ipc_initval( client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
872    if( NULL == pkinf )
873    {
874        errnomsg( "failed to build message" );
875        byebye( client->ev, EVBUFFER_EOF, NULL );
876        return;
877    }
878
879    for( ii = 0; val->val.l.count > ii; ii++ )
880    {
881        const tr_info * inf;
882        hash = &val->val.l.vals[ii];
883        if( NULL == hash || TYPE_STR != hash->type ||
884            SHA_DIGEST_LENGTH * 2 != hash->val.s.i )
885        {
886            tr_bencFree( &pk );
887            msgresp( client, tag, IPC_MSG_BAD );
888            return;
889        }
890        found = torrent_lookup( ( uint8_t * )hash->val.s.s );
891        if( !TORRENT_ID_VALID( found ) )
892        {
893            continue;
894        }
895        inf = torrent_info( found );
896        assert( NULL != inf );
897        if( 0 > ipc_addinfo( pkinf, found, inf, IPC_INF_HASH ) )
898        {
899            errnomsg( "failed to build message" );
900            tr_bencFree( &pk );
901            byebye( client->ev, EVBUFFER_EOF, NULL );
902            return;
903        }
904    }
905
906    buf = ipc_mkval( &pk, &buflen );
907    tr_bencFree( &pk );
908    queuemsg( client, buf, buflen );
909    free( buf );
910}
911
912void
913prefmsg( enum ipc_msg id, benc_val_t * val UNUSED, int64_t tag, void * arg )
914{
915    struct client * client = arg;
916    uint8_t       * buf;
917    size_t          buflen;
918    const char    * strval;
919
920    switch( id )
921    {
922        case IPC_MSG_GETAUTOMAP:
923            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_AUTOMAP, tag,
924                             torrent_get_port_mapping() );
925            break;
926        case IPC_MSG_GETAUTOSTART:
927            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_AUTOSTART, tag,
928                             torrent_get_autostart() );
929            break;
930        case IPC_MSG_GETCRYPTO:
931            switch(torrent_get_encryption())
932            {
933                case TR_ENCRYPTION_PREFERRED:
934                    strval = "preferred";
935                    break;
936                case TR_ENCRYPTION_REQUIRED:
937                    strval = "required";
938                    break;
939                default:
940                    assert(0);
941                    return;
942            }
943            buf = ipc_mkstr(client->ipc, &buflen, IPC_MSG_CRYPTO, tag, strval);
944            break;
945        case IPC_MSG_GETDIR:
946            buf = ipc_mkstr( client->ipc, &buflen, IPC_MSG_DIR, tag,
947                             torrent_get_directory() );
948            break;
949        case IPC_MSG_GETDOWNLIMIT:
950            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_DOWNLIMIT, tag,
951                             torrent_get_downlimit() );
952            break;
953        case IPC_MSG_GETPEX:
954            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_PEX, tag,
955                             torrent_get_pex() );
956            break;
957        case IPC_MSG_GETPORT:
958            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_PORT, tag,
959                             torrent_get_port() );
960            break;
961        case IPC_MSG_GETUPLIMIT:
962            buf = ipc_mkint( client->ipc, &buflen, IPC_MSG_UPLIMIT, tag,
963                             torrent_get_uplimit() );
964            break;
965        default:
966            assert( 0 );
967            return;
968    }
969
970    queuemsg( client, buf, buflen );
971    free( buf );
972}
973
974void
975supmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
976{
977    struct client  * client = arg;
978    uint8_t        * buf;
979    size_t           buflen;
980    int              ii;
981    benc_val_t       pk, *pkval, * name;
982    enum ipc_msg     found;
983
984    if( NULL == val || TYPE_LIST != val->type )
985    {
986        msgresp( client, tag, IPC_MSG_BAD );
987        return;
988    }
989
990    pkval = ipc_initval( client->ipc, IPC_MSG_SUP, tag, &pk, TYPE_LIST );
991    if( NULL == pkval )
992    {
993        errnomsg( "failed to build message" );
994        byebye( client->ev, EVBUFFER_EOF, NULL );
995        return;
996    }
997    /* XXX look at other initval to make sure we free pk */
998    if( tr_bencListReserve( pkval, val->val.l.count ) )
999    {
1000        errnomsg( "failed to build message" );
1001        tr_bencFree( &pk );
1002        byebye( client->ev, EVBUFFER_EOF, NULL );
1003        return;
1004    }
1005
1006    for( ii = 0; val->val.l.count > ii; ii++ )
1007    {
1008        name = &val->val.l.vals[ii];
1009        if( NULL == name || TYPE_STR != name->type )
1010        {
1011            tr_bencFree( &pk );
1012            msgresp( client, tag, IPC_MSG_BAD );
1013            return;
1014        }
1015        found = ipc_msgid( client->ipc, name->val.s.s );
1016        if( IPC__MSG_COUNT == found || !ipc_ishandled( client->ipc, found ) )
1017        {
1018            continue;
1019        }
1020        tr_bencInitStr( tr_bencListAdd( pkval ),
1021                        name->val.s.s, name->val.s.i, 1 );
1022    }
1023
1024    buf = ipc_mkval( &pk, &buflen );
1025    tr_bencFree( &pk );
1026    queuemsg( client, buf, buflen );
1027    free( buf );
1028}
Note: See TracBrowser for help on using the repository browser.