source: trunk/daemon/server.c @ 5142

Last change on this file since 5142 was 5142, checked in by charles, 14 years ago

make struct ipc_info opaque. const-ify the ipcparse functions where possible.

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