source: trunk/libtransmission/ipcparse.c @ 2369

Last change on this file since 2369 was 2369, checked in by charles, 15 years ago

disambiguate some of the tr_stat_t variables' names

  • Property svn:keywords set to Date Rev Author Id
File size: 39.5 KB
Line 
1/******************************************************************************
2 * $Id: ipcparse.c 2369 2007-07-16 00:50:43Z charles $
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 = 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    bzero( &key, 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        bzero( &key, 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    bzero( &key, 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    bzero( &key, 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    bzero( &key, 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.