source: trunk/libtransmission/ipcparse.c @ 3426

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

remove unused flags

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