source: trunk/libtransmission/ipcparse.c @ 2149

Last change on this file since 2149 was 2149, checked in by livings124, 15 years ago

Merge file selection and torrent creation into the main branch.

The new code for these features is under a new license.

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