source: trunk/libtransmission/ipcparse.c @ 5127

Last change on this file since 5127 was 5127, checked in by charles, 14 years ago

more housekeeping: benc_val_t --> tr_benc

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