source: trunk/libtransmission/ipcparse.c @ 3578

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

Add IPC messages to set and retrieve the encryption mode.
Implement encryption mode messages in -daemon and -remote.

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