source: trunk/daemon/server.c @ 5137

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

tear out about 1/3 of ipcparse's overhead.

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