source: trunk/libtransmission/ipcparse.c @ 3809

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

tiennou's fix for #463

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