source: trunk/libtransmission/ipcparse.c @ 2391

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

Include trcompat.h for asprintf()
Don't mix function and non function pointers without casting.
Replace a couple bzero()s with memset()s.
Remove a stray ;
Include a missing header.

  • Property svn:keywords set to Date Rev Author Id
File size: 39.5 KB
Line 
1/******************************************************************************
2 * $Id: ipcparse.c 2391 2007-07-18 17:25:42Z 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 <assert.h>
26#include <errno.h>
27#include <limits.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "transmission.h"
33
34#include "ipcparse.h"
35#include "bsdtree.h"
36
37/* begin copy-paste from daemon/misc.h */
38
39#define ARRAYLEN( ary )         ( sizeof( ary ) / sizeof( (ary)[0] ) )
40
41#ifndef MIN
42#define MIN( aa, bb )           ( (aa) < (bb) ? (aa) : (bb) )
43#endif
44#ifndef MAX
45#define MAX( aa, bb )           ( (aa) > (bb) ? (aa) : (bb) )
46#endif
47
48#undef NULL
49#define NULL                    ( ( void * )0 )
50
51#define SAFEFREE( ptr )                                                       \
52    do                                                                        \
53    {                                                                         \
54        int saved = errno;                                                    \
55        free( ptr );                                                          \
56        errno = saved;                                                        \
57    }                                                                         \
58    while( 0 )
59#define SAFEFREESTRLIST( ptr )                                                \
60    do                                                                        \
61    {                                                                         \
62        int saved = errno;                                                    \
63        FREESTRLIST( ptr );                                                   \
64        errno = saved;                                                        \
65    }                                                                         \
66    while( 0 )
67#define SAFEBENCFREE( val )                                                   \
68    do                                                                        \
69    {                                                                         \
70        int saved = errno;                                                    \
71        tr_bencFree( val );                                                   \
72        errno = saved;                                                        \
73    }                                                                         \
74    while( 0 )
75
76#define INTCMP_FUNC( name, type, id )                                         \
77int                                                                           \
78name( struct type * _icf_first, struct type * _icf_second )                   \
79{                                                                             \
80    if( _icf_first->id < _icf_second->id )                                    \
81    {                                                                         \
82        return -1;                                                            \
83    }                                                                         \
84    else if( _icf_first->id > _icf_second->id )                               \
85    {                                                                         \
86        return 1;                                                             \
87    }                                                                         \
88    else                                                                      \
89    {                                                                         \
90        return 0;                                                             \
91    }                                                                         \
92}
93
94/* end copy-paste from daemon/misc.h */
95
96/* IPC protocol version */
97#define PROTO_VERS_MIN          ( 1 )
98#define PROTO_VERS_MAX          ( 2 )
99
100#define MSGVALID( id )          ( IPC__MSG_COUNT > (id) )
101#define MSGNAME( id )           ( gl_msgs[(id)].name )
102#define DICTPAYLOAD( info )     ( 2 > (info)->vers )
103
104struct msg
105{
106    const char        * name;
107    const int          minvers;
108    const enum ipc_msg id;
109    RB_ENTRY( msg )    link;
110};
111
112struct inf
113{
114    const char    * name;
115    const int       type;
116    RB_ENTRY( inf ) link;
117};
118
119struct msgfunc
120{
121    int             id;
122    trd_msgfunc     func;
123    RB_ENTRY( msgfunc ) link;
124};
125
126RB_HEAD( msgtree, msg );
127RB_HEAD( inftree, inf );
128RB_HEAD( functree, msgfunc );
129
130struct ipc_funcs
131{
132    struct functree msgs;
133    trd_msgfunc    def;
134};
135
136static struct msg gl_msgs[] =
137{
138    { "addfiles",            1, IPC_MSG_ADDMANYFILES, RB_ENTRY_INITIALIZER() },
139    { "addfile-detailed",    2, IPC_MSG_ADDONEFILE,   RB_ENTRY_INITIALIZER() },
140    { "automap",             2, IPC_MSG_AUTOMAP,      RB_ENTRY_INITIALIZER() },
141    { "autostart",           2, IPC_MSG_AUTOSTART,    RB_ENTRY_INITIALIZER() },
142    { "bad-format",          2, IPC_MSG_BAD,          RB_ENTRY_INITIALIZER() },
143    { "directory",           2, IPC_MSG_DIR,          RB_ENTRY_INITIALIZER() },
144    { "downlimit",           2, IPC_MSG_DOWNLIMIT,    RB_ENTRY_INITIALIZER() },
145    { "failed",              2, IPC_MSG_FAIL,         RB_ENTRY_INITIALIZER() },
146    { "get-automap",         2, IPC_MSG_GETAUTOMAP,   RB_ENTRY_INITIALIZER() },
147    { "get-autostart",       2, IPC_MSG_GETAUTOSTART, RB_ENTRY_INITIALIZER() },
148    { "get-directory",       2, IPC_MSG_GETDIR,       RB_ENTRY_INITIALIZER() },
149    { "get-downlimit",       2, IPC_MSG_GETDOWNLIMIT, RB_ENTRY_INITIALIZER() },
150    { "get-info",            2, IPC_MSG_GETINFO,      RB_ENTRY_INITIALIZER() },
151    { "get-info-all",        2, IPC_MSG_GETINFOALL,   RB_ENTRY_INITIALIZER() },
152    { "get-pex",             2, IPC_MSG_GETPEX,       RB_ENTRY_INITIALIZER() },
153    { "get-port",            2, IPC_MSG_GETPORT,      RB_ENTRY_INITIALIZER() },
154    { "get-status",          2, IPC_MSG_GETSTAT,      RB_ENTRY_INITIALIZER() },
155    { "get-status-all",      2, IPC_MSG_GETSTATALL,   RB_ENTRY_INITIALIZER() },
156    { "get-supported",       2, IPC_MSG_GETSUP,       RB_ENTRY_INITIALIZER() },
157    { "get-uplimit",         2, IPC_MSG_GETUPLIMIT,   RB_ENTRY_INITIALIZER() },
158    { "lookup",              2, IPC_MSG_LOOKUP,       RB_ENTRY_INITIALIZER() },
159    { "info",                2, IPC_MSG_INFO,         RB_ENTRY_INITIALIZER() },
160    { "noop",                2, IPC_MSG_NOOP,         RB_ENTRY_INITIALIZER() },
161    { "not-supported",       2, IPC_MSG_NOTSUP,       RB_ENTRY_INITIALIZER() },
162    { "pex",                 2, IPC_MSG_PEX,          RB_ENTRY_INITIALIZER() },
163    { "port",                2, IPC_MSG_PORT,         RB_ENTRY_INITIALIZER() },
164    { "quit",                1, IPC_MSG_QUIT,         RB_ENTRY_INITIALIZER() },
165    { "remove",              2, IPC_MSG_REMOVE,       RB_ENTRY_INITIALIZER() },
166    { "remove-all",          2, IPC_MSG_REMOVEALL,    RB_ENTRY_INITIALIZER() },
167    { "start",               2, IPC_MSG_START,        RB_ENTRY_INITIALIZER() },
168    { "start-all",           2, IPC_MSG_STARTALL,     RB_ENTRY_INITIALIZER() },
169    { "status",              2, IPC_MSG_STAT,         RB_ENTRY_INITIALIZER() },
170    { "stop",                2, IPC_MSG_STOP,         RB_ENTRY_INITIALIZER() },
171    { "stop-all",            2, IPC_MSG_STOPALL,      RB_ENTRY_INITIALIZER() },
172    { "succeeded",           2, IPC_MSG_OK,           RB_ENTRY_INITIALIZER() },
173    { "supported",           2, IPC_MSG_SUP,          RB_ENTRY_INITIALIZER() },
174    { "uplimit",             2, IPC_MSG_UPLIMIT,      RB_ENTRY_INITIALIZER() },
175    { "version",             1, IPC_MSG_VERSION,      RB_ENTRY_INITIALIZER() },
176};
177
178static struct inf gl_inf[] =
179{
180    { "comment",                IPC_INF_COMMENT,      RB_ENTRY_INITIALIZER() },
181    { "creator",                IPC_INF_CREATOR,      RB_ENTRY_INITIALIZER() },
182    { "date",                   IPC_INF_DATE,         RB_ENTRY_INITIALIZER() },
183    { "files",                  IPC_INF_FILES,        RB_ENTRY_INITIALIZER() },
184    { "hash",                   IPC_INF_HASH,         RB_ENTRY_INITIALIZER() },
185    { "id",                     IPC_INF_ID,           RB_ENTRY_INITIALIZER() },
186    { "name",                   IPC_INF_NAME,         RB_ENTRY_INITIALIZER() },
187    { "path",                   IPC_INF_PATH,         RB_ENTRY_INITIALIZER() },
188    { "private",                IPC_INF_PRIVATE,      RB_ENTRY_INITIALIZER() },
189    { "saved",                  IPC_INF_SAVED,        RB_ENTRY_INITIALIZER() },
190    { "size",                   IPC_INF_SIZE,         RB_ENTRY_INITIALIZER() },
191    { "trackers",               IPC_INF_TRACKERS,     RB_ENTRY_INITIALIZER() },
192};
193
194static struct inf gl_stat[] =
195{
196    { "completed",              IPC_ST_COMPLETED,     RB_ENTRY_INITIALIZER() },
197    { "download-speed",         IPC_ST_DOWNSPEED,     RB_ENTRY_INITIALIZER() },
198    { "download-total",         IPC_ST_DOWNTOTAL,     RB_ENTRY_INITIALIZER() },
199    { "download-valid",         IPC_ST_DOWNVALID,     RB_ENTRY_INITIALIZER() },
200    { "error",                  IPC_ST_ERROR,         RB_ENTRY_INITIALIZER() },
201    { "error-message",          IPC_ST_ERRMSG,        RB_ENTRY_INITIALIZER() },
202    { "eta",                    IPC_ST_ETA,           RB_ENTRY_INITIALIZER() },
203    { "id",                     IPC_ST_ID,            RB_ENTRY_INITIALIZER() },
204    { "peers-downloading",      IPC_ST_PEERDOWN,      RB_ENTRY_INITIALIZER() },
205    { "peers-from",             IPC_ST_PEERFROM,      RB_ENTRY_INITIALIZER() },
206    { "peers-total",            IPC_ST_PEERTOTAL,     RB_ENTRY_INITIALIZER() },
207    { "peers-uploading",        IPC_ST_PEERUP,        RB_ENTRY_INITIALIZER() },
208    { "running",                IPC_ST_RUNNING,       RB_ENTRY_INITIALIZER() },
209    { "state",                  IPC_ST_STATE,         RB_ENTRY_INITIALIZER() },
210    { "swarm-speed",            IPC_ST_SWARM,         RB_ENTRY_INITIALIZER() },
211    { "tracker",                IPC_ST_TRACKER,       RB_ENTRY_INITIALIZER() },
212    { "scrape-completed",       IPC_ST_TKDONE,        RB_ENTRY_INITIALIZER() },
213    { "scrape-leechers",        IPC_ST_TKLEECH,       RB_ENTRY_INITIALIZER() },
214    { "scrape-seeders",         IPC_ST_TKSEED,        RB_ENTRY_INITIALIZER() },
215    { "upload-speed",           IPC_ST_UPSPEED,       RB_ENTRY_INITIALIZER() },
216    { "upload-total",           IPC_ST_UPTOTAL,       RB_ENTRY_INITIALIZER() },
217};
218
219static int          handlevers ( struct ipc_info *, benc_val_t * );
220static int          handlemsgs ( struct ipc_info *, benc_val_t *, void * );
221static int          gotmsg     ( struct ipc_info *, benc_val_t *, benc_val_t *,
222                                 benc_val_t *, void * );
223static int          msgcmp     ( struct msg *, struct msg * );
224static int          infcmp     ( struct inf *, struct inf * );
225static struct msg * msglookup  ( const char * );
226static int          filltracker( benc_val_t *, const tr_tracker_info_t * );
227static int          handlercmp ( struct msgfunc *, struct msgfunc * );
228
229RB_GENERATE_STATIC( msgtree, msg, link, msgcmp )
230RB_GENERATE_STATIC( inftree, inf, link, infcmp )
231RB_GENERATE_STATIC( functree, msgfunc, link, handlercmp )
232INTCMP_FUNC( handlercmp, msgfunc, id )
233
234struct ipc_funcs *
235ipc_initmsgs( void )
236{
237    struct ipc_funcs * tree;
238
239    tree = malloc( sizeof *tree );
240    if( NULL != tree )
241    {
242        RB_INIT( &tree->msgs );
243        tree->def = (trd_msgfunc) NULL;
244    }
245
246    return tree;
247}
248
249int
250ipc_addmsg( struct ipc_funcs * tree, enum ipc_msg id, trd_msgfunc func )
251{
252    struct msgfunc key, * entry;
253
254    assert( MSGVALID( id ) );
255    assert( IPC_MSG_VERSION != id );
256
257    memset( &key, 0, sizeof key );
258    key.id = id;
259    entry = RB_FIND( functree, &tree->msgs, &key );
260    assert( NULL == entry );
261
262    entry = calloc( 1, sizeof *entry );
263    if( NULL == entry )
264    {
265        return -1;
266    }
267
268    entry->id   = id;
269    entry->func = func;
270    entry = RB_INSERT( functree, &tree->msgs, entry );
271    assert( NULL == entry );
272
273    return 0;
274}
275
276void
277ipc_setdefmsg( struct ipc_funcs * tree, trd_msgfunc func )
278{
279    tree->def = func;
280}
281
282void
283ipc_freemsgs( struct ipc_funcs * tree )
284{
285    struct msgfunc * ii, * next;
286
287    for( ii = RB_MIN( functree, &tree->msgs ); NULL != ii; ii = next )
288    {
289        next = RB_NEXT( functree, &tree->msgs, ii );
290        RB_REMOVE( functree, &tree->msgs, ii );
291        free( ii );
292    }
293    free( tree );
294}
295
296struct ipc_info *
297ipc_newcon( struct ipc_funcs * funcs )
298{
299    struct ipc_info * info;
300
301    info = calloc( 1, sizeof *info );
302    if( NULL != info )
303    {
304        info->funcs = funcs;
305        info->vers  = -1;
306    }
307
308    return info;
309}
310
311void
312ipc_freecon( struct ipc_info * info )
313{
314    if( NULL != info )
315    {
316        free( info->label );
317        free( info );
318    }
319}
320
321benc_val_t *
322ipc_initval( struct ipc_info * info, enum ipc_msg id, int64_t tag,
323             benc_val_t * pk, int type )
324{
325    benc_val_t * ret;
326
327    assert( MSGVALID( id ) );
328
329    if( !ipc_havemsg( info, id ) || ( 0 < tag && !ipc_havetags( info ) ) )
330    {
331        errno = EPERM;
332        return NULL;
333    }
334
335    if( DICTPAYLOAD( info ) )
336    {
337        tr_bencInit( pk, TYPE_DICT );
338        if( tr_bencDictReserve( pk, 1 ) )
339        {
340            return NULL;
341        }
342        ret = tr_bencDictAdd( pk, MSGNAME( id ) );
343    }
344    else
345    {
346        tr_bencInit( pk, TYPE_LIST );
347        if( tr_bencListReserve( pk, ( 0 < tag ? 3 : 2 ) ) )
348        {
349            return NULL;
350        }
351        tr_bencInitStr( tr_bencListAdd( pk ), MSGNAME( id ), -1, 1 );
352        ret = tr_bencListAdd( pk );
353        if( 0 < tag )
354        {
355            tr_bencInitInt( tr_bencListAdd( pk ), tag );
356        }
357    }
358
359    tr_bencInit( ret, type );
360
361    return ret;
362}
363
364uint8_t *
365ipc_mkval( benc_val_t * pk, size_t * len )
366{
367    char * buf, hex[IPC_MIN_MSG_LEN+1];
368    int    used, max;
369
370    used = IPC_MIN_MSG_LEN;
371    max  = IPC_MIN_MSG_LEN;
372    buf  = malloc( IPC_MIN_MSG_LEN );
373    if( NULL == buf )
374    {
375        return NULL;
376    }
377
378    if( tr_bencSave( pk, &buf, &used, &max ) )
379    {
380        SAFEFREE( buf );
381        return NULL;
382    }
383
384    /* ok, this check is pretty laughable */
385    if( IPC_MAX_MSG_LEN < used )
386    {
387        free( buf );
388        errno = EFBIG;
389        return NULL;
390    }
391
392    assert( 0 <= used );
393    snprintf( hex, sizeof hex, "%0*X",
394              IPC_MIN_MSG_LEN, used - IPC_MIN_MSG_LEN );
395    memcpy( buf, hex, IPC_MIN_MSG_LEN );
396    *len = used;
397
398    return ( uint8_t * )buf;
399}
400
401uint8_t *
402ipc_mkempty( struct ipc_info * info, size_t * len, enum ipc_msg id,
403             int64_t tag )
404{
405    benc_val_t pk;
406    uint8_t  * ret;
407
408    if( NULL == ipc_initval( info, id, tag, &pk, TYPE_STR ) )
409    {
410        return NULL;
411    }
412
413    ret = ipc_mkval( &pk, len );
414    SAFEBENCFREE( &pk );
415
416    return ret;
417}
418
419uint8_t *
420ipc_mkint( struct ipc_info * info, size_t * len, enum ipc_msg id, int64_t tag,
421           int64_t num )
422{
423    benc_val_t pk, * val;
424    uint8_t  * ret;
425
426    val = ipc_initval( info, id, tag, &pk, TYPE_INT );
427    if( NULL == val )
428    {
429        return NULL;
430    }
431
432    val->val.i = num;
433    ret = ipc_mkval( &pk, len );
434    SAFEBENCFREE( &pk );
435
436    return ret;
437}
438
439uint8_t *
440ipc_mkstr( struct ipc_info * info, size_t * len, enum ipc_msg id, int64_t tag,
441           const char * str )
442{
443    benc_val_t pk, * val;
444    uint8_t  * ret;
445
446    val = ipc_initval( info, id, tag, &pk, TYPE_STR );
447    if( NULL == val )
448    {
449        return NULL;
450    }
451
452    tr_bencInitStr( val, str, -1, 1 );
453    ret = ipc_mkval( &pk, len );
454    SAFEBENCFREE( &pk );
455
456    return ret;
457}
458
459uint8_t *
460ipc_mkvers( size_t * len, const char * label )
461{
462    benc_val_t pk, * dict;
463    uint8_t  * ret;
464 
465    tr_bencInit( &pk, TYPE_DICT );
466    if( tr_bencDictReserve( &pk, 1 ) )
467    {
468        return NULL;
469    }
470    dict = tr_bencDictAdd( &pk, MSGNAME( IPC_MSG_VERSION ) );
471
472    tr_bencInit( dict, TYPE_DICT );
473    if( tr_bencDictReserve( dict, ( NULL == label ? 2 : 3 ) ) )
474    {
475        SAFEBENCFREE( &pk );
476        return NULL;
477    }
478    tr_bencInitInt( tr_bencDictAdd( dict, "min" ), PROTO_VERS_MIN );
479    tr_bencInitInt( tr_bencDictAdd( dict, "max" ), PROTO_VERS_MAX );
480    if( NULL != label )
481        tr_bencInitStr( tr_bencDictAdd( dict, "label" ), label, -1, 1 );
482
483    ret = ipc_mkval( &pk, len );
484    SAFEBENCFREE( &pk );
485
486    return ret;
487}
488
489uint8_t *
490ipc_mkgetinfo( struct ipc_info * info, size_t * len, enum ipc_msg id,
491               int64_t tag, int types, const int * ids )
492{
493    benc_val_t   pk, * top, * idlist, * typelist;
494    size_t       ii, typecount, used;
495    struct inf * typearray;
496    uint8_t    * ret;
497
498    /* no ID list, send an -all message */
499    if( NULL == ids )
500    {
501        typelist = ipc_initval( info, id, tag, &pk, TYPE_LIST );
502        if( NULL == typelist )
503        {
504            return NULL;
505        }
506    }
507    else
508    {
509        top = ipc_initval( info, id, tag, &pk, TYPE_DICT );
510        if( NULL == top )
511        {
512            return NULL;
513        }
514        /* add the requested IDs */
515        if( tr_bencDictReserve( top, 2 ) )
516        {
517            SAFEBENCFREE( &pk );
518            return NULL;
519        }
520        idlist   = tr_bencDictAdd( top, "id" );
521        typelist = tr_bencDictAdd( top, "type" );
522        tr_bencInit( idlist, TYPE_LIST );
523        tr_bencInit( typelist, TYPE_LIST );
524        for( ii = 0; TORRENT_ID_VALID( ids[ii] ); ii++ )
525        {
526        }
527        if( tr_bencListReserve( idlist, ii ) )
528        {
529            SAFEBENCFREE( &pk );
530            return NULL;
531        }
532        for( ii = 0; TORRENT_ID_VALID( ids[ii] ); ii++ )
533        {
534            tr_bencInitInt( tr_bencListAdd( idlist ), ids[ii] );
535        }
536    }
537
538    /* get the type name array */
539    switch( id )
540    {
541        case IPC_MSG_GETINFO:
542        case IPC_MSG_GETINFOALL:
543            typecount = ARRAYLEN( gl_inf );
544            typearray = gl_inf;
545            break;
546        case IPC_MSG_GETSTAT:
547        case IPC_MSG_GETSTATALL:
548            typecount = ARRAYLEN( gl_stat );
549            typearray = gl_stat;
550            break;
551        default:
552            assert( 0 );
553            break;
554    }       
555
556    /* add the type names */
557    for( ii = used = 0; typecount > ii; ii++ )
558    {
559        if( types & ( 1 << ii ) )
560        {
561            used++;
562        }
563    }
564    if( tr_bencListReserve( typelist, used ) )
565    {
566        SAFEBENCFREE( &pk );
567        return NULL;
568    }
569    for( ii = 0; typecount > ii; ii++ )
570    {
571        if( !( types & ( 1 << ii ) ) )
572        {
573            continue;
574        }
575        assert( typearray[ii].type == ( 1 << ii ) );
576        tr_bencInitStr( tr_bencListAdd( typelist ),
577                        typearray[ii].name, -1, 1 );
578    }
579
580    /* generate packet */
581    ret = ipc_mkval( &pk, len );
582    SAFEBENCFREE( &pk );
583
584    return ret;
585}
586
587int
588ipc_addinfo( benc_val_t * list, int tor, const tr_info_t * inf, int types )
589{
590    benc_val_t * dict, * item, * file, * tier;
591    int          ii, jj, kk;
592
593    /* always send torrent id */
594    types |= IPC_INF_ID;
595
596    if( tr_bencListReserve( list, 1 ) )
597    {
598        return -1;
599    }
600
601    dict = tr_bencListAdd( list );
602    tr_bencInit( dict, TYPE_DICT );
603    for( ii = jj = 0; IPC_INF__MAX > 1 << ii; ii++ )
604    {
605        if( !( types & ( 1 << ii ) ) )
606        {
607            continue;
608        }
609        assert( ARRAYLEN( gl_inf ) > ( unsigned )ii );
610        assert( gl_inf[ii].type == ( 1 << ii ) );
611        /* check for missing optional info */
612        if( ( IPC_INF_COMMENT == ( 1 << ii ) && '\0' == inf->comment ) ||
613            ( IPC_INF_CREATOR == ( 1 << ii ) && '\0' == inf->creator ) ||
614            ( IPC_INF_DATE    == ( 1 << ii ) &&   0  >= inf->dateCreated ) )
615        {
616            continue;
617        }
618        jj++;
619    }
620    if( tr_bencDictReserve( dict, jj ) )
621    {
622        return -1;
623    }
624
625    for( ii = 0; IPC_INF__MAX > 1 << ii; ii++ )
626    {
627        if( !( types & ( 1 << ii ) ) )
628        {
629            continue;
630        }
631        /* check for missing optional info */
632        if( ( IPC_INF_COMMENT == ( 1 << ii ) && '\0' == inf->comment ) ||
633            ( IPC_INF_CREATOR == ( 1 << ii ) && '\0' == inf->creator ) ||
634            ( IPC_INF_DATE    == ( 1 << ii ) && 0 >= inf->dateCreated ) )
635        {
636            continue;
637        }
638
639        item = tr_bencDictAdd( dict, gl_inf[ii].name );
640        switch( 1 << ii )
641        {
642            case IPC_INF_COMMENT:
643                tr_bencInitStr( item, inf->comment, -1, 1 );
644                break;
645            case IPC_INF_CREATOR:
646                tr_bencInitStr( item, inf->creator, -1, 1 );
647                break;
648            case IPC_INF_DATE:
649                tr_bencInitInt( item, inf->dateCreated );
650                break;
651            case IPC_INF_FILES:
652                tr_bencInit( item, TYPE_LIST );
653                if( tr_bencListReserve( item, inf->fileCount ) )
654                {
655                    return -1;
656                }
657                for( jj = 0; inf->fileCount > jj; jj++ )
658                {
659                    file = tr_bencListAdd( item );
660                    tr_bencInit( file, TYPE_DICT );
661                    if( tr_bencDictReserve( file, 2 ) )
662                    {
663                        return -1;
664                    }
665                    tr_bencInitStr( tr_bencDictAdd( file, "name" ),
666                                    inf->files[jj].name, -1, 1 );
667                    tr_bencInitInt( tr_bencDictAdd( file, "size" ),
668                                    inf->files[jj].length );
669                }
670                break;
671            case IPC_INF_HASH:
672                tr_bencInitStr( item, inf->hashString, -1, 1 );
673                break;
674            case IPC_INF_ID:
675                tr_bencInitInt( item, tor );
676                break;
677            case IPC_INF_NAME:
678                tr_bencInitStr( item, inf->name, -1, 1 );
679                break;
680            case IPC_INF_PATH:
681                tr_bencInitStr( item, inf->torrent, -1, 1 );
682                break;
683            case IPC_INF_PRIVATE:
684                tr_bencInitInt( item,
685                                ( TR_FLAG_PRIVATE & inf->flags ? 1 : 0 ) );
686                break;
687            case IPC_INF_SAVED:
688                tr_bencInitInt( item,
689                                ( TR_FLAG_SAVE & inf->flags ? 1 : 0 ) );
690                break;
691            case IPC_INF_SIZE:
692                tr_bencInitInt( item, inf->totalSize );
693                break;
694            case IPC_INF_TRACKERS:
695                tr_bencInit( item, TYPE_LIST );
696                if( tr_bencListReserve( item, inf->trackerTiers ) )
697                {
698                    return -1;
699                }
700                for( jj = 0; inf->trackerTiers > jj; jj++ )
701                {
702                    tier = tr_bencListAdd( item );
703                    tr_bencInit( tier, TYPE_LIST );
704                    if( tr_bencListReserve( tier,
705                                            inf->trackerList[jj].count ) )
706                    {
707                        return -1;
708                    }
709                    for( kk = 0; inf->trackerList[jj].count > kk; kk++ )
710                    {
711                        if( 0 > filltracker( tr_bencListAdd( tier ),
712                                             &inf->trackerList[jj].list[kk] ) )
713                        {
714                            return -1;
715                        }
716                    }
717                }
718                break;
719            default:
720                assert( 0 );
721                break;
722        }
723    }
724
725    return 0;
726}
727
728int
729ipc_addstat( benc_val_t * list, int tor,
730             const tr_stat_t * st, int types )
731{
732    benc_val_t  * dict, * item;
733    int           ii, used;
734    unsigned int  error;
735
736    /* always send torrent id */
737    types |= IPC_ST_ID;
738
739    if( tr_bencListReserve( list, 1 ) )
740    {
741        return -1;
742    }
743    dict = tr_bencListAdd( list );
744
745    for( ii = used = 0; IPC_ST__MAX > 1 << ii; ii++ )
746    {
747        if( types & ( 1 << ii ) )
748        {
749            used++;
750        }
751    }
752    tr_bencInit( dict, TYPE_DICT );
753    if( tr_bencDictReserve( dict, used ) )
754    {
755        return -1;
756    }
757
758    for( ii = 0; IPC_ST__MAX > 1 << ii; ii++ )
759    {
760        if( !( types & ( 1 << ii ) ) )
761        {
762            continue;
763        }
764        assert( ARRAYLEN( gl_stat ) > ( unsigned )ii );
765        assert( gl_stat[ii].type == ( 1 << ii ) );
766        item = tr_bencDictAdd( dict, gl_stat[ii].name );
767        switch( 1 << ii )
768        {
769            case IPC_ST_COMPLETED:
770                tr_bencInitInt( item, st->downloadedValid );
771                break;
772            case IPC_ST_DOWNSPEED:
773                tr_bencInitInt( item, st->rateDownload * 1024 );
774                break;
775            case IPC_ST_DOWNTOTAL:
776                tr_bencInitInt( item, st->downloaded );
777                break;
778            case IPC_ST_DOWNVALID:
779                tr_bencInitInt( item, st->downloadedValid );
780                break;
781            case IPC_ST_ERROR:
782                error = st->error;
783                if( TR_OK == error )
784                {
785                    tr_bencInitStr( item, NULL, 0, 1 );
786                }
787                else if( TR_ERROR_ISSET( TR_ERROR_ASSERT, error ) )
788                {
789                    tr_bencInitStr( item, "assert", -1, 1 );
790                }
791                else if( TR_ERROR_ISSET( TR_ERROR_IO_PARENT, error ) )
792                {
793                    tr_bencInitStr( item, "io-parent", -1, 1 );
794                }
795                else if( TR_ERROR_ISSET( TR_ERROR_IO_PERMISSIONS, error ) )
796                {
797                    tr_bencInitStr( item, "io-permissions", -1, 1 );
798                }
799                else if( TR_ERROR_ISSET( TR_ERROR_IO_SPACE, error ) )
800                {
801                    tr_bencInitStr( item, "io-space", -1, 1 );
802                }
803                else if( TR_ERROR_ISSET( TR_ERROR_IO_FILE_TOO_BIG, error ) )
804                {
805                    tr_bencInitStr( item, "io-file-too-big", -1, 1 );
806                }
807                else if( TR_ERROR_ISSET( TR_ERROR_IO_OPEN_FILES, error ) )
808                {
809                    tr_bencInitStr( item, "io-open-files", -1, 1 );
810                }
811                else if( TR_ERROR_ISSET( TR_ERROR_IO_MASK, error ) )
812                {
813                    tr_bencInitStr( item, "io-other", -1, 1 );
814                }
815                else if( TR_ERROR_ISSET( TR_ERROR_TC_ERROR, error ) )
816                {
817                    tr_bencInitStr( item, "tracker-error", -1, 1 );
818                }
819                else if( TR_ERROR_ISSET( TR_ERROR_TC_WARNING, error ) )
820                {
821                    tr_bencInitStr( item, "tracker-warning", -1, 1 );
822                }
823                else
824                {
825                    tr_bencInitStr( item, "other", -1, 1 );
826                }
827                break;
828            case IPC_ST_ERRMSG:
829                if( '\0' == st->errorString[0] )
830                {
831                    tr_bencInitStr( item, NULL, 0, 1 );
832                }
833                else if( tr_bencInitStrDup( item, st->errorString ) )
834                {
835                    return -1;
836                }
837                break;
838            case IPC_ST_ETA:
839                tr_bencInitInt( item, st->eta );
840                break;
841            case IPC_ST_ID:
842                tr_bencInitInt( item, tor );
843                break;
844            case IPC_ST_PEERDOWN:
845                tr_bencInitInt( item, st->peersSendingToUs );
846                break;
847            case IPC_ST_PEERFROM:
848                tr_bencInit( item, TYPE_DICT );
849                if( tr_bencDictReserve( item, 4 ) )
850                {
851                    return -1;
852                }
853                tr_bencInitInt( tr_bencDictAdd( item, "incoming" ),
854                                st->peersFrom[TR_PEER_FROM_INCOMING] );
855                tr_bencInitInt( tr_bencDictAdd( item, "tracker" ),
856                                st->peersFrom[TR_PEER_FROM_TRACKER] );
857                tr_bencInitInt( tr_bencDictAdd( item, "cache" ),
858                                st->peersFrom[TR_PEER_FROM_CACHE] );
859                tr_bencInitInt( tr_bencDictAdd( item, "pex" ),
860                                st->peersFrom[TR_PEER_FROM_PEX] );
861                break;
862            case IPC_ST_PEERTOTAL:
863                tr_bencInitInt( item, st->peersTotal );
864                break;
865            case IPC_ST_PEERUP:
866                tr_bencInitInt( item, st->peersGettingFromUs );
867                break;
868            case IPC_ST_RUNNING:
869                tr_bencInitInt( item,
870                                ( TR_STATUS_INACTIVE & st->status ? 0 : 1 ) );
871                break;
872            case IPC_ST_STATE:
873                if( TR_STATUS_CHECK_WAIT & st->status )
874                {
875                    tr_bencInitStr( item, "waiting to checking", -1, 1 );
876                }
877                else if( TR_STATUS_CHECK & st->status )
878                {
879                    tr_bencInitStr( item, "checking", -1, 1 );
880                }
881                else if( TR_STATUS_DOWNLOAD & st->status )
882                {
883                    tr_bencInitStr( item, "downloading", -1, 1 );
884                }
885                else if( TR_STATUS_SEED & st->status )
886                {
887                    tr_bencInitStr( item, "seeding", -1, 1 );
888                }
889                else if( TR_STATUS_STOPPING & st->status )
890                {
891                    tr_bencInitStr( item, "stopping", -1, 1 );
892                }
893                else if( TR_STATUS_INACTIVE & st->status )
894                {
895                    tr_bencInitStr( item, "paused", -1, 1 );
896                }
897                else
898                {
899                    assert( 0 );
900                }
901                break;
902            case IPC_ST_SWARM:
903                tr_bencInitInt( item, st->swarmspeed * 1024 );
904                break;
905            case IPC_ST_TRACKER:
906                if( 0 > filltracker( item, st->tracker ) )
907                {
908                    return -1;
909                }
910                break;
911            case IPC_ST_TKDONE:
912                tr_bencInitInt( item, st->completedFromTracker );
913                break;
914            case IPC_ST_TKLEECH:
915                tr_bencInitInt( item, st->leechers );
916                break;
917            case IPC_ST_TKSEED:
918                tr_bencInitInt( item, st->seeders );
919                break;
920            case IPC_ST_UPSPEED:
921                tr_bencInitInt( item, st->rateUpload * 1024 );
922                break;
923            case IPC_ST_UPTOTAL:
924                tr_bencInitInt( item, st->uploaded );
925                break;
926            default:
927                assert( 0 );
928                break;
929        }
930    }
931
932    return 0;
933}
934
935ssize_t
936ipc_parse( struct ipc_info * info, uint8_t * buf, ssize_t total, void * arg )
937{
938    char        hex[IPC_MIN_MSG_LEN+1], * end;
939    ssize_t     off, len;
940    benc_val_t  benc;
941
942    for( off = 0; off + IPC_MIN_MSG_LEN < total; off += IPC_MIN_MSG_LEN + len )
943    {
944        memcpy( hex, buf + off, IPC_MIN_MSG_LEN );
945        hex[IPC_MIN_MSG_LEN] = '\0';
946        end = NULL;
947        len = strtol( hex, &end, 16 );
948        if( hex + IPC_MIN_MSG_LEN != end ||
949            0 > len || IPC_MAX_MSG_LEN < len )
950        {
951            errno = EINVAL;
952            return -1;
953        }
954        if( off + IPC_MIN_MSG_LEN + len > total )
955        {
956            break;
957        }
958        errno = 0;
959        if( tr_bencLoad( buf + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) )
960        {
961            if( 0 == errno )
962            {
963                errno = EINVAL;
964            }
965            return -1;
966        }
967        if( 0 > ( HASVERS( info ) ? handlemsgs( info, &benc, arg ) :
968                                    handlevers( info, &benc ) ) )
969        {
970            SAFEBENCFREE( &benc );
971            return -1;
972        }
973        tr_bencFree( &benc );
974    }
975
976    return off;
977}
978
979static int
980handlevers( struct ipc_info * info, benc_val_t * dict )
981{
982    benc_val_t * vers, * num;
983    int64_t      min, max;
984
985    if( TYPE_DICT != dict->type )
986    {
987        errno = EINVAL;
988        return -1;
989    }
990
991    vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) );
992    if( NULL == vers )
993    {
994        errno = EINVAL;
995        return -1;
996    }
997
998    switch( vers->type )
999    {
1000        case TYPE_INT:
1001            min = vers->val.i;
1002            max = vers->val.i;
1003            break;
1004        case TYPE_DICT:
1005            num = tr_bencDictFind( vers, "min" );
1006            min = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
1007            num = tr_bencDictFind( vers, "max" );
1008            max = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
1009            break;
1010        default:
1011            min = -1;
1012            max = -1;
1013            break;
1014    }
1015
1016    if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max )
1017    {
1018        errno = EINVAL;
1019        return -1;
1020    }
1021
1022    assert( PROTO_VERS_MIN <= PROTO_VERS_MAX );
1023    if( min > max )
1024    {
1025        errno = EINVAL;
1026        return -1;
1027    }
1028    if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max )
1029    {
1030        errno = EPERM;
1031        return -1;
1032    }
1033
1034    info->vers = MIN( PROTO_VERS_MAX, max );
1035
1036    return 0;
1037}
1038
1039static int
1040handlemsgs( struct ipc_info * info, benc_val_t * pay, void * arg )
1041{
1042    benc_val_t * name, * val, * tag;
1043    int          ii;
1044
1045    assert( HASVERS( info ) );
1046
1047    if( DICTPAYLOAD( info ) )
1048    {
1049        if( TYPE_DICT != pay->type || pay->val.l.count % 2 )
1050        {
1051            errno = EINVAL;
1052            return -1;
1053        }
1054
1055        for( ii = 0; ii < pay->val.l.count; ii += 2 )
1056        {
1057            assert( ii + 1 < pay->val.l.count );
1058            name = &pay->val.l.vals[ii];
1059            val  = &pay->val.l.vals[ii+1];
1060            if( 0 > gotmsg( info, name, val, NULL, arg ) )
1061            {
1062                return -1;
1063            }
1064        }
1065    }
1066    else
1067    {
1068        if( TYPE_LIST != pay->type || 2 > pay->val.l.count )
1069        {
1070            errno = EINVAL;
1071            return -1;
1072        }
1073
1074        name = &pay->val.l.vals[0];
1075        val  = &pay->val.l.vals[1];
1076        tag  = ( 2 == pay->val.l.count ? NULL : &pay->val.l.vals[2] );
1077        if( 0 > gotmsg( info, name, val, tag, arg ) )
1078        {
1079            return -1;
1080        }
1081    }
1082
1083    return 0;
1084}
1085
1086static int
1087gotmsg( struct ipc_info * info, benc_val_t * name, benc_val_t * val,
1088        benc_val_t * tagval, void * arg )
1089{
1090    struct msgfunc key, * handler;
1091    struct msg   * msg;
1092    int64_t        tag;
1093
1094    if( TYPE_STR != name->type )
1095    {
1096        errno = EINVAL;
1097        return -1;
1098    }
1099
1100    if( NULL == tagval )
1101    {
1102        tag = -1;
1103    }
1104    else
1105    {
1106        if( TYPE_INT != tagval->type )
1107        {
1108            errno = EINVAL;
1109            return -1;
1110        }
1111        tag = tagval->val.i;
1112    }
1113
1114    msg = msglookup( name->val.s.s );
1115    if( NULL != msg && msg->minvers <= info->vers )
1116    {
1117        memset( &key, 0, sizeof key );
1118        key.id  = msg->id;
1119        handler = RB_FIND( functree, &info->funcs->msgs, &key );
1120        if( NULL != handler )
1121        {
1122            handler->func( msg->id, val, tag, arg );
1123        }
1124        else if( NULL != info->funcs->def )
1125        {
1126            info->funcs->def( msg->id, val, tag, arg );
1127        }
1128    }
1129    else if( NULL != info->funcs->def )
1130        info->funcs->def( IPC__MSG_UNKNOWN, NULL, tag, arg );
1131
1132    return 0;
1133}
1134
1135int
1136ipc_havemsg( struct ipc_info * info, enum ipc_msg id )
1137{
1138    assert( MSGVALID( id ) );
1139    assert( HASVERS( info ) );
1140
1141    return ( gl_msgs[id].minvers <= info->vers );
1142}
1143
1144enum ipc_msg
1145ipc_msgid( struct ipc_info * info, const char * name )
1146{
1147    struct msg * msg;
1148
1149    msg = msglookup( name );
1150    if( NULL == msg || !ipc_havemsg( info, msg->id ) )
1151    {
1152        return IPC__MSG_COUNT;
1153    }
1154
1155    return msg->id;
1156}
1157
1158int
1159ipc_ishandled( struct ipc_info * info, enum ipc_msg id )
1160{
1161    struct msgfunc key;
1162
1163    assert( MSGVALID( id ) );
1164
1165    memset( &key, 0, sizeof key );
1166    key.id = id;
1167    return ( NULL != RB_FIND( functree, &info->funcs->msgs, &key ) );
1168}
1169
1170int
1171ipc_havetags( struct ipc_info * info )
1172{
1173    return !DICTPAYLOAD( info );
1174}
1175
1176int
1177ipc_infotypes( enum ipc_msg id, benc_val_t * list )
1178{
1179    static struct inftree infotree = RB_INITIALIZER( &tree );
1180    static struct inftree stattree = RB_INITIALIZER( &tree );
1181    struct inftree * tree;
1182    benc_val_t     * name;
1183    struct inf     * array, * inf, key;
1184    size_t           len, ii;
1185    int              ret, jj;
1186
1187    switch( id )
1188    {
1189        case IPC_MSG_INFO:
1190            tree  = &infotree;
1191            array = gl_inf;
1192            len   = ARRAYLEN( gl_inf );
1193            break;
1194        case IPC_MSG_STAT:
1195            tree  = &stattree;
1196            array = gl_stat;
1197            len   = ARRAYLEN( gl_stat );
1198            break;
1199        default:
1200            assert( 0 );
1201            break;
1202    }
1203
1204    if( RB_EMPTY( tree ) )
1205    {
1206        for( ii = 0; len > ii; ii++ )
1207        {
1208            assert( 1 << ii == array[ii].type );
1209            inf = RB_INSERT( inftree, tree, &array[ii] );
1210            assert( NULL == inf );
1211        }
1212    }
1213
1214    ret = IPC_INF_ID;
1215
1216    if( NULL == list || TYPE_LIST != list->type )
1217    {
1218        return ret;
1219    }
1220
1221    memset( &key, 0, sizeof key );
1222    for( jj = 0; list->val.l.count > jj; jj++ )
1223    {
1224        name = &list->val.l.vals[jj];
1225        if( TYPE_STR != name->type )
1226        {
1227            continue;
1228        }
1229        key.name = name->val.s.s;
1230        inf = RB_FIND( inftree, tree, &key );
1231        if( NULL != inf )
1232        {
1233            ret |= inf->type;
1234        }
1235    }
1236
1237    return ret;
1238}
1239
1240const char *
1241ipc_infoname( enum ipc_msg id, int type )
1242{
1243    struct inf * array;
1244    size_t len, ii;
1245
1246    switch( id )
1247    {
1248        case IPC_MSG_INFO:
1249            array = gl_inf;
1250            len   = ARRAYLEN( gl_inf );
1251            break;
1252        case IPC_MSG_STAT:
1253            array = gl_stat;
1254            len   = ARRAYLEN( gl_stat );
1255            break;
1256        default:
1257            assert( 0 );
1258            break;
1259    }
1260
1261    for( ii = 0; len > ii; ii++ )
1262    {
1263        if( array[ii].type == type )
1264        {
1265            return array[ii].name;
1266        }
1267    }
1268
1269    assert( 0 );
1270
1271    return NULL;
1272}
1273
1274static int
1275msgcmp( struct msg * first, struct msg * second )
1276{
1277    return strcmp( first->name, second->name );
1278}
1279
1280static int
1281infcmp( struct inf * first, struct inf * second )
1282{
1283    return strcmp( first->name, second->name );
1284}
1285
1286static struct msg *
1287msglookup( const char * name )
1288{
1289    static struct msgtree tree = RB_INITIALIZER( &tree );
1290    struct msg          * ret, key;
1291    size_t                ii;
1292
1293    assert( IPC__MSG_COUNT == ARRAYLEN( gl_msgs ) );
1294
1295    if( RB_EMPTY( &tree ) )
1296    {
1297        for( ii = 0; ARRAYLEN( gl_msgs ) > ii; ii++ )
1298        {
1299            assert( ii == gl_msgs[ii].id );
1300            ret = RB_INSERT( msgtree, &tree, &gl_msgs[ii] );
1301            assert( NULL == ret );
1302        }
1303    }
1304
1305    memset( &key, 0, sizeof key );
1306    key.name = name;
1307    return RB_FIND( msgtree, &tree, &key );
1308}
1309
1310static int
1311filltracker( benc_val_t * val, const tr_tracker_info_t * tk )
1312{
1313    tr_bencInit( val, TYPE_DICT );
1314    if( tr_bencDictReserve( val, ( NULL == tk->scrape ? 3 : 4 ) ) )
1315    {
1316        return -1;
1317    }
1318
1319    tr_bencInitStr( tr_bencDictAdd( val, "address" ),  tk->address,  -1, 1 );
1320    tr_bencInitInt( tr_bencDictAdd( val, "port" ),     tk->port );
1321    tr_bencInitStr( tr_bencDictAdd( val, "announce" ), tk->announce, -1, 1 );
1322    if( NULL != tk->scrape )
1323    {
1324        tr_bencInitStr( tr_bencDictAdd( val, "scrape" ), tk->scrape, -1, 1 );
1325    }
1326
1327    return 0;
1328}
Note: See TracBrowser for help on using the repository browser.