source: trunk/libtransmission/ipcparse.c @ 2201

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

Add tr_stat_t.downloadedValid hook to IPC for Gimp_

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