source: branches/daemon/daemon/client.c @ 1696

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

Add strlcpy and strlcat from openbsd and use them when not provided by libc.

  • Property svn:keywords set to Date Rev Author Id
File size: 24.4 KB
Line 
1/******************************************************************************
2 * $Id: client.c 1696 2007-04-10 02:27:46Z 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 <limits.h>
33#include <stdarg.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "bsdqueue.h"
39#include "bsdtree.h"
40#include "client.h"
41#include "bencode.h"
42#include "errors.h"
43#include "ipc.h"
44#include "misc.h"
45#include "trcompat.h"
46
47/* time out server after this many seconds */
48#define SERVER_TIMEOUT          ( 15 )
49
50struct con
51{
52    int                  infd;
53    int                  outfd;
54    struct ipc_info      ipc;
55    struct bufferevent * evin;
56    struct bufferevent * evout;
57};
58
59struct req
60{
61    enum ipc_msg       id;
62    int64_t            tag;
63    struct strlist   * strs;
64    int64_t            num;
65    char             * str;
66    size_t             listlen;
67    int64_t          * numlist;
68    uint8_t          * buf;
69    int                types;
70    SLIST_ENTRY( req ) next;
71};
72
73SLIST_HEAD( reqlist, req );
74
75struct resp
76{
77    int64_t            tag;
78    cl_listfunc        listcb;
79    cl_infofunc        infocb;
80    cl_hashfunc        hashcb;
81    cl_statfunc        statcb;
82    RB_ENTRY( resp )   links;
83};
84
85RB_HEAD( resptree, resp );
86
87static struct req * addreq   ( enum ipc_msg, int64_t, struct resp ** );
88static int     addintlistreq ( enum ipc_msg, size_t, const int * );
89static void    noop          ( struct bufferevent *, void * );
90static void    noway         ( struct bufferevent *, void * );
91static void    didwrite      ( struct bufferevent *, void * );
92static void    ohshit        ( struct bufferevent *, short, void * );
93static void    canread       ( struct bufferevent *, void * );
94static void    flushreqs     ( struct con * );
95static int     sendvers      ( struct con * );
96static void    infomsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
97static void    statmsg       ( enum ipc_msg, benc_val_t *, int64_t, void * );
98static void    defmsg        ( enum ipc_msg, benc_val_t *, int64_t, void * );
99static void    cbdone        ( struct resp * );
100static int64_t getinfoint    ( enum ipc_msg, benc_val_t *, int, int64_t );
101static char *  getinfostr    ( enum ipc_msg, benc_val_t *, int, char * );
102static int     resptagcmp    ( struct resp *, struct resp * );
103
104RB_GENERATE_STATIC( resptree, resp, links, resptagcmp )
105INTCMP_FUNC( resptagcmp, resp, tag )
106
107static struct event_base * gl_base   = NULL;
108static struct ipc_funcs  * gl_tree   = NULL;
109static struct reqlist      gl_reqs   = SLIST_HEAD_INITIALIZER( &gl_reqs );
110static struct resptree     gl_resps  = RB_INITIALIZER( &gl_resps );
111static int64_t             gl_tag    = 0;
112static int                 gl_proxy  = -1;
113
114int
115client_init( struct event_base * base )
116{
117    assert( NULL == gl_base && NULL == gl_tree );
118    gl_base   = base;
119    gl_tree   = ipc_initmsgs();
120    if( NULL == gl_tree )
121    {
122        return -1;
123    }
124
125    if( 0 > ipc_addmsg( gl_tree, IPC_MSG_INFO, infomsg ) ||
126        0 > ipc_addmsg( gl_tree, IPC_MSG_STAT, statmsg ) )
127    {
128        return -1;
129    }
130
131    ipc_setdefmsg( gl_tree, defmsg );
132
133    return 0;
134}
135
136int
137client_new_sock( const char * path )
138{
139    struct sockaddr_un sun;
140    int                fd;
141    struct con       * con;
142
143    assert( NULL != gl_base );
144    assert( 0 > gl_proxy );
145    assert( NULL != path );
146
147    gl_proxy = 0;
148
149    bzero( &sun, sizeof sun );
150    sun.sun_family = AF_LOCAL;
151    strlcpy( sun.sun_path, path, sizeof sun.sun_path );
152
153    fd = socket( AF_UNIX, SOCK_STREAM, 0 );
154    if( 0 > fd )
155    {
156        errnomsg( "failed to create socket" );
157        return -1;
158    }
159
160    if( 0 > connect( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
161    {
162        errnomsg( "failed to connect to socket file: %s", path );
163        close( fd );
164        return -1;
165    }
166    con = calloc( 1, sizeof *con );
167    if( NULL == con )
168    {
169        mallocmsg( sizeof *con );
170        close( fd );
171        return -1;
172    }
173    ipc_newcon( &con->ipc, gl_tree );
174    con->infd = fd;
175    con->evin = bufferevent_new( fd, canread, didwrite, ohshit, con );
176    if( NULL == con->evin )
177    {
178        mallocmsg( -1 );
179        close( fd );
180        free( con );
181        return -1;
182    }
183    con->outfd = con->infd;
184    con->evout = con->evin;
185    bufferevent_base_set( gl_base, con->evin );
186    bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT );
187    bufferevent_enable( con->evin, EV_READ );
188    if( 0 > sendvers( con ) )
189    {
190        exit( 1 );
191    }
192
193    return 0;
194}
195
196int
197client_new_cmd( char * const * cmd )
198{
199    struct con * con;
200    int          tocmd[2], fromcmd[2];
201    pid_t        kid;
202
203    assert( NULL != gl_base );
204    assert( 0 > gl_proxy );
205    assert( NULL != cmd && NULL != cmd[0] );
206
207    gl_proxy = 1;
208
209    if( 0 > pipe( tocmd ) )
210    {
211        errnomsg( "failed to create pipe" );
212        return -1;
213    }
214
215    if( 0 > pipe( fromcmd ) )
216    {
217        errnomsg( "failed to create pipe" );
218        close( tocmd[0] );
219        close( tocmd[1] );
220        return -1;
221    }
222
223    kid = fork();
224    if( 0 > kid )
225    {
226        close( tocmd[0] );
227        close( tocmd[1] );
228        close( fromcmd[0] );
229        close( fromcmd[1] );
230        return -1;
231    }
232    else if( 0 == kid )
233    {
234        if( 0 > dup2( tocmd[0], STDIN_FILENO ) ||
235            0 > dup2( fromcmd[1], STDOUT_FILENO ) )
236        {
237            errnomsg( "failed to duplicate descriptors" );
238            _exit( 1 );
239        }
240        close( tocmd[0] );
241        close( tocmd[1] );
242        close( fromcmd[0] );
243        close( fromcmd[1] );
244        execvp( cmd[0], cmd );
245        errnomsg( "failed to execute: %s", cmd[0] );
246        _exit( 1 );
247    }
248
249    close( tocmd[0] );
250    close( fromcmd[1] );
251
252    con = calloc( 1, sizeof *con );
253    if( NULL == con )
254    {
255        mallocmsg( sizeof *con );
256        close( tocmd[1] );
257        close( fromcmd[0] );
258        return -1;
259    }
260
261    con->infd = fromcmd[0];
262    con->evin = bufferevent_new( con->infd, canread, noop, ohshit, con );
263    if( NULL == con->evin )
264    {
265        free( con );
266        close( tocmd[1] );
267        close( fromcmd[0] );
268        return -1;
269    }
270    bufferevent_base_set( gl_base, con->evin );
271    bufferevent_settimeout( con->evin, SERVER_TIMEOUT, SERVER_TIMEOUT );
272    bufferevent_enable( con->evin, EV_READ );
273
274    con->outfd = tocmd[1];
275    con->evout = bufferevent_new( con->outfd, noway, didwrite, ohshit, con );
276    if( NULL == con->evout )
277    {
278        bufferevent_free( con->evin );
279        free( con );
280        close( tocmd[1] );
281        close( fromcmd[0] );
282        return -1;
283    }
284    bufferevent_base_set( gl_base, con->evout );
285    bufferevent_settimeout( con->evout, SERVER_TIMEOUT, SERVER_TIMEOUT );
286    bufferevent_enable( con->evout, EV_READ );
287
288    ipc_newcon( &con->ipc, gl_tree );
289    if( 0 > sendvers( con ) )
290    {
291        exit( 1 );
292    }
293
294    return 0;
295}
296
297struct req *
298addreq( enum ipc_msg id, int64_t tag, struct resp ** resp )
299{
300    struct req * req;
301
302    assert( ( 0 < tag && NULL != resp ) || ( 0 >= tag && NULL == resp ) );
303
304    req = calloc( 1, sizeof *req );
305    if( NULL == req )
306    {
307        mallocmsg( sizeof *req );
308        return NULL;
309    }
310
311    if( NULL != resp )
312    {
313        *resp = calloc( 1, sizeof **resp );
314        if( NULL == *resp )
315        {
316            mallocmsg( sizeof **resp );
317            free( req );
318            return NULL;
319        }
320        (*resp)->tag = tag;
321        RB_INSERT( resptree, &gl_resps, *resp );
322    }
323
324    req->id  = id;
325    req->tag = tag;
326    SLIST_INSERT_HEAD( &gl_reqs, req, next );
327
328    return req;
329}
330
331int
332client_quit( void )
333{
334    return ( NULL == addreq( IPC_MSG_QUIT, -1, NULL ) ? -1 : 0 );
335}
336
337int
338client_addfiles( struct strlist * list )
339{
340    struct stritem * ii;
341    uint8_t        * buf;
342    size_t           size;
343    struct req     * req;
344
345    if( gl_proxy )
346    {
347        SLIST_FOREACH( ii, list, next )
348        {
349            buf = readfile( ii->str, &size );
350            req = addreq( IPC_MSG_ADDONEFILE, -1, NULL );
351            if( NULL == req )
352            {
353                free( buf );
354                return -1;
355            }
356            req->buf     = buf;
357            req->listlen = size;
358        }
359    }
360    else
361    {
362        req = addreq( IPC_MSG_ADDMANYFILES, -1, NULL );
363        if( NULL == req )
364        {
365            return -1;
366        }
367
368        /* XXX need to move arg parsing back here or something */
369        req->strs = list;
370    }
371
372    return 0;
373}
374
375int
376client_automap( int automap )
377{
378    struct req * req;
379
380    req = addreq( IPC_MSG_AUTOMAP, -1, NULL );
381    if( NULL == req )
382    {
383        return -1;
384    }
385
386    req->num = ( automap ? 1 : 0 );
387
388    return 0;
389}
390
391int
392client_pex( int pex )
393{
394    struct req * req;
395
396    req = addreq( IPC_MSG_PEX, -1, NULL );
397    if( NULL == req )
398    {
399        return -1;
400    }
401
402    req->num = ( pex ? 1 : 0 );
403
404    return 0;
405}
406
407int
408client_port( int port )
409{
410    struct req * req;
411
412    req = addreq( IPC_MSG_PORT, -1, NULL );
413    if( NULL == req )
414    {
415        return -1;
416    }
417
418    req->num = port;
419
420    return 0;
421}
422
423int
424client_downlimit( int limit )
425{
426    struct req * req;
427
428    req = addreq( IPC_MSG_DOWNLIMIT, -1, NULL );
429    if( NULL == req )
430    {
431        return -1;
432    }
433
434    req->num = ( 0 > limit ? -1 : limit );
435
436    return 0;
437}
438
439int
440client_uplimit( int limit )
441{
442    struct req * req;
443
444    req = addreq( IPC_MSG_UPLIMIT, -1, NULL );
445    if( NULL == req )
446    {
447        return -1;
448    }
449
450    req->num = ( 0 > limit ? -1 : limit );
451
452    return 0;
453}
454
455int
456client_dir( const char * dir )
457{
458    struct req * req;
459    char       * dircpy;
460
461    dircpy = strdup( dir );
462    if( NULL == dircpy )
463    {
464        mallocmsg( strlen( dir ) );
465        return -1;
466    }
467
468    req = addreq( IPC_MSG_DIR, -1, NULL );
469    if( NULL == req )
470    {
471        free( dircpy );
472        return -1;
473    }
474
475    req->str = dircpy;
476
477    return 0;
478}
479
480int
481addintlistreq( enum ipc_msg which, size_t len, const int * list )
482{
483    struct req * req;
484    int64_t    * duplist;
485    size_t       ii;
486
487    assert( ( 0 == len && NULL == list ) || ( 0 < len && NULL != list ) );
488
489    duplist = NULL;
490    if( NULL != list )
491    {
492        duplist = calloc( len, sizeof duplist[0] );
493        if( NULL == duplist )
494        {
495            mallocmsg( len * sizeof( duplist[0] ) );
496            return -1;
497        }
498    }
499
500    req = addreq( which, -1, NULL );
501    if( NULL == req )
502    {
503        free( duplist );
504        return -1;
505    }
506
507    for( ii = 0; len > ii; ii++ )
508    {
509        duplist[ii] = list[ii];
510    }
511    req->listlen = len;
512    req->numlist = duplist;
513
514    return 0;
515}
516
517int
518client_start( size_t len, const int * list )
519{
520    enum ipc_msg id;
521
522    id = ( NULL == list ? IPC_MSG_STARTALL : IPC_MSG_START );
523
524    return addintlistreq( id, len, list );
525}
526
527int
528client_stop( size_t len, const int * list )
529{
530    enum ipc_msg id;
531
532    id = ( NULL == list ? IPC_MSG_STOPALL : IPC_MSG_STOP );
533
534    return addintlistreq( id, len, list );
535}
536
537int
538client_remove( size_t len, const int * list )
539{
540    enum ipc_msg id;
541
542    id = ( NULL == list ? IPC_MSG_REMOVEALL : IPC_MSG_REMOVE );
543
544    return addintlistreq( id, len, list );
545}
546
547int
548client_list( cl_listfunc func )
549{
550    struct req  * req;
551    struct resp * resp;
552
553    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
554    if( NULL == req )
555    {
556        return -1;
557    }
558
559    resp->listcb = func;
560    req->types   = IPC_INF_NAME | IPC_INF_HASH;
561
562    return 0;
563}
564
565int
566client_info( cl_infofunc func )
567{
568    struct req  * req;
569    struct resp * resp;
570
571    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
572    if( NULL == req )
573    {
574        return -1;
575    }
576
577    resp->infocb = func;
578    req->types   = IPC_INF_NAME | IPC_INF_HASH | IPC_INF_SIZE;
579
580    return 0;
581}
582
583int
584client_hashids( cl_hashfunc func )
585{
586    struct req  * req;
587    struct resp * resp;
588
589    req = addreq( IPC_MSG_GETINFOALL, ++gl_tag, &resp );
590    if( NULL == req )
591    {
592        return -1;
593    }
594
595    resp->hashcb = func;
596    req->types   = IPC_INF_HASH;
597
598    return 0;
599}
600
601int
602client_status( cl_statfunc func )
603{
604    struct req  * req;
605    struct resp * resp;
606
607    req = addreq( IPC_MSG_GETSTATALL, ++gl_tag, &resp );
608    if( NULL == req )
609    {
610        return -1;
611    }
612
613    resp->statcb = func;
614    req->types   = IPC_ST_STATE | IPC_ST_ETA | IPC_ST_COMPLETED |
615        IPC_ST_DOWNSPEED | IPC_ST_UPSPEED | IPC_ST_ERROR | IPC_ST_ERRMSG;
616
617    return 0;
618}
619
620void
621noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
622{
623    /* libevent prior to 1.2 couldn't handle a NULL write callback */
624}
625
626void
627noway( struct bufferevent * evin, void * arg UNUSED )
628{
629    /* this shouldn't happen, but let's drain the buffer anyway */
630    evbuffer_drain( EVBUFFER_INPUT( evin ),
631                    EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) ) );
632}
633
634void
635didwrite( struct bufferevent * evout, void * arg )
636{
637    struct con * con = arg;
638
639    assert( evout == con->evout );
640    flushreqs( con );
641}
642
643void
644ohshit( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
645{
646    if( EVBUFFER_EOF & what )
647    {
648        errmsg( "server closed connection" );
649    }
650    else if( EVBUFFER_TIMEOUT & what )
651    {
652        errmsg( "server connection timed out" );
653    }
654    else if( EVBUFFER_READ & what )
655    {
656        errmsg( "read error on server connection" );
657    }
658    else if( EVBUFFER_WRITE & what )
659    {
660        errmsg( "write error on server connection" );
661    }
662    else if( EVBUFFER_ERROR & what )
663    {
664        errmsg( "error on server connection" );
665    }
666    else
667    {
668        errmsg( "unknown error on server connection: 0x%x", what );
669    }
670    exit( 1 );
671}
672
673void
674canread( struct bufferevent * evin, void * arg )
675{
676    struct con * con = arg;
677    uint8_t    * buf;
678    size_t       len;
679    ssize_t      res;
680
681    assert( evin == con->evin );
682    buf = EVBUFFER_DATA( EVBUFFER_INPUT( evin ) );
683    len = EVBUFFER_LENGTH( EVBUFFER_INPUT( evin ) );
684
685    if( IPC_MIN_MSG_LEN > len )
686    {
687        return;
688    }
689
690    res = ipc_parse( &con->ipc, buf, len, con );
691    if( 0 > res )
692    {
693        switch( errno )
694        {
695            case EPERM:
696                errmsg( "unsupported protocol version" );
697                break;
698            case EINVAL:
699                errmsg( "protocol parse error" );
700                break;
701            default:
702                errnomsg( "parsing failed" );
703                break;
704        }
705        exit( 1 );
706    }
707
708    if( 0 < res )
709    {
710        evbuffer_drain( EVBUFFER_INPUT( evin ), res );
711        flushreqs( con );
712    }
713}
714
715void
716flushreqs( struct con * con )
717{
718    struct req     * req;
719    uint8_t        * buf;
720    size_t           buflen, ii;
721    benc_val_t       pk, * val;
722    struct stritem * jj;
723
724    if( !HASVERS( &con->ipc ) )
725    {
726        return;
727    }
728
729    if( SLIST_EMPTY( &gl_reqs ) && RB_EMPTY( &gl_resps ) )
730    {
731        exit( 0 );
732    }
733
734    while( !SLIST_EMPTY( &gl_reqs ) )
735    {
736        req = SLIST_FIRST( &gl_reqs );
737        SLIST_REMOVE_HEAD( &gl_reqs, next );
738        buf = NULL;
739        switch( req->id )
740        {
741            case IPC_MSG_QUIT:
742            case IPC_MSG_STARTALL:
743            case IPC_MSG_STOPALL:
744            case IPC_MSG_REMOVEALL:
745                buf = ipc_mkempty( &con->ipc, &buflen, req->id, req->tag );
746                break;
747            case IPC_MSG_ADDMANYFILES:
748                ii = 0;
749                SLIST_FOREACH( jj, req->strs, next )
750                {
751                    ii++;
752                }
753                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_LIST );
754                if( NULL != val && !tr_bencListReserve( val, ii ) )
755                {
756                    SLIST_FOREACH( jj, req->strs, next )
757                    {
758                        tr_bencInitStr( tr_bencListAdd( val ),
759                                        jj->str, -1, 1 );
760                    }
761                    buf = ipc_mkval( &pk, &buflen );
762                    SAFEBENCFREE( &pk );
763                }
764                SAFEFREESTRLIST( req->strs );
765                break;
766            case IPC_MSG_ADDONEFILE:
767                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_DICT );
768                if( NULL != val && !tr_bencDictReserve( val, 1 ) )
769                {
770                    tr_bencInitStr( tr_bencDictAdd( val, "data" ),
771                                    req->buf, req->listlen, 1 );
772                    buf = ipc_mkval( &pk, &buflen );
773                    SAFEBENCFREE( &pk );
774                }
775                SAFEFREE( req->buf );
776                break;
777            case IPC_MSG_AUTOMAP:
778            case IPC_MSG_PORT:
779            case IPC_MSG_DOWNLIMIT:
780            case IPC_MSG_UPLIMIT:
781                buf = ipc_mkint( &con->ipc, &buflen, req->id, -1, req->num );
782                break;
783            case IPC_MSG_DIR:
784                buf = ipc_mkstr( &con->ipc, &buflen, req->id, -1, req->str );
785                SAFEFREE( req->str );
786                break;
787            case IPC_MSG_START:
788            case IPC_MSG_STOP:
789            case IPC_MSG_REMOVE:
790                val = ipc_initval( &con->ipc, req->id, -1, &pk, TYPE_LIST );
791                if( NULL != val && !tr_bencListReserve( val, req->listlen ) )
792                {
793                    for( ii = 0; ii < req->listlen; ii++ )
794                    {
795                        tr_bencInitInt( tr_bencListAdd( val ),
796                                        req->numlist[ii] );
797                    }
798                    buf = ipc_mkval( &pk, &buflen );
799                    SAFEBENCFREE( &pk );
800                }
801                SAFEFREE( req->numlist );
802                break;
803            case IPC_MSG_GETINFOALL:
804            case IPC_MSG_GETSTATALL:
805                buf = ipc_mkgetinfo( &con->ipc, &buflen, req->id, req->tag,
806                                     req->types, NULL );
807                break;
808            default:
809                assert( 0 );
810                return;
811        }
812
813        SAFEFREE( req );
814        if( NULL == buf )
815        {
816            if( EPERM == errno )
817            {
818                errmsg( "message not supported by server" );
819            }
820            else
821            {
822                errnomsg( "failed to create message" );
823            }
824            exit( 1 );
825        }
826        if( 0 > bufferevent_write( con->evout, buf, buflen ) )
827        {
828            errmsg( "failed to buffer %zd bytes of data for write", buflen );
829            exit( 1 );
830        }
831        free( buf );
832    }
833}
834
835int
836sendvers( struct con * con )
837{
838    uint8_t   * buf;
839    size_t      len;
840
841    buf = ipc_mkvers( &len );
842    if( NULL == buf )
843    {
844        if( EPERM == errno )
845        {
846            errmsg( "message not supported by server" );
847        }
848        else
849        {
850            errnomsg( "failed to create message" );
851        }
852        return -1;
853    }
854
855    if( 0 > bufferevent_write( con->evout, buf, len ) )
856    {
857        free( buf );
858        errmsg( "failed to buffer %i bytes of data for write", ( int )len );
859        return -1;
860    }
861
862    free( buf );
863
864    return 0;
865}
866
867void
868infomsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
869         void * arg UNUSED )
870{
871    benc_val_t  * dict;
872    int           ii;
873    int64_t       id, size;
874    char        * name, * hash;
875    struct resp * resp, key;
876
877    assert( IPC_MSG_INFO == msgid );
878
879    if( TYPE_LIST != list->type )
880    {
881        return;
882    }
883
884    bzero( &key, sizeof key );
885    key.tag = tag;
886    resp = RB_FIND( resptree, &gl_resps, &key );
887    if( NULL == resp )
888    {
889        return;
890    }
891    RB_REMOVE( resptree, &gl_resps, resp );
892
893    for( ii = 0; list->val.l.count > ii; ii++ )
894    {
895        dict = &list->val.l.vals[ii];
896        if( TYPE_DICT != dict->type )
897        {
898            continue;
899        }
900
901        id   = getinfoint( msgid, dict, IPC_INF_ID,   -1   );
902        name = getinfostr( msgid, dict, IPC_INF_NAME, NULL );
903        hash = getinfostr( msgid, dict, IPC_INF_HASH, NULL );
904        size = getinfoint( msgid, dict, IPC_INF_SIZE, -1   );
905
906        if( !TORRENT_ID_VALID( id ) )
907        {
908            continue;
909        }
910
911        if( NULL != resp->infocb )
912        {
913            resp->infocb( id, name, size );
914        }
915        else if( NULL != resp->listcb )
916        {
917            resp->listcb( id, name, hash );
918        }
919        else if( NULL != resp->hashcb )
920        {
921            resp->hashcb( id, hash );
922        }
923    }
924
925    cbdone( resp );
926    free( resp );
927}
928
929void
930statmsg( enum ipc_msg msgid, benc_val_t * list, int64_t tag,
931         void * arg UNUSED )
932{
933    benc_val_t  * dict;
934    int           ii;
935    int64_t       id, eta, done, down, up;
936    char        * state, * error, * errmsg;
937    struct resp * resp, key;
938
939    assert( IPC_MSG_STAT == msgid );
940
941    if( TYPE_LIST != list->type )
942    {
943        return;
944    }
945
946    bzero( &key, sizeof key );
947    key.tag = tag;
948    resp = RB_FIND( resptree, &gl_resps, &key );
949    if( NULL == resp )
950    {
951        return;
952    }
953    RB_REMOVE( resptree, &gl_resps, resp );
954
955    for( ii = 0; list->val.l.count > ii; ii++ )
956    {
957        dict   = &list->val.l.vals[ii];
958        if( TYPE_DICT != dict->type )
959        {
960            continue;
961        }
962
963        id     = getinfoint( msgid, dict, IPC_ST_ID,        -1   );
964        state  = getinfostr( msgid, dict, IPC_ST_STATE,     NULL );
965        eta    = getinfoint( msgid, dict, IPC_ST_ETA,       -1   );
966        done   = getinfoint( msgid, dict, IPC_ST_COMPLETED, -1   );
967        down   = getinfoint( msgid, dict, IPC_ST_DOWNSPEED, -1   );
968        up     = getinfoint( msgid, dict, IPC_ST_UPSPEED,   -1   );
969        error  = getinfostr( msgid, dict, IPC_ST_ERROR,     NULL );
970        errmsg = getinfostr( msgid, dict, IPC_ST_ERRMSG,    NULL );
971
972        if( !TORRENT_ID_VALID( id ) )
973        {
974            continue;
975        }
976
977        if( NULL != resp->statcb )
978        {
979            resp->statcb( id, state, eta, done, down, up, error, errmsg );
980        }
981    }
982
983    cbdone( resp );
984    free( resp );
985}
986
987void
988defmsg( enum ipc_msg msgid, benc_val_t * val, int64_t tag, void * arg UNUSED )
989{
990    struct resp * resp, key;
991
992    switch( msgid )
993    {
994        case IPC_MSG_FAIL:
995            if( TYPE_STR == val->type && NULL != val->val.s.s )
996            {
997                errmsg( "request failed: %s", val->val.s.s );
998            }
999            else
1000            {
1001                errmsg( "request failed" );
1002            }
1003            break;
1004        case IPC_MSG_NOTSUP:
1005            errmsg( "request message not supported" );
1006            break;
1007        default:
1008            break;
1009    }
1010
1011    bzero( &key, sizeof key );
1012    key.tag = tag;
1013    resp = RB_FIND( resptree, &gl_resps, &key );
1014    if( NULL == resp )
1015    {
1016        return;
1017    }
1018    RB_REMOVE( resptree, &gl_resps, resp );
1019
1020    cbdone( resp );
1021    free( resp );
1022}
1023
1024void
1025cbdone( struct resp * resp )
1026{
1027    if( NULL != resp->infocb )
1028    {
1029        resp->infocb( -1, NULL, -1 );
1030    }
1031    else if( NULL != resp->listcb )
1032    {
1033        resp->listcb( -1, NULL, NULL );
1034    }
1035    else if( NULL != resp->hashcb )
1036    {
1037        resp->hashcb( -1, NULL );
1038    }
1039    else if( NULL != resp->statcb )
1040    {
1041        resp->statcb( -1, NULL, -1, -1, -1, -1, NULL, NULL );
1042    }
1043}
1044
1045int64_t
1046getinfoint( enum ipc_msg msgid, benc_val_t * dict, int type, int64_t defval  )
1047{
1048    benc_val_t * val;
1049
1050    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
1051
1052    if( NULL != val && TYPE_INT == val->type )
1053    {
1054        return val->val.i;
1055    }
1056
1057    return defval;
1058}
1059
1060char *
1061getinfostr( enum ipc_msg msgid, benc_val_t * dict, int type, char * defval  )
1062{
1063    benc_val_t * val;
1064
1065    val = tr_bencDictFind( dict, ipc_infoname( msgid, type ) );
1066
1067    if( NULL != val && TYPE_STR == val->type )
1068    {
1069        return val->val.s.s ;
1070    }
1071
1072    return defval;
1073}
Note: See TracBrowser for help on using the repository browser.