source: trunk/daemon/server.c @ 2149

Last change on this file since 2149 was 2149, checked in by livings124, 15 years ago

Merge file selection and torrent creation into the main branch.

The new code for these features is under a new license.

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