source: trunk/libtransmission/ipcparse.c @ 3484

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

Dead code removal (TR_STATUS_STOPPING)

  • Property svn:keywords set to Date Rev Author Id
File size: 39.0 KB
Line 
1/******************************************************************************
2 * $Id: ipcparse.c 3484 2007-10-20 21:17:37Z 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_INACTIVE & st->status )
882                {
883                    tr_bencInitStr( item, "paused", -1, 1 );
884                }
885                else
886                {
887                    assert( 0 );
888                }
889                break;
890            case IPC_ST_SWARM:
891                tr_bencInitInt( item, st->swarmspeed * 1024 );
892                break;
893            case IPC_ST_TRACKER:
894                if( 0 > filltracker( item, st->tracker ) )
895                {
896                    return -1;
897                }
898                break;
899            case IPC_ST_TKDONE:
900                tr_bencInitInt( item, st->completedFromTracker );
901                break;
902            case IPC_ST_TKLEECH:
903                tr_bencInitInt( item, st->leechers );
904                break;
905            case IPC_ST_TKSEED:
906                tr_bencInitInt( item, st->seeders );
907                break;
908            case IPC_ST_UPSPEED:
909                tr_bencInitInt( item, st->rateUpload * 1024 );
910                break;
911            case IPC_ST_UPTOTAL:
912                tr_bencInitInt( item, st->uploadedEver );
913                break;
914            default:
915                assert( 0 );
916                break;
917        }
918    }
919
920    return 0;
921}
922
923ssize_t
924ipc_parse( struct ipc_info * info, uint8_t * buf, ssize_t total, void * arg )
925{
926    char        hex[IPC_MIN_MSG_LEN+1], * end;
927    ssize_t     off, len;
928    benc_val_t  benc;
929
930    for( off = 0; off + IPC_MIN_MSG_LEN < total; off += IPC_MIN_MSG_LEN + len )
931    {
932        memcpy( hex, buf + off, IPC_MIN_MSG_LEN );
933        hex[IPC_MIN_MSG_LEN] = '\0';
934        end = NULL;
935        len = strtol( hex, &end, 16 );
936        if( hex + IPC_MIN_MSG_LEN != end ||
937            0 > len || IPC_MAX_MSG_LEN < len )
938        {
939            errno = EINVAL;
940            return -1;
941        }
942        if( off + IPC_MIN_MSG_LEN + len > total )
943        {
944            break;
945        }
946        errno = 0;
947        if( tr_bencLoad( buf + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) )
948        {
949            if( 0 == errno )
950            {
951                errno = EINVAL;
952            }
953            return -1;
954        }
955        if( 0 > ( HASVERS( info ) ? handlemsgs( info, &benc, arg ) :
956                                    handlevers( info, &benc ) ) )
957        {
958            SAFEBENCFREE( &benc );
959            return -1;
960        }
961        tr_bencFree( &benc );
962    }
963
964    return off;
965}
966
967static int
968handlevers( struct ipc_info * info, benc_val_t * dict )
969{
970    benc_val_t * vers, * num;
971    int64_t      min, max;
972
973    if( TYPE_DICT != dict->type )
974    {
975        errno = EINVAL;
976        return -1;
977    }
978
979    vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) );
980    if( NULL == vers )
981    {
982        errno = EINVAL;
983        return -1;
984    }
985
986    switch( vers->type )
987    {
988        case TYPE_INT:
989            min = vers->val.i;
990            max = vers->val.i;
991            break;
992        case TYPE_DICT:
993            num = tr_bencDictFind( vers, "min" );
994            min = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
995            num = tr_bencDictFind( vers, "max" );
996            max = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
997            break;
998        default:
999            min = -1;
1000            max = -1;
1001            break;
1002    }
1003
1004    if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max )
1005    {
1006        errno = EINVAL;
1007        return -1;
1008    }
1009
1010    assert( PROTO_VERS_MIN <= PROTO_VERS_MAX );
1011    if( min > max )
1012    {
1013        errno = EINVAL;
1014        return -1;
1015    }
1016    if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max )
1017    {
1018        errno = EPERM;
1019        return -1;
1020    }
1021
1022    info->vers = MIN( PROTO_VERS_MAX, max );
1023
1024    return 0;
1025}
1026
1027static int
1028handlemsgs( struct ipc_info * info, benc_val_t * pay, void * arg )
1029{
1030    benc_val_t * name, * val, * tag;
1031    int          ii;
1032
1033    assert( HASVERS( info ) );
1034
1035    if( DICTPAYLOAD( info ) )
1036    {
1037        if( TYPE_DICT != pay->type || pay->val.l.count % 2 )
1038        {
1039            errno = EINVAL;
1040            return -1;
1041        }
1042
1043        for( ii = 0; ii < pay->val.l.count; ii += 2 )
1044        {
1045            assert( ii + 1 < pay->val.l.count );
1046            name = &pay->val.l.vals[ii];
1047            val  = &pay->val.l.vals[ii+1];
1048            if( 0 > gotmsg( info, name, val, NULL, arg ) )
1049            {
1050                return -1;
1051            }
1052        }
1053    }
1054    else
1055    {
1056        if( TYPE_LIST != pay->type || 2 > pay->val.l.count )
1057        {
1058            errno = EINVAL;
1059            return -1;
1060        }
1061
1062        name = &pay->val.l.vals[0];
1063        val  = &pay->val.l.vals[1];
1064        tag  = ( 2 == pay->val.l.count ? NULL : &pay->val.l.vals[2] );
1065        if( 0 > gotmsg( info, name, val, tag, arg ) )
1066        {
1067            return -1;
1068        }
1069    }
1070
1071    return 0;
1072}
1073
1074static int
1075gotmsg( struct ipc_info * info, benc_val_t * name, benc_val_t * val,
1076        benc_val_t * tagval, void * arg )
1077{
1078    struct msgfunc key, * handler;
1079    struct msg   * msg;
1080    int64_t        tag;
1081
1082    if( TYPE_STR != name->type )
1083    {
1084        errno = EINVAL;
1085        return -1;
1086    }
1087
1088    if( NULL == tagval )
1089    {
1090        tag = -1;
1091    }
1092    else
1093    {
1094        if( TYPE_INT != tagval->type )
1095        {
1096            errno = EINVAL;
1097            return -1;
1098        }
1099        tag = tagval->val.i;
1100    }
1101
1102    msg = msglookup( name->val.s.s );
1103    if( NULL != msg && msg->minvers <= info->vers )
1104    {
1105        memset( &key, 0, sizeof key );
1106        key.id  = msg->id;
1107        handler = RB_FIND( functree, &info->funcs->msgs, &key );
1108        if( NULL != handler )
1109        {
1110            handler->func( msg->id, val, tag, arg );
1111        }
1112        else if( NULL != info->funcs->def )
1113        {
1114            info->funcs->def( msg->id, val, tag, arg );
1115        }
1116    }
1117    else if( NULL != info->funcs->def )
1118        info->funcs->def( IPC__MSG_UNKNOWN, NULL, tag, arg );
1119
1120    return 0;
1121}
1122
1123int
1124ipc_havemsg( struct ipc_info * info, enum ipc_msg id )
1125{
1126    assert( MSGVALID( id ) );
1127    assert( HASVERS( info ) );
1128
1129    return ( gl_msgs[id].minvers <= info->vers );
1130}
1131
1132enum ipc_msg
1133ipc_msgid( struct ipc_info * info, const char * name )
1134{
1135    struct msg * msg;
1136
1137    msg = msglookup( name );
1138    if( NULL == msg || !ipc_havemsg( info, msg->id ) )
1139    {
1140        return IPC__MSG_COUNT;
1141    }
1142
1143    return msg->id;
1144}
1145
1146int
1147ipc_ishandled( struct ipc_info * info, enum ipc_msg id )
1148{
1149    struct msgfunc key;
1150
1151    assert( MSGVALID( id ) );
1152
1153    memset( &key, 0, sizeof key );
1154    key.id = id;
1155    return ( NULL != RB_FIND( functree, &info->funcs->msgs, &key ) );
1156}
1157
1158int
1159ipc_havetags( struct ipc_info * info )
1160{
1161    return !DICTPAYLOAD( info );
1162}
1163
1164int
1165ipc_infotypes( enum ipc_msg id, benc_val_t * list )
1166{
1167    static struct inftree infotree = RB_INITIALIZER( &tree );
1168    static struct inftree stattree = RB_INITIALIZER( &tree );
1169    struct inftree * tree;
1170    benc_val_t     * name;
1171    struct inf     * array, * inf, key;
1172    size_t           len, ii;
1173    int              ret, jj;
1174
1175    switch( id )
1176    {
1177        case IPC_MSG_INFO:
1178            tree  = &infotree;
1179            array = gl_inf;
1180            len   = ARRAYLEN( gl_inf );
1181            break;
1182        case IPC_MSG_STAT:
1183            tree  = &stattree;
1184            array = gl_stat;
1185            len   = ARRAYLEN( gl_stat );
1186            break;
1187        default:
1188            assert( 0 );
1189            break;
1190    }
1191
1192    if( RB_EMPTY( tree ) )
1193    {
1194        for( ii = 0; len > ii; ii++ )
1195        {
1196            assert( 1 << ii == array[ii].type );
1197            inf = RB_INSERT( inftree, tree, &array[ii] );
1198            assert( NULL == inf );
1199        }
1200    }
1201
1202    ret = IPC_INF_ID;
1203
1204    if( NULL == list || TYPE_LIST != list->type )
1205    {
1206        return ret;
1207    }
1208
1209    memset( &key, 0, sizeof key );
1210    for( jj = 0; list->val.l.count > jj; jj++ )
1211    {
1212        name = &list->val.l.vals[jj];
1213        if( TYPE_STR != name->type )
1214        {
1215            continue;
1216        }
1217        key.name = name->val.s.s;
1218        inf = RB_FIND( inftree, tree, &key );
1219        if( NULL != inf )
1220        {
1221            ret |= inf->type;
1222        }
1223    }
1224
1225    return ret;
1226}
1227
1228const char *
1229ipc_infoname( enum ipc_msg id, int type )
1230{
1231    struct inf * array;
1232    size_t len, ii;
1233
1234    switch( id )
1235    {
1236        case IPC_MSG_INFO:
1237            array = gl_inf;
1238            len   = ARRAYLEN( gl_inf );
1239            break;
1240        case IPC_MSG_STAT:
1241            array = gl_stat;
1242            len   = ARRAYLEN( gl_stat );
1243            break;
1244        default:
1245            assert( 0 );
1246            break;
1247    }
1248
1249    for( ii = 0; len > ii; ii++ )
1250    {
1251        if( array[ii].type == type )
1252        {
1253            return array[ii].name;
1254        }
1255    }
1256
1257    assert( 0 );
1258
1259    return NULL;
1260}
1261
1262static int
1263msgcmp( struct msg * first, struct msg * second )
1264{
1265    return strcmp( first->name, second->name );
1266}
1267
1268static int
1269infcmp( struct inf * first, struct inf * second )
1270{
1271    return strcmp( first->name, second->name );
1272}
1273
1274static struct msg *
1275msglookup( const char * name )
1276{
1277    static struct msgtree tree = RB_INITIALIZER( &tree );
1278    struct msg          * ret, key;
1279    size_t                ii;
1280
1281    assert( IPC__MSG_COUNT == ARRAYLEN( gl_msgs ) );
1282
1283    if( RB_EMPTY( &tree ) )
1284    {
1285        for( ii = 0; ARRAYLEN( gl_msgs ) > ii; ii++ )
1286        {
1287            assert( ii == gl_msgs[ii].id );
1288            ret = RB_INSERT( msgtree, &tree, &gl_msgs[ii] );
1289            assert( NULL == ret );
1290        }
1291    }
1292
1293    memset( &key, 0, sizeof key );
1294    key.name = name;
1295    return RB_FIND( msgtree, &tree, &key );
1296}
1297
1298static int
1299filltracker( benc_val_t * val, const tr_tracker_info * tk )
1300{
1301    tr_bencInit( val, TYPE_DICT );
1302    if( tr_bencDictReserve( val, ( NULL == tk->scrape ? 3 : 4 ) ) )
1303    {
1304        return -1;
1305    }
1306
1307    tr_bencInitStr( tr_bencDictAdd( val, "address" ),  tk->address,  -1, 1 );
1308    tr_bencInitInt( tr_bencDictAdd( val, "port" ),     tk->port );
1309    tr_bencInitStr( tr_bencDictAdd( val, "announce" ), tk->announce, -1, 1 );
1310    if( NULL != tk->scrape )
1311    {
1312        tr_bencInitStr( tr_bencDictAdd( val, "scrape" ), tk->scrape, -1, 1 );
1313    }
1314
1315    return 0;
1316}
Note: See TracBrowser for help on using the repository browser.