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

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

Minor cleanup of client code.
Display ratio in transmission-remote -l output.

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