source: trunk/libtransmission/ipcparse.c @ 2227

Last change on this file since 2227 was 2227, checked in by joshe, 15 years ago

Add client/server name to IPC version handshake.

  • Property svn:keywords set to Date Rev Author Id
File size: 39.1 KB
Line 
1/******************************************************************************
2 * $Id: ipcparse.c 2227 2007-06-29 02:21:29Z joshe $
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 <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "transmission.h"
32
33#include "ipcparse.h"
34#include "bsdtree.h"
35
36/* begin copy-paste from daemon/misc.h */
37
38#define ARRAYLEN( ary )         ( sizeof( ary ) / sizeof( (ary)[0] ) )
39
40#ifndef MIN
41#define MIN( aa, bb )           ( (aa) < (bb) ? (aa) : (bb) )
42#endif
43#ifndef MAX
44#define MAX( aa, bb )           ( (aa) > (bb) ? (aa) : (bb) )
45#endif
46
47#undef NULL
48#define NULL                    ( ( void * )0 )
49
50#define SAFEFREE( ptr )                                                       \
51    do                                                                        \
52    {                                                                         \
53        int saved = errno;                                                    \
54        free( ptr );                                                          \
55        errno = saved;                                                        \
56    }                                                                         \
57    while( 0 )
58#define SAFEFREESTRLIST( ptr )                                                \
59    do                                                                        \
60    {                                                                         \
61        int saved = errno;                                                    \
62        FREESTRLIST( ptr );                                                   \
63        errno = saved;                                                        \
64    }                                                                         \
65    while( 0 )
66#define SAFEBENCFREE( val )                                                   \
67    do                                                                        \
68    {                                                                         \
69        int saved = errno;                                                    \
70        tr_bencFree( val );                                                   \
71        errno = saved;                                                        \
72    }                                                                         \
73    while( 0 )
74
75#define INTCMP_FUNC( name, type, id )                                         \
76int                                                                           \
77name( struct type * _icf_first, struct type * _icf_second )                   \
78{                                                                             \
79    if( _icf_first->id < _icf_second->id )                                    \
80    {                                                                         \
81        return -1;                                                            \
82    }                                                                         \
83    else if( _icf_first->id > _icf_second->id )                               \
84    {                                                                         \
85        return 1;                                                             \
86    }                                                                         \
87    else                                                                      \
88    {                                                                         \
89        return 0;                                                             \
90    }                                                                         \
91}
92
93/* end copy-paste from daemon/misc.h */
94
95/* IPC protocol version */
96#define PROTO_VERS_MIN          ( 1 )
97#define PROTO_VERS_MAX          ( 2 )
98
99#define MSGVALID( id )          ( IPC__MSG_COUNT > (id) )
100#define MSGNAME( id )           ( gl_msgs[(id)].name )
101#define DICTPAYLOAD( info )     ( 2 > (info)->vers )
102
103struct msg
104{
105    const char        * name;
106    const int          minvers;
107    const enum ipc_msg id;
108    RB_ENTRY( msg )    link;
109};
110
111struct inf
112{
113    const char    * name;
114    const int       type;
115    RB_ENTRY( inf ) link;
116};
117
118struct msgfunc
119{
120    int             id;
121    trd_msgfunc     func;
122    RB_ENTRY( msgfunc ) link;
123};
124
125RB_HEAD( msgtree, msg );
126RB_HEAD( inftree, inf );
127RB_HEAD( functree, msgfunc );
128
129struct ipc_funcs
130{
131    struct functree msgs;
132    trd_msgfunc    def;
133};
134
135static struct msg gl_msgs[] =
136{
137    { "addfiles",            1, IPC_MSG_ADDMANYFILES, RB_ENTRY_INITIALIZER() },
138    { "addfile-detailed",    2, IPC_MSG_ADDONEFILE,   RB_ENTRY_INITIALIZER() },
139    { "automap",             2, IPC_MSG_AUTOMAP,      RB_ENTRY_INITIALIZER() },
140    { "autostart",           2, IPC_MSG_AUTOSTART,    RB_ENTRY_INITIALIZER() },
141    { "bad-format",          2, IPC_MSG_BAD,          RB_ENTRY_INITIALIZER() },
142    { "directory",           2, IPC_MSG_DIR,          RB_ENTRY_INITIALIZER() },
143    { "downlimit",           2, IPC_MSG_DOWNLIMIT,    RB_ENTRY_INITIALIZER() },
144    { "failed",              2, IPC_MSG_FAIL,         RB_ENTRY_INITIALIZER() },
145    { "get-automap",         2, IPC_MSG_GETAUTOMAP,   RB_ENTRY_INITIALIZER() },
146    { "get-autostart",       2, IPC_MSG_GETAUTOSTART, RB_ENTRY_INITIALIZER() },
147    { "get-directory",       2, IPC_MSG_GETDIR,       RB_ENTRY_INITIALIZER() },
148    { "get-downlimit",       2, IPC_MSG_GETDOWNLIMIT, RB_ENTRY_INITIALIZER() },
149    { "get-info",            2, IPC_MSG_GETINFO,      RB_ENTRY_INITIALIZER() },
150    { "get-info-all",        2, IPC_MSG_GETINFOALL,   RB_ENTRY_INITIALIZER() },
151    { "get-pex",             2, IPC_MSG_GETPEX,       RB_ENTRY_INITIALIZER() },
152    { "get-port",            2, IPC_MSG_GETPORT,      RB_ENTRY_INITIALIZER() },
153    { "get-status",          2, IPC_MSG_GETSTAT,      RB_ENTRY_INITIALIZER() },
154    { "get-status-all",      2, IPC_MSG_GETSTATALL,   RB_ENTRY_INITIALIZER() },
155    { "get-supported",       2, IPC_MSG_GETSUP,       RB_ENTRY_INITIALIZER() },
156    { "get-uplimit",         2, IPC_MSG_GETUPLIMIT,   RB_ENTRY_INITIALIZER() },
157    { "lookup",              2, IPC_MSG_LOOKUP,       RB_ENTRY_INITIALIZER() },
158    { "info",                2, IPC_MSG_INFO,         RB_ENTRY_INITIALIZER() },
159    { "noop",                2, IPC_MSG_NOOP,         RB_ENTRY_INITIALIZER() },
160    { "not-supported",       2, IPC_MSG_NOTSUP,       RB_ENTRY_INITIALIZER() },
161    { "pex",                 2, IPC_MSG_PEX,          RB_ENTRY_INITIALIZER() },
162    { "port",                2, IPC_MSG_PORT,         RB_ENTRY_INITIALIZER() },
163    { "quit",                1, IPC_MSG_QUIT,         RB_ENTRY_INITIALIZER() },
164    { "remove",              2, IPC_MSG_REMOVE,       RB_ENTRY_INITIALIZER() },
165    { "remove-all",          2, IPC_MSG_REMOVEALL,    RB_ENTRY_INITIALIZER() },
166    { "start",               2, IPC_MSG_START,        RB_ENTRY_INITIALIZER() },
167    { "start-all",           2, IPC_MSG_STARTALL,     RB_ENTRY_INITIALIZER() },
168    { "status",              2, IPC_MSG_STAT,         RB_ENTRY_INITIALIZER() },
169    { "stop",                2, IPC_MSG_STOP,         RB_ENTRY_INITIALIZER() },
170    { "stop-all",            2, IPC_MSG_STOPALL,      RB_ENTRY_INITIALIZER() },
171    { "succeeded",           2, IPC_MSG_OK,           RB_ENTRY_INITIALIZER() },
172    { "supported",           2, IPC_MSG_SUP,          RB_ENTRY_INITIALIZER() },
173    { "uplimit",             2, IPC_MSG_UPLIMIT,      RB_ENTRY_INITIALIZER() },
174    { "version",             1, IPC_MSG_VERSION,      RB_ENTRY_INITIALIZER() },
175};
176
177static struct inf gl_inf[] =
178{
179    { "comment",                IPC_INF_COMMENT,      RB_ENTRY_INITIALIZER() },
180    { "creator",                IPC_INF_CREATOR,      RB_ENTRY_INITIALIZER() },
181    { "date",                   IPC_INF_DATE,         RB_ENTRY_INITIALIZER() },
182    { "files",                  IPC_INF_FILES,        RB_ENTRY_INITIALIZER() },
183    { "hash",                   IPC_INF_HASH,         RB_ENTRY_INITIALIZER() },
184    { "id",                     IPC_INF_ID,           RB_ENTRY_INITIALIZER() },
185    { "name",                   IPC_INF_NAME,         RB_ENTRY_INITIALIZER() },
186    { "path",                   IPC_INF_PATH,         RB_ENTRY_INITIALIZER() },
187    { "private",                IPC_INF_PRIVATE,      RB_ENTRY_INITIALIZER() },
188    { "saved",                  IPC_INF_SAVED,        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 *, tr_tracker_info_t * );
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 = 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    bzero( &key, 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, tr_info_t * 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,
684                                ( TR_FLAG_PRIVATE & inf->flags ? 1 : 0 ) );
685                break;
686            case IPC_INF_SAVED:
687                tr_bencInitInt( item,
688                                ( TR_FLAG_SAVE & inf->flags ? 1 : 0 ) );
689                break;
690            case IPC_INF_SIZE:
691                tr_bencInitInt( item, inf->totalSize );
692                break;
693            case IPC_INF_TRACKERS:
694                tr_bencInit( item, TYPE_LIST );
695                if( tr_bencListReserve( item, inf->trackerTiers ) )
696                {
697                    return -1;
698                }
699                for( jj = 0; inf->trackerTiers > jj; jj++ )
700                {
701                    tier = tr_bencListAdd( item );
702                    tr_bencInit( tier, TYPE_LIST );
703                    if( tr_bencListReserve( tier,
704                                            inf->trackerList[jj].count ) )
705                    {
706                        return -1;
707                    }
708                    for( kk = 0; inf->trackerList[jj].count > kk; kk++ )
709                    {
710                        if( 0 > filltracker( tr_bencListAdd( tier ),
711                                             &inf->trackerList[jj].list[kk] ) )
712                        {
713                            return -1;
714                        }
715                    }
716                }
717                break;
718            default:
719                assert( 0 );
720                break;
721        }
722    }
723
724    return 0;
725}
726
727int
728ipc_addstat( benc_val_t * list, int tor,
729             const tr_stat_t * st, int types )
730{
731    benc_val_t  * dict, * item;
732    int           ii, used;
733    unsigned int  error;
734
735    /* always send torrent id */
736    types |= IPC_ST_ID;
737
738    if( tr_bencListReserve( list, 1 ) )
739    {
740        return -1;
741    }
742    dict = tr_bencListAdd( list );
743
744    for( ii = used = 0; IPC_ST__MAX > 1 << ii; ii++ )
745    {
746        if( types & ( 1 << ii ) )
747        {
748            used++;
749        }
750    }
751    tr_bencInit( dict, TYPE_DICT );
752    if( tr_bencDictReserve( dict, used ) )
753    {
754        return -1;
755    }
756
757    for( ii = 0; IPC_ST__MAX > 1 << ii; ii++ )
758    {
759        if( !( types & ( 1 << ii ) ) )
760        {
761            continue;
762        }
763        assert( ARRAYLEN( gl_stat ) > ( unsigned )ii );
764        assert( gl_stat[ii].type == ( 1 << ii ) );
765        item = tr_bencDictAdd( dict, gl_stat[ii].name );
766        switch( 1 << ii )
767        {
768            case IPC_ST_COMPLETED:
769                tr_bencInitInt( item, st->downloadedValid );
770                break;
771            case IPC_ST_DOWNSPEED:
772                tr_bencInitInt( item, st->rateDownload * 1024 );
773                break;
774            case IPC_ST_DOWNTOTAL:
775                tr_bencInitInt( item, st->downloaded );
776                break;
777            case IPC_ST_DOWNVALID:
778                tr_bencInitInt( item, st->downloadedValid );
779                break;
780            case IPC_ST_ERROR:
781                error = st->error;
782                if( TR_OK == error )
783                {
784                    tr_bencInitStr( item, NULL, 0, 1 );
785                }
786                else if( TR_ERROR_ISSET( TR_ERROR_ASSERT, error ) )
787                {
788                    tr_bencInitStr( item, "assert", -1, 1 );
789                }
790                else if( TR_ERROR_ISSET( TR_ERROR_IO_PARENT, error ) )
791                {
792                    tr_bencInitStr( item, "io-parent", -1, 1 );
793                }
794                else if( TR_ERROR_ISSET( TR_ERROR_IO_PERMISSIONS, error ) )
795                {
796                    tr_bencInitStr( item, "io-permissions", -1, 1 );
797                }
798                else if( TR_ERROR_ISSET( TR_ERROR_IO_SPACE, error ) )
799                {
800                    tr_bencInitStr( item, "io-space", -1, 1 );
801                }
802                else if( TR_ERROR_ISSET( TR_ERROR_IO_RESOURCES, error ) )
803                {
804                    tr_bencInitStr( item, "io-resources", -1, 1 );
805                }
806                else if( TR_ERROR_ISSET( TR_ERROR_IO_MASK, error ) )
807                {
808                    tr_bencInitStr( item, "io-other", -1, 1 );
809                }
810                else if( TR_ERROR_ISSET( TR_ERROR_TC_ERROR, error ) )
811                {
812                    tr_bencInitStr( item, "tracker-error", -1, 1 );
813                }
814                else if( TR_ERROR_ISSET( TR_ERROR_TC_WARNING, error ) )
815                {
816                    tr_bencInitStr( item, "tracker-warning", -1, 1 );
817                }
818                else
819                {
820                    tr_bencInitStr( item, "other", -1, 1 );
821                }
822                break;
823            case IPC_ST_ERRMSG:
824                if( '\0' == st->errorString[0] )
825                {
826                    tr_bencInitStr( item, NULL, 0, 1 );
827                }
828                else if( tr_bencInitStrDup( item, st->errorString ) )
829                {
830                    return -1;
831                }
832                break;
833            case IPC_ST_ETA:
834                tr_bencInitInt( item, st->eta );
835                break;
836            case IPC_ST_ID:
837                tr_bencInitInt( item, tor );
838                break;
839            case IPC_ST_PEERDOWN:
840                tr_bencInitInt( item, st->peersDownloading );
841                break;
842            case IPC_ST_PEERFROM:
843                tr_bencInit( item, TYPE_DICT );
844                if( tr_bencDictReserve( item, 4 ) )
845                {
846                    return -1;
847                }
848                tr_bencInitInt( tr_bencDictAdd( item, "incoming" ),
849                                st->peersFrom[TR_PEER_FROM_INCOMING] );
850                tr_bencInitInt( tr_bencDictAdd( item, "tracker" ),
851                                st->peersFrom[TR_PEER_FROM_TRACKER] );
852                tr_bencInitInt( tr_bencDictAdd( item, "cache" ),
853                                st->peersFrom[TR_PEER_FROM_CACHE] );
854                tr_bencInitInt( tr_bencDictAdd( item, "pex" ),
855                                st->peersFrom[TR_PEER_FROM_PEX] );
856                break;
857            case IPC_ST_PEERTOTAL:
858                tr_bencInitInt( item, st->peersTotal );
859                break;
860            case IPC_ST_PEERUP:
861                tr_bencInitInt( item, st->peersUploading );
862                break;
863            case IPC_ST_RUNNING:
864                tr_bencInitInt( item,
865                                ( TR_STATUS_INACTIVE & st->status ? 0 : 1 ) );
866                break;
867            case IPC_ST_STATE:
868                if( TR_STATUS_CHECK_WAIT & st->status )
869                {
870                    tr_bencInitStr( item, "waiting to checking", -1, 1 );
871                }
872                else if( TR_STATUS_CHECK & st->status )
873                {
874                    tr_bencInitStr( item, "checking", -1, 1 );
875                }
876                else if( TR_STATUS_DOWNLOAD & st->status )
877                {
878                    tr_bencInitStr( item, "downloading", -1, 1 );
879                }
880                else if( TR_STATUS_SEED & st->status )
881                {
882                    tr_bencInitStr( item, "seeding", -1, 1 );
883                }
884                else if( TR_STATUS_STOPPING & st->status )
885                {
886                    tr_bencInitStr( item, "stopping", -1, 1 );
887                }
888                else if( TR_STATUS_INACTIVE & st->status )
889                {
890                    tr_bencInitStr( item, "paused", -1, 1 );
891                }
892                else
893                {
894                    assert( 0 );
895                }
896                break;
897            case IPC_ST_SWARM:
898                tr_bencInitInt( item, st->swarmspeed * 1024 );
899                break;
900            case IPC_ST_TRACKER:
901                if( 0 > filltracker( item, st->tracker ) )
902                {
903                    return -1;
904                }
905                break;
906            case IPC_ST_TKDONE:
907                tr_bencInitInt( item, st->completedFromTracker );
908                break;
909            case IPC_ST_TKLEECH:
910                tr_bencInitInt( item, st->leechers );
911                break;
912            case IPC_ST_TKSEED:
913                tr_bencInitInt( item, st->seeders );
914                break;
915            case IPC_ST_UPSPEED:
916                tr_bencInitInt( item, st->rateUpload * 1024 );
917                break;
918            case IPC_ST_UPTOTAL:
919                tr_bencInitInt( item, st->uploaded );
920                break;
921            default:
922                assert( 0 );
923                break;
924        }
925    }
926
927    return 0;
928}
929
930ssize_t
931ipc_parse( struct ipc_info * info, uint8_t * buf, ssize_t total, void * arg )
932{
933    char        hex[IPC_MIN_MSG_LEN+1], * end;
934    ssize_t     off, len;
935    benc_val_t  benc;
936
937    for( off = 0; off + IPC_MIN_MSG_LEN < total; off += IPC_MIN_MSG_LEN + len )
938    {
939        memcpy( hex, buf + off, IPC_MIN_MSG_LEN );
940        hex[IPC_MIN_MSG_LEN] = '\0';
941        end = NULL;
942        len = strtol( hex, &end, 16 );
943        if( hex + IPC_MIN_MSG_LEN != end ||
944            0 > len || IPC_MAX_MSG_LEN < len )
945        {
946            errno = EINVAL;
947            return -1;
948        }
949        if( off + IPC_MIN_MSG_LEN + len > total )
950        {
951            break;
952        }
953        errno = 0;
954        if( tr_bencLoad( buf + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) )
955        {
956            if( 0 == errno )
957            {
958                errno = EINVAL;
959            }
960            return -1;
961        }
962        if( 0 > ( HASVERS( info ) ? handlemsgs( info, &benc, arg ) :
963                                    handlevers( info, &benc ) ) )
964        {
965            SAFEBENCFREE( &benc );
966            return -1;
967        }
968        tr_bencFree( &benc );
969    }
970
971    return off;
972}
973
974int
975handlevers( struct ipc_info * info, benc_val_t * dict )
976{
977    benc_val_t * vers, * num;
978    int64_t      min, max;
979
980    if( TYPE_DICT != dict->type )
981    {
982        errno = EINVAL;
983        return -1;
984    }
985
986    vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) );
987    if( NULL == vers )
988    {
989        errno = EINVAL;
990        return -1;
991    }
992
993    switch( vers->type )
994    {
995        case TYPE_INT:
996            min = vers->val.i;
997            max = vers->val.i;
998            break;
999        case TYPE_DICT:
1000            num = tr_bencDictFind( vers, "min" );
1001            min = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
1002            num = tr_bencDictFind( vers, "max" );
1003            max = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
1004            break;
1005        default:
1006            min = -1;
1007            max = -1;
1008            break;
1009    }
1010
1011    if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max )
1012    {
1013        errno = EINVAL;
1014        return -1;
1015    }
1016
1017    assert( PROTO_VERS_MIN <= PROTO_VERS_MAX );
1018    if( min > max )
1019    {
1020        errno = EINVAL;
1021        return -1;
1022    }
1023    if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max )
1024    {
1025        errno = EPERM;
1026        return -1;
1027    }
1028
1029    info->vers = MIN( PROTO_VERS_MAX, max );
1030
1031    return 0;
1032}
1033
1034int
1035handlemsgs( struct ipc_info * info, benc_val_t * pay, void * arg )
1036{
1037    benc_val_t * name, * val, * tag;
1038    int          ii;
1039
1040    assert( HASVERS( info ) );
1041
1042    if( DICTPAYLOAD( info ) )
1043    {
1044        if( TYPE_DICT != pay->type || pay->val.l.count % 2 )
1045        {
1046            errno = EINVAL;
1047            return -1;
1048        }
1049
1050        for( ii = 0; ii < pay->val.l.count; ii += 2 )
1051        {
1052            assert( ii + 1 < pay->val.l.count );
1053            name = &pay->val.l.vals[ii];
1054            val  = &pay->val.l.vals[ii+1];
1055            if( 0 > gotmsg( info, name, val, NULL, arg ) )
1056            {
1057                return -1;
1058            }
1059        }
1060    }
1061    else
1062    {
1063        if( TYPE_LIST != pay->type || 2 > pay->val.l.count )
1064        {
1065            errno = EINVAL;
1066            return -1;
1067        }
1068
1069        name = &pay->val.l.vals[0];
1070        val  = &pay->val.l.vals[1];
1071        tag  = ( 2 == pay->val.l.count ? NULL : &pay->val.l.vals[2] );
1072        if( 0 > gotmsg( info, name, val, tag, arg ) )
1073        {
1074            return -1;
1075        }
1076    }
1077
1078    return 0;
1079}
1080
1081int
1082gotmsg( struct ipc_info * info, benc_val_t * name, benc_val_t * val,
1083        benc_val_t * tagval, void * arg )
1084{
1085    struct msgfunc key, * handler;
1086    struct msg   * msg;
1087    int64_t        tag;
1088
1089    if( TYPE_STR != name->type )
1090    {
1091        errno = EINVAL;
1092        return -1;
1093    }
1094
1095    if( NULL == tagval )
1096    {
1097        tag = -1;
1098    }
1099    else
1100    {
1101        if( TYPE_INT != tagval->type )
1102        {
1103            errno = EINVAL;
1104            return -1;
1105        }
1106        tag = tagval->val.i;
1107    }
1108
1109    msg = msglookup( name->val.s.s );
1110    if( NULL != msg && msg->minvers <= info->vers )
1111    {
1112        bzero( &key, sizeof key );
1113        key.id  = msg->id;
1114        handler = RB_FIND( functree, &info->funcs->msgs, &key );
1115        if( NULL != handler )
1116        {
1117            handler->func( msg->id, val, tag, arg );
1118        }
1119        else if( NULL != info->funcs->def )
1120        {
1121            info->funcs->def( msg->id, val, tag, arg );
1122        }
1123    }
1124
1125    return 0;
1126}
1127
1128int
1129ipc_havemsg( struct ipc_info * info, enum ipc_msg id )
1130{
1131    assert( MSGVALID( id ) );
1132    assert( HASVERS( info ) );
1133
1134    return ( gl_msgs[id].minvers <= info->vers );
1135}
1136
1137enum ipc_msg
1138ipc_msgid( struct ipc_info * info, const char * name )
1139{
1140    struct msg * msg;
1141
1142    msg = msglookup( name );
1143    if( NULL == msg || !ipc_havemsg( info, msg->id ) )
1144    {
1145        return IPC__MSG_COUNT;
1146    }
1147
1148    return msg->id;
1149}
1150
1151int
1152ipc_ishandled( struct ipc_info * info, enum ipc_msg id )
1153{
1154    struct msgfunc key;
1155
1156    assert( MSGVALID( id ) );
1157
1158    bzero( &key, sizeof key );
1159    key.id = id;
1160    return ( NULL != RB_FIND( functree, &info->funcs->msgs, &key ) );
1161}
1162
1163int
1164ipc_havetags( struct ipc_info * info )
1165{
1166    return !DICTPAYLOAD( info );
1167}
1168
1169int
1170ipc_infotypes( enum ipc_msg id, benc_val_t * list )
1171{
1172    static struct inftree infotree = RB_INITIALIZER( &tree );
1173    static struct inftree stattree = RB_INITIALIZER( &tree );
1174    struct inftree * tree;
1175    benc_val_t     * name;
1176    struct inf     * array, * inf, key;
1177    size_t           len, ii;
1178    int              ret, jj;
1179
1180    switch( id )
1181    {
1182        case IPC_MSG_INFO:
1183            tree  = &infotree;
1184            array = gl_inf;
1185            len   = ARRAYLEN( gl_inf );
1186            break;
1187        case IPC_MSG_STAT:
1188            tree  = &stattree;
1189            array = gl_stat;
1190            len   = ARRAYLEN( gl_stat );
1191            break;
1192        default:
1193            assert( 0 );
1194            break;
1195    }
1196
1197    if( RB_EMPTY( tree ) )
1198    {
1199        for( ii = 0; len > ii; ii++ )
1200        {
1201            assert( 1 << ii == array[ii].type );
1202            inf = RB_INSERT( inftree, tree, &array[ii] );
1203            assert( NULL == inf );
1204        }
1205    }
1206
1207    ret = IPC_INF_ID;
1208
1209    if( NULL == list || TYPE_LIST != list->type )
1210    {
1211        return ret;
1212    }
1213
1214    bzero( &key, sizeof key );
1215    for( jj = 0; list->val.l.count > jj; jj++ )
1216    {
1217        name = &list->val.l.vals[jj];
1218        if( TYPE_STR != name->type )
1219        {
1220            continue;
1221        }
1222        key.name = name->val.s.s;
1223        inf = RB_FIND( inftree, tree, &key );
1224        if( NULL != inf )
1225        {
1226            ret |= inf->type;
1227        }
1228    }
1229
1230    return ret;
1231}
1232
1233const char *
1234ipc_infoname( enum ipc_msg id, int type )
1235{
1236    struct inf * array;
1237    size_t len, ii;
1238
1239    switch( id )
1240    {
1241        case IPC_MSG_INFO:
1242            array = gl_inf;
1243            len   = ARRAYLEN( gl_inf );
1244            break;
1245        case IPC_MSG_STAT:
1246            array = gl_stat;
1247            len   = ARRAYLEN( gl_stat );
1248            break;
1249        default:
1250            assert( 0 );
1251            break;
1252    }
1253
1254    for( ii = 0; len > ii; ii++ )
1255    {
1256        if( array[ii].type == type )
1257        {
1258            return array[ii].name;
1259        }
1260    }
1261
1262    assert( 0 );
1263
1264    return NULL;
1265}
1266
1267int
1268msgcmp( struct msg * first, struct msg * second )
1269{
1270    return strcmp( first->name, second->name );
1271}
1272
1273int
1274infcmp( struct inf * first, struct inf * second )
1275{
1276    return strcmp( first->name, second->name );
1277}
1278
1279struct msg *
1280msglookup( const char * name )
1281{
1282    static struct msgtree tree = RB_INITIALIZER( &tree );
1283    struct msg          * ret, key;
1284    size_t                ii;
1285
1286    assert( IPC__MSG_COUNT == ARRAYLEN( gl_msgs ) );
1287
1288    if( RB_EMPTY( &tree ) )
1289    {
1290        for( ii = 0; ARRAYLEN( gl_msgs ) > ii; ii++ )
1291        {
1292            assert( ii == gl_msgs[ii].id );
1293            ret = RB_INSERT( msgtree, &tree, &gl_msgs[ii] );
1294            assert( NULL == ret );
1295        }
1296    }
1297
1298    bzero( &key, sizeof key );
1299    key.name = name;
1300    return RB_FIND( msgtree, &tree, &key );
1301}
1302
1303int
1304filltracker( benc_val_t * val, tr_tracker_info_t * tk )
1305{
1306    tr_bencInit( val, TYPE_DICT );
1307    if( tr_bencDictReserve( val, ( NULL == tk->scrape ? 3 : 4 ) ) )
1308    {
1309        return -1;
1310    }
1311
1312    tr_bencInitStr( tr_bencDictAdd( val, "address" ),  tk->address,  -1, 1 );
1313    tr_bencInitInt( tr_bencDictAdd( val, "port" ),     tk->port );
1314    tr_bencInitStr( tr_bencDictAdd( val, "announce" ), tk->announce, -1, 1 );
1315    if( NULL != tk->scrape )
1316    {
1317        tr_bencInitStr( tr_bencDictAdd( val, "scrape" ), tk->scrape, -1, 1 );
1318    }
1319
1320    return 0;
1321}
Note: See TracBrowser for help on using the repository browser.