source: trunk/daemon/server.c @ 2227

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

Add client/server name to IPC version handshake.

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