source: branches/daemon/daemon/ipc.c @ 1623

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

Add a daemon frontend.

  • Property svn:keywords set to Date Rev Author Id
File size: 36.0 KB
Line 
1/******************************************************************************
2 * $Id: ipc.c 1617 2007-03-31 20:00:40Z joshe $
3 *
4 * Copyright (c) 2007 Joshua Elsasser
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "bsdtree.h"
32#include "ipc.h"
33#include "misc.h"
34
35#include "transmission.h"
36#include "bencode.h"
37
38/* IPC protocol version */
39#define PROTO_VERS_MIN          ( 1 )
40#define PROTO_VERS_MAX          ( 2 )
41
42#define MSGVALID( id )          ( IPC__MSG_COUNT > (id) )
43#define MSGNAME( id )           ( gl_msgs[(id)].name )
44#define DICTPAYLOAD( info )     ( 2 > (info)->vers )
45
46struct msg
47{
48    const char        * name;
49    const int          minvers;
50    const enum ipc_msg id;
51    RB_ENTRY( msg )    link;
52};
53
54struct inf
55{
56    const char    * name;
57    const int       type;
58    RB_ENTRY( inf ) link;
59};
60
61struct msgfunc
62{
63    int             id;
64    trd_msgfunc     func;
65    RB_ENTRY( msgfunc ) link;
66};
67
68RB_HEAD( msgtree, msg );
69RB_HEAD( inftree, inf );
70RB_HEAD( functree, msgfunc );
71
72struct ipc_funcs
73{
74    struct functree msgs;
75    trd_msgfunc    def;
76};
77
78static struct msg gl_msgs[] =
79{
80    { "addfiles",            1, IPC_MSG_ADDFILES,     RB_ENTRY_INITIALIZER() },
81    { "automap",             2, IPC_MSG_AUTOMAP,      RB_ENTRY_INITIALIZER() },
82    { "autostart",           2, IPC_MSG_AUTOSTART,    RB_ENTRY_INITIALIZER() },
83    { "directory",           2, IPC_MSG_DIR,          RB_ENTRY_INITIALIZER() },
84    { "downlimit",           2, IPC_MSG_DOWNLIMIT,    RB_ENTRY_INITIALIZER() },
85    { "failed",              2, IPC_MSG_FAIL,         RB_ENTRY_INITIALIZER() },
86    { "get-automap",         2, IPC_MSG_GETAUTOMAP,   RB_ENTRY_INITIALIZER() },
87    { "get-autostart",       2, IPC_MSG_GETAUTOSTART, RB_ENTRY_INITIALIZER() },
88    { "get-directory",       2, IPC_MSG_GETDIR,       RB_ENTRY_INITIALIZER() },
89    { "get-downlimit",       2, IPC_MSG_GETDOWNLIMIT, RB_ENTRY_INITIALIZER() },
90    { "get-info",            2, IPC_MSG_GETINFO,      RB_ENTRY_INITIALIZER() },
91    { "get-info-all",        2, IPC_MSG_GETINFOALL,   RB_ENTRY_INITIALIZER() },
92    { "get-port",            2, IPC_MSG_GETPORT,      RB_ENTRY_INITIALIZER() },
93    { "get-status",          2, IPC_MSG_GETSTAT,      RB_ENTRY_INITIALIZER() },
94    { "get-status-all",      2, IPC_MSG_GETSTATALL,   RB_ENTRY_INITIALIZER() },
95    { "get-supported",       2, IPC_MSG_GETSUP,       RB_ENTRY_INITIALIZER() },
96    { "get-uplimit",         2, IPC_MSG_GETUPLIMIT,   RB_ENTRY_INITIALIZER() },
97    { "lookup",              2, IPC_MSG_LOOKUP,       RB_ENTRY_INITIALIZER() },
98    { "info",                2, IPC_MSG_INFO,         RB_ENTRY_INITIALIZER() },
99    { "noop",                2, IPC_MSG_NOOP,         RB_ENTRY_INITIALIZER() },
100    { "not-supported",       2, IPC_MSG_NOTSUP,       RB_ENTRY_INITIALIZER() },
101    { "port",                2, IPC_MSG_PORT,         RB_ENTRY_INITIALIZER() },
102    { "quit",                1, IPC_MSG_QUIT,         RB_ENTRY_INITIALIZER() },
103    { "remove",              2, IPC_MSG_REMOVE,       RB_ENTRY_INITIALIZER() },
104    { "remove-all",          2, IPC_MSG_REMOVEALL,    RB_ENTRY_INITIALIZER() },
105    { "start",               2, IPC_MSG_START,        RB_ENTRY_INITIALIZER() },
106    { "start-all",           2, IPC_MSG_STARTALL,     RB_ENTRY_INITIALIZER() },
107    { "status",              2, IPC_MSG_STAT,         RB_ENTRY_INITIALIZER() },
108    { "stop",                2, IPC_MSG_STOP,         RB_ENTRY_INITIALIZER() },
109    { "stop-all",            2, IPC_MSG_STOPALL,      RB_ENTRY_INITIALIZER() },
110    { "succeeded",           2, IPC_MSG_OK,           RB_ENTRY_INITIALIZER() },
111    { "supported",           2, IPC_MSG_SUP,          RB_ENTRY_INITIALIZER() },
112    { "uplimit",             2, IPC_MSG_UPLIMIT,      RB_ENTRY_INITIALIZER() },
113    { "version",             1, IPC_MSG_VERSION,      RB_ENTRY_INITIALIZER() },
114};
115
116static struct inf gl_inf[] =
117{
118    { "comment",                IPC_INF_COMMENT,      RB_ENTRY_INITIALIZER() },
119    { "creator",                IPC_INF_CREATOR,      RB_ENTRY_INITIALIZER() },
120    { "date",                   IPC_INF_DATE,         RB_ENTRY_INITIALIZER() },
121    { "files",                  IPC_INF_FILES,        RB_ENTRY_INITIALIZER() },
122    { "hash",                   IPC_INF_HASH,         RB_ENTRY_INITIALIZER() },
123    { "id",                     IPC_INF_ID,           RB_ENTRY_INITIALIZER() },
124    { "name",                   IPC_INF_NAME,         RB_ENTRY_INITIALIZER() },
125    { "path",                   IPC_INF_PATH,         RB_ENTRY_INITIALIZER() },
126    { "private",                IPC_INF_PRIVATE,      RB_ENTRY_INITIALIZER() },
127    { "saved",                  IPC_INF_SAVED,        RB_ENTRY_INITIALIZER() },
128    { "size",                   IPC_INF_SIZE,         RB_ENTRY_INITIALIZER() },
129    { "trackers",               IPC_INF_TRACKERS,     RB_ENTRY_INITIALIZER() },
130};
131
132static struct inf gl_stat[] =
133{
134    { "completed",              IPC_ST_COMPLETED,     RB_ENTRY_INITIALIZER() },
135    { "download-speed",         IPC_ST_DOWNSPEED,     RB_ENTRY_INITIALIZER() },
136    { "download-total",         IPC_ST_DOWNTOTAL,     RB_ENTRY_INITIALIZER() },
137    { "error",                  IPC_ST_ERROR,         RB_ENTRY_INITIALIZER() },
138    { "error-message",          IPC_ST_ERRMSG,        RB_ENTRY_INITIALIZER() },
139    { "eta",                    IPC_ST_ETA,           RB_ENTRY_INITIALIZER() },
140    { "id",                     IPC_ST_ID,            RB_ENTRY_INITIALIZER() },
141    { "peers-downloading",      IPC_ST_PEERDOWN,      RB_ENTRY_INITIALIZER() },
142    { "peers-from",             IPC_ST_PEERFROM,      RB_ENTRY_INITIALIZER() },
143    { "peers-total",            IPC_ST_PEERTOTAL,     RB_ENTRY_INITIALIZER() },
144    { "peers-uploading",        IPC_ST_PEERUP,        RB_ENTRY_INITIALIZER() },
145    { "running",                IPC_ST_RUNNING,       RB_ENTRY_INITIALIZER() },
146    { "state",                  IPC_ST_STATE,         RB_ENTRY_INITIALIZER() },
147    { "swarm-speed",            IPC_ST_SWARM,         RB_ENTRY_INITIALIZER() },
148    { "tracker",                IPC_ST_TRACKER,       RB_ENTRY_INITIALIZER() },
149    { "scrape-completed",       IPC_ST_TKDONE,        RB_ENTRY_INITIALIZER() },
150    { "scrape-leechers",        IPC_ST_TKLEECH,       RB_ENTRY_INITIALIZER() },
151    { "scrape-seeders",         IPC_ST_TKSEED,        RB_ENTRY_INITIALIZER() },
152    { "upload-speed",           IPC_ST_UPSPEED,       RB_ENTRY_INITIALIZER() },
153    { "upload-total",           IPC_ST_UPTOTAL,       RB_ENTRY_INITIALIZER() },
154};
155
156static int          pkinit     ( struct ipc_info *, benc_val_t *, enum ipc_msg,
157                                 benc_val_t **, int64_t );
158static uint8_t *    pkgenerate ( benc_val_t *, size_t * );
159static int          handlevers ( struct ipc_info *, benc_val_t * );
160static int          handlemsgs ( struct ipc_info *, benc_val_t *, void * );
161static int          gotmsg     ( struct ipc_info *, benc_val_t *, benc_val_t *,
162                                 benc_val_t *, void * );
163static int          msgcmp     ( struct msg *, struct msg * );
164static int          infcmp     ( struct inf *, struct inf * );
165static struct msg * msglookup  ( const char * );
166static int          filltracker( benc_val_t *, tr_tracker_info_t * );
167static int          handlercmp ( struct msgfunc *, struct msgfunc * );
168
169RB_GENERATE_STATIC( msgtree, msg, link, msgcmp )
170RB_GENERATE_STATIC( inftree, inf, link, infcmp )
171RB_GENERATE_STATIC( functree, msgfunc, link, handlercmp )
172INTCMP_FUNC( handlercmp, msgfunc, id )
173
174struct ipc_funcs *
175ipc_initmsgs( void )
176{
177    struct ipc_funcs * tree;
178
179    tree = malloc( sizeof *tree );
180    if( NULL != tree )
181    {
182        RB_INIT( &tree->msgs );
183        tree->def = NULL;
184    }
185
186    return tree;
187}
188
189int
190ipc_addmsg( struct ipc_funcs * tree, enum ipc_msg id, trd_msgfunc func )
191{
192    struct msgfunc key, * entry;
193
194    assert( MSGVALID( id ) );
195    assert( IPC_MSG_VERSION != id );
196
197    bzero( &key, sizeof key );
198    key.id = id;
199    entry = RB_FIND( functree, &tree->msgs, &key );
200    assert( NULL == entry );
201
202    entry = calloc( 1, sizeof *entry );
203    if( NULL == entry )
204    {
205        return -1;
206    }
207
208    entry->id   = id;
209    entry->func = func;
210    entry = RB_INSERT( functree, &tree->msgs, entry );
211    assert( NULL == entry );
212
213    return 0;
214}
215
216void
217ipc_setdefmsg( struct ipc_funcs * tree, trd_msgfunc func )
218{
219    tree->def = func;
220}
221
222void
223ipc_freemsgs( struct ipc_funcs * tree )
224{
225    struct msgfunc * ii, * next;
226
227    for( ii = RB_MIN( functree, &tree->msgs ); NULL != ii; ii = next )
228    {
229        next = RB_NEXT( functree, &tree->msgs, ii );
230        RB_REMOVE( functree, &tree->msgs, ii );
231        free( ii );
232    }
233    free( tree );
234}
235
236void
237ipc_newcon( struct ipc_info * info, struct ipc_funcs * funcs )
238{
239    bzero( info, sizeof *info );
240    info->funcs = funcs;
241    info->vers  = -1;
242}
243
244int
245pkinit( struct ipc_info * info, benc_val_t * pk, enum ipc_msg id,
246        benc_val_t ** val, int64_t tag )
247{
248    assert( MSGVALID( id ) );
249
250    if( !ipc_havemsg( info, id ) || ( 0 < tag && !ipc_havetags( info ) ) )
251    {
252        errno = EPERM;
253        return -1;
254    }
255
256    if( DICTPAYLOAD( info ) )
257    {
258        tr_bencInit( pk, TYPE_DICT );
259        if( tr_bencDictReserve( pk, 1 ) )
260        {
261            return -1;
262        }
263        *val = tr_bencDictAdd( pk, MSGNAME( id ) );
264    }
265    else
266    {
267        tr_bencInit( pk, TYPE_LIST );
268        if( tr_bencListReserve( pk, ( 0 < tag ? 3 : 2 ) ) )
269        {
270            return -1;
271        }
272        tr_bencInitStr( tr_bencListAdd( pk ), MSGNAME( id ), -1, 1 );
273        *val = tr_bencListAdd( pk );
274        if( 0 < tag )
275        {
276            tr_bencInitInt( tr_bencListAdd( pk ), tag );
277        }
278    }
279
280    return 0;
281}
282
283uint8_t *
284pkgenerate( benc_val_t * pk, size_t * len )
285{
286    char * buf, hex[IPC_MIN_MSG_LEN+1];
287    int    used, max;
288
289    used = IPC_MIN_MSG_LEN;
290    max  = IPC_MIN_MSG_LEN;
291    buf  = malloc( IPC_MIN_MSG_LEN );
292    if( NULL == buf )
293    {
294        return NULL;
295    }
296
297    if( tr_bencSave( pk, &buf, &used, &max ) )
298    {
299        SAFEFREE( buf );
300        return NULL;
301    }
302
303    /* ok, this check is pretty laughable */
304    if( IPC_MAX_MSG_LEN < used )
305    {
306        free( buf );
307        errno = EFBIG;
308        return NULL;
309    }
310
311    assert( 0 <= used );
312    snprintf( hex, sizeof hex, "%0*X",
313              IPC_MIN_MSG_LEN, used - IPC_MIN_MSG_LEN );
314    memcpy( buf, hex, IPC_MIN_MSG_LEN );
315    *len = used;
316
317    return ( uint8_t * )buf;
318}
319
320uint8_t *
321ipc_mkstr( struct ipc_info * info, size_t * len, enum ipc_msg id, int64_t tag,
322           const char * str )
323{
324    benc_val_t pk, * strval;
325    uint8_t  * ret;
326
327    if( 0 > pkinit( info, &pk, id, &strval, tag ) )
328    {
329        return NULL;
330    }
331
332    tr_bencInitStr( strval, str, -1, 1 );
333
334    ret = pkgenerate( &pk, len );
335    SAFEBENCFREE( &pk );
336
337    return ret;
338}
339
340uint8_t *
341ipc_mkstrlist( struct ipc_info * info, size_t * len, enum ipc_msg id,
342               int64_t tag, struct strlist * strs )
343{
344    benc_val_t       pk, * list;
345    struct stritem * ii;
346    uint8_t        * ret;
347    int              count;
348
349    if( 0 > pkinit( info, &pk, id, &list, tag ) )
350    {
351        return NULL;
352    }
353
354    count = 0;
355    SLIST_FOREACH( ii, strs, next )
356    {
357        count++;
358    }
359
360    tr_bencInit( list, TYPE_LIST );
361    if( tr_bencListReserve( list, count ) )
362    {
363        SAFEBENCFREE( &pk );
364        return NULL;
365    }
366
367    SLIST_FOREACH( ii, strs, next )
368    {
369        if( tr_bencInitStrDup( tr_bencListAdd( list ), ii->str ) )
370        {
371            SAFEBENCFREE( &pk );
372            return NULL;
373        }
374    }
375
376    ret = pkgenerate( &pk, len );
377    SAFEBENCFREE( &pk );
378
379    return ret;
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, * empty;
387    uint8_t  * ret;
388
389    if( 0 > pkinit( info, &pk, id, &empty, tag ) )
390    {
391        return NULL;
392    }
393
394    tr_bencInitStr( empty, NULL, 0, 1 );
395    ret = pkgenerate( &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    if( 0 > pkinit( info, &pk, id, &val, tag ) )
409    {
410        return NULL;
411    }
412
413    tr_bencInitInt( val, num );
414    ret = pkgenerate( &pk, len );
415    SAFEBENCFREE( &pk );
416
417    return ret;
418}
419
420uint8_t *
421ipc_mkints( struct ipc_info * info, size_t * len, enum ipc_msg id, int64_t tag,
422            size_t count, const int64_t * nums )
423{
424    benc_val_t pk, * list;
425    size_t     ii;
426    uint8_t  * ret;
427
428    if( 0 > pkinit( info, &pk, id, &list, tag ) )
429    {
430        return NULL;
431    }
432
433    tr_bencInit( list, TYPE_LIST );
434    if( tr_bencListReserve( list, count ) )
435    {
436        SAFEBENCFREE( &pk );
437        return NULL;
438    }
439
440    for( ii = 0; count > ii; ii++ )
441    {
442        tr_bencInitInt( tr_bencListAdd( list ), nums[ii] );
443    }
444
445    ret = pkgenerate( &pk, len );
446    SAFEBENCFREE( &pk );
447
448    return ret;
449}
450
451uint8_t *
452ipc_mkvers( size_t * len )
453{
454    benc_val_t pk, * dict;
455    uint8_t  * ret;
456 
457    tr_bencInit( &pk, TYPE_DICT );
458    if( tr_bencDictReserve( &pk, 1 ) )
459    {
460        return NULL;
461    }
462    dict = tr_bencDictAdd( &pk, MSGNAME( IPC_MSG_VERSION ) );
463
464    tr_bencInit( dict, TYPE_DICT );
465    if( tr_bencDictReserve( dict, 2 ) )
466    {
467        SAFEBENCFREE( &pk );
468        return NULL;
469    }
470    tr_bencInitInt( tr_bencDictAdd( dict, "min" ), PROTO_VERS_MIN );
471    tr_bencInitInt( tr_bencDictAdd( dict, "max" ), PROTO_VERS_MAX );
472
473    ret = pkgenerate( &pk, len );
474    SAFEBENCFREE( &pk );
475
476    return ret;
477}
478
479uint8_t *
480ipc_mkgetinfo( struct ipc_info * info, size_t * len, enum ipc_msg id,
481               int64_t tag, int types, const int * ids )
482{
483    benc_val_t   pk, * top, * idlist, * typelist;
484    size_t       ii, typecount, used;
485    struct inf * typearray;
486    uint8_t    * ret;
487
488    if( 0 > pkinit( info, &pk, id, &top, tag ) )
489    {
490        return NULL;
491    }
492
493    /* no ID list, send an -all message */
494    if( NULL == ids )
495    {
496        typelist = top;
497    }
498    else
499    {
500        /* add the requested IDs */
501        tr_bencInit( top, TYPE_DICT );
502        if( tr_bencDictReserve( top, 2 ) )
503        {
504            SAFEBENCFREE( &pk );
505            return NULL;
506        }
507        idlist   = tr_bencDictAdd( top, "id" );
508        typelist = tr_bencDictAdd( top, "type" );
509        tr_bencInit( idlist, TYPE_LIST );
510        for( ii = 0; TORRENT_ID_VALID( ids[ii] ); ii++ )
511        {
512        }
513        if( tr_bencListReserve( idlist, ii ) )
514        {
515            SAFEBENCFREE( &pk );
516            return NULL;
517        }
518        for( ii = 0; TORRENT_ID_VALID( ids[ii] ); ii++ )
519        {
520            tr_bencInitInt( tr_bencListAdd( idlist ), ids[ii] );
521        }
522    }
523
524    /* get the type name array */
525    switch( id )
526    {
527        case IPC_MSG_GETINFO:
528        case IPC_MSG_GETINFOALL:
529            typecount = ARRAYLEN( gl_inf );
530            typearray = gl_inf;
531            break;
532        case IPC_MSG_GETSTAT:
533        case IPC_MSG_GETSTATALL:
534            typecount = ARRAYLEN( gl_stat );
535            typearray = gl_stat;
536            break;
537        default:
538            assert( 0 );
539            break;
540    }       
541
542    /* add the type names */
543    tr_bencInit( typelist, TYPE_LIST );
544    for( ii = used = 0; typecount > ii; ii++ )
545    {
546        if( types & ( 1 << ii ) )
547        {
548            used++;
549        }
550    }
551    if( tr_bencListReserve( typelist, used ) )
552    {
553        SAFEBENCFREE( &pk );
554        return NULL;
555    }
556    for( ii = 0; typecount > ii; ii++ )
557    {
558        if( !( types & ( 1 << ii ) ) )
559        {
560            continue;
561        }
562        assert( typearray[ii].type == ( 1 << ii ) );
563        tr_bencInitStr( tr_bencListAdd( typelist ),
564                        typearray[ii].name, -1, 1 );
565    }
566
567    /* generate packet */
568    ret = pkgenerate( &pk, len );
569    SAFEBENCFREE( &pk );
570
571    return ret;
572}
573
574int
575ipc_initinfo( struct ipc_info * info, enum ipc_msg id, int64_t tag,
576              benc_val_t * pk, benc_val_t ** val )
577{
578    assert( IPC_MSG_INFO == id || IPC_MSG_STAT == id );
579
580    if( 0 > pkinit( info, pk, id, val, tag ) )
581    {
582        return -1;
583    }
584
585    tr_bencInit( *val, TYPE_LIST );
586
587    return 0;
588}
589
590int
591ipc_addinfo( benc_val_t * list, int tor, tr_info_t * inf, int types )
592{
593    benc_val_t * dict, * item, * file, * tier;
594    int          ii, jj, kk;
595
596    /* always send torrent id */
597    types |= IPC_INF_ID;
598
599    if( tr_bencListReserve( list, 1 ) )
600    {
601        return -1;
602    }
603
604    dict = tr_bencListAdd( list );
605    tr_bencInit( dict, TYPE_DICT );
606    for( ii = jj = 0; IPC_INF__MAX > 1 << ii; ii++ )
607    {
608        if( !( types & ( 1 << ii ) ) )
609        {
610            continue;
611        }
612        assert( ARRAYLEN( gl_inf ) > ( unsigned )ii );
613        assert( gl_inf[ii].type == ( 1 << ii ) );
614        /* check for missing optional info */
615        if( ( IPC_INF_COMMENT == ( 1 << ii ) && '\0' == inf->comment ) ||
616            ( IPC_INF_CREATOR == ( 1 << ii ) && '\0' == inf->creator ) ||
617            ( IPC_INF_DATE    == ( 1 << ii ) &&   0  >= inf->dateCreated ) )
618        {
619            continue;
620        }
621        jj++;
622    }
623    if( tr_bencDictReserve( dict, jj ) )
624    {
625        return -1;
626    }
627
628    for( ii = 0; IPC_INF__MAX > 1 << ii; ii++ )
629    {
630        if( !( types & ( 1 << ii ) ) )
631        {
632            continue;
633        }
634        /* check for missing optional info */
635        if( ( IPC_INF_COMMENT == ( 1 << ii ) && '\0' == inf->comment ) ||
636            ( IPC_INF_CREATOR == ( 1 << ii ) && '\0' == inf->creator ) ||
637            ( IPC_INF_DATE    == ( 1 << ii ) && 0 >= inf->dateCreated ) )
638        {
639            continue;
640        }
641
642        item = tr_bencDictAdd( dict, gl_inf[ii].name );
643        switch( 1 << ii )
644        {
645            case IPC_INF_COMMENT:
646                tr_bencInitStr( item, inf->comment, -1, 1 );
647                break;
648            case IPC_INF_CREATOR:
649                tr_bencInitStr( item, inf->creator, -1, 1 );
650                break;
651            case IPC_INF_DATE:
652                tr_bencInitInt( item, inf->dateCreated );
653                break;
654            case IPC_INF_FILES:
655                tr_bencInit( item, TYPE_LIST );
656                if( tr_bencListReserve( item, inf->fileCount ) )
657                {
658                    return -1;
659                }
660                for( jj = 0; inf->fileCount > jj; jj++ )
661                {
662                    file = tr_bencListAdd( item );
663                    tr_bencInit( file, TYPE_DICT );
664                    if( tr_bencDictReserve( file, 2 ) )
665                    {
666                        return -1;
667                    }
668                    tr_bencInitStr( tr_bencDictAdd( file, "name" ),
669                                    inf->files[jj].name, -1, 1 );
670                    tr_bencInitInt( tr_bencDictAdd( file, "size" ),
671                                    inf->files[jj].length );
672                }
673                break;
674            case IPC_INF_HASH:
675                tr_bencInitStr( item, inf->hashString, -1, 1 );
676                break;
677            case IPC_INF_ID:
678                tr_bencInitInt( item, tor );
679                break;
680            case IPC_INF_NAME:
681                tr_bencInitStr( item, inf->name, -1, 1 );
682                break;
683            case IPC_INF_PATH:
684                tr_bencInitStr( item, inf->torrent, -1, 1 );
685                break;
686            case IPC_INF_PRIVATE:
687                tr_bencInitInt( item,
688                                ( TR_FLAG_PRIVATE & inf->flags ? 1 : 0 ) );
689                break;
690            case IPC_INF_SAVED:
691                tr_bencInitInt( item,
692                                ( TR_FLAG_SAVE & inf->flags ? 1 : 0 ) );
693                break;
694            case IPC_INF_SIZE:
695                tr_bencInitInt( item, inf->totalSize );
696                break;
697            case IPC_INF_TRACKERS:
698                tr_bencInit( item, TYPE_LIST );
699                if( tr_bencListReserve( item, inf->trackerTiers ) )
700                {
701                    return -1;
702                }
703                for( jj = 0; inf->trackerTiers > jj; jj++ )
704                {
705                    tier = tr_bencListAdd( item );
706                    tr_bencInit( tier, TYPE_LIST );
707                    if( tr_bencListReserve( tier,
708                                            inf->trackerList[jj].count ) )
709                    {
710                        return -1;
711                    }
712                    for( kk = 0; inf->trackerList[jj].count > kk; kk++ )
713                    {
714                        if( 0 > filltracker( tr_bencListAdd( tier ),
715                                             &inf->trackerList[jj].list[kk] ) )
716                        {
717                            return -1;
718                        }
719                    }
720                }
721                break;
722            default:
723                assert( 0 );
724                break;
725        }
726    }
727
728    return 0;
729}
730
731int
732ipc_addstat( benc_val_t * list, int tor, tr_info_t * inf,
733             tr_stat_t * st, int types )
734{
735    benc_val_t  * dict, * item;
736    int           ii, used;
737
738    /* always send torrent id */
739    types |= IPC_ST_ID;
740
741    if( tr_bencListReserve( list, 1 ) )
742    {
743        return -1;
744    }
745    dict = tr_bencListAdd( list );
746
747    for( ii = used = 0; IPC_ST__MAX > 1 << ii; ii++ )
748    {
749        if( types & ( 1 << ii ) )
750        {
751            used++;
752        }
753    }
754    tr_bencInit( dict, TYPE_DICT );
755    if( tr_bencDictReserve( dict, used ) )
756    {
757        return -1;
758    }
759
760    for( ii = 0; IPC_ST__MAX > 1 << ii; ii++ )
761    {
762        if( !( types & ( 1 << ii ) ) )
763        {
764            continue;
765        }
766        assert( ARRAYLEN( gl_stat ) > ( unsigned )ii );
767        assert( gl_stat[ii].type == ( 1 << ii ) );
768        item = tr_bencDictAdd( dict, gl_stat[ii].name );
769        switch( 1 << ii )
770        {
771            case IPC_ST_COMPLETED:
772                tr_bencInitInt( item, st->progress * ( float )inf->totalSize );
773                break;
774            case IPC_ST_DOWNSPEED:
775                tr_bencInitInt( item, st->rateDownload * 1024 );
776                break;
777            case IPC_ST_DOWNTOTAL:
778                tr_bencInitInt( item, st->downloaded );
779                break;
780            case IPC_ST_ERROR:
781                if( TR_OK == st->error )
782                {
783                    tr_bencInitStr( item, NULL, 0, 1 );
784                }
785                else if( TR_ERROR_ASSERT & st->error )
786                {
787                    tr_bencInitStr( item, "assert", -1, 1 );
788                }
789                else if( TR_ERROR_IO_PARENT & st->error )
790                {
791                    tr_bencInitStr( item, "io-parent", -1, 1 );
792                }
793                else if( TR_ERROR_IO_PERMISSIONS & st->error )
794                {
795                    tr_bencInitStr( item, "io-permissions", -1, 1 );
796                }
797                else if( TR_ERROR_IO_MASK & st->error )
798                {
799                    tr_bencInitStr( item, "io-other", -1, 1 );
800                }
801                else if( TR_ERROR_TC_ERROR & st->error )
802                {
803                    tr_bencInitStr( item, "tracker-error", -1, 1 );
804                }
805                else if( TR_ERROR_TC_WARNING & st->error )
806                {
807                    tr_bencInitStr( item, "tracker-warning", -1, 1 );
808                }
809                else
810                {
811                    tr_bencInitStr( item, "other", -1, 1 );
812                }
813                break;
814            case IPC_ST_ERRMSG:
815                if( '\0' == st->errorString[0] )
816                {
817                    tr_bencInitStr( item, NULL, 0, 1 );
818                }
819                else if( tr_bencInitStrDup( item, st->errorString ) )
820                {
821                    return -1;
822                }
823                break;
824            case IPC_ST_ETA:
825                tr_bencInitInt( item, st->eta );
826                break;
827            case IPC_ST_ID:
828                tr_bencInitInt( item, tor );
829                break;
830            case IPC_ST_PEERDOWN:
831                tr_bencInitInt( item, st->peersDownloading );
832                break;
833            case IPC_ST_PEERFROM:
834                tr_bencInit( item, TYPE_DICT );
835                if( tr_bencDictReserve( item, 4 ) )
836                {
837                    return -1;
838                }
839                tr_bencInitInt( tr_bencDictAdd( item, "incoming" ),
840                                st->peersFrom[TR_PEER_FROM_INCOMING] );
841                tr_bencInitInt( tr_bencDictAdd( item, "tracker" ),
842                                st->peersFrom[TR_PEER_FROM_TRACKER] );
843                tr_bencInitInt( tr_bencDictAdd( item, "cache" ),
844                                st->peersFrom[TR_PEER_FROM_CACHE] );
845                tr_bencInitInt( tr_bencDictAdd( item, "pex" ),
846                                st->peersFrom[TR_PEER_FROM_PEX] );
847                break;
848            case IPC_ST_PEERTOTAL:
849                tr_bencInitInt( item, st->peersTotal );
850                break;
851            case IPC_ST_PEERUP:
852                tr_bencInitInt( item, st->peersUploading );
853                break;
854            case IPC_ST_RUNNING:
855                tr_bencInitInt( item,
856                                ( TR_STATUS_INACTIVE & st->status ? 0 : 1 ) );
857                break;
858            case IPC_ST_STATE:
859                if( TR_STATUS_CHECK & st->status )
860                {
861                    tr_bencInitStr( item, "checking", -1, 1 );
862                }
863                else if( TR_STATUS_DOWNLOAD & st->status )
864                {
865                    tr_bencInitStr( item, "downloading", -1, 1 );
866                }
867                else if( TR_STATUS_SEED & st->status )
868                {
869                    tr_bencInitStr( item, "seeding", -1, 1 );
870                }
871                else if( TR_STATUS_STOPPING & st->status )
872                {
873                    tr_bencInitStr( item, "stopping", -1, 1 );
874                }
875                else if( TR_STATUS_INACTIVE & st->status )
876                {
877                    tr_bencInitStr( item, "paused", -1, 1 );
878                }
879                else
880                {
881                    assert( 0 );
882                }
883                break;
884            case IPC_ST_SWARM:
885                tr_bencInitInt( item, st->swarmspeed * 1024 );
886                break;
887            case IPC_ST_TRACKER:
888                if( 0 > filltracker( item, st->tracker ) )
889                {
890                    return -1;
891                }
892                break;
893            case IPC_ST_TKDONE:
894                tr_bencInitInt( item, st->completedFromTracker );
895                break;
896            case IPC_ST_TKLEECH:
897                tr_bencInitInt( item, st->leechers );
898                break;
899            case IPC_ST_TKSEED:
900                tr_bencInitInt( item, st->seeders );
901                break;
902            case IPC_ST_UPSPEED:
903                tr_bencInitInt( item, st->rateUpload * 1024 );
904                break;
905            case IPC_ST_UPTOTAL:
906                tr_bencInitInt( item, st->uploaded );
907                break;
908            default:
909                assert( 0 );
910                break;
911        }
912    }
913
914    return 0;
915}
916
917uint8_t *
918ipc_mkinfo( benc_val_t * pk, size_t * len )
919{
920    return pkgenerate( pk, len );
921}
922
923ssize_t
924ipc_parse( struct ipc_info * info, uint8_t * buf, ssize_t total, void * arg )
925{
926    char        hex[IPC_MIN_MSG_LEN+1], * end;
927    ssize_t     off, len;
928    benc_val_t  benc;
929
930    for( off = 0; off + IPC_MIN_MSG_LEN < total; off += IPC_MIN_MSG_LEN + len )
931    {
932        memcpy( hex, buf + off, IPC_MIN_MSG_LEN );
933        hex[IPC_MIN_MSG_LEN] = '\0';
934        end = NULL;
935        len = strtol( hex, &end, 16 );
936        if( hex + IPC_MIN_MSG_LEN != end ||
937            0 > len || IPC_MAX_MSG_LEN < len )
938        {
939            errno = EINVAL;
940            return -1;
941        }
942        if( off + IPC_MIN_MSG_LEN + len > total )
943        {
944            break;
945        }
946        errno = 0;
947        if( tr_bencLoad( buf + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) )
948        {
949            if( 0 == errno )
950            {
951                errno = EINVAL;
952            }
953            return -1;
954        }
955        if( 0 > ( HASVERS( info ) ? handlemsgs( info, &benc, arg ) :
956                                    handlevers( info, &benc ) ) )
957        {
958            SAFEBENCFREE( &benc );
959            return -1;
960        }
961        tr_bencFree( &benc );
962    }
963
964    return off;
965}
966
967int
968handlevers( struct ipc_info * info, benc_val_t * dict )
969{
970    benc_val_t * vers, * num;
971    int64_t      min, max;
972
973    if( TYPE_DICT != dict->type )
974    {
975        errno = EINVAL;
976        return -1;
977    }
978
979    vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) );
980    if( NULL == vers )
981    {
982        errno = EINVAL;
983        return -1;
984    }
985
986    switch( vers->type )
987    {
988        case TYPE_INT:
989            min = vers->val.i;
990            max = vers->val.i;
991            break;
992        case TYPE_DICT:
993            num = tr_bencDictFind( vers, "min" );
994            min = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
995            num = tr_bencDictFind( vers, "max" );
996            max = ( NULL == num || TYPE_INT != num->type ? -1 : num->val.i );
997            break;
998        default:
999            min = -1;
1000            max = -1;
1001            break;
1002    }
1003
1004    if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max )
1005    {
1006        errno = EINVAL;
1007        return -1;
1008    }
1009
1010    assert( PROTO_VERS_MIN <= PROTO_VERS_MAX );
1011    if( min > max )
1012    {
1013        errno = EINVAL;
1014        return -1;
1015    }
1016    if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max )
1017    {
1018        errno = EPERM;
1019        return -1;
1020    }
1021
1022    info->vers = MIN( PROTO_VERS_MAX, max );
1023
1024    return 0;
1025}
1026
1027int
1028handlemsgs( struct ipc_info * info, benc_val_t * pay, void * arg )
1029{
1030    benc_val_t * name, * val, * tag;
1031    int          ii;
1032
1033    assert( HASVERS( info ) );
1034
1035    if( DICTPAYLOAD( info ) )
1036    {
1037        if( TYPE_DICT != pay->type || pay->val.l.count % 2 )
1038        {
1039            errno = EINVAL;
1040            return -1;
1041        }
1042
1043        for( ii = 0; ii < pay->val.l.count; ii += 2 )
1044        {
1045            assert( ii + 1 < pay->val.l.count );
1046            name = &pay->val.l.vals[ii];
1047            val  = &pay->val.l.vals[ii+1];
1048            if( 0 > gotmsg( info, name, val, NULL, arg ) )
1049            {
1050                return -1;
1051            }
1052        }
1053    }
1054    else
1055    {
1056        if( TYPE_LIST != pay->type || 2 > pay->val.l.count )
1057        {
1058            errno = EINVAL;
1059            return -1;
1060        }
1061
1062        name = &pay->val.l.vals[0];
1063        val  = &pay->val.l.vals[1];
1064        tag  = ( 2 == pay->val.l.count ? NULL : &pay->val.l.vals[2] );
1065        if( 0 > gotmsg( info, name, val, tag, arg ) )
1066        {
1067            return -1;
1068        }
1069    }
1070
1071    return 0;
1072}
1073
1074int
1075gotmsg( struct ipc_info * info, benc_val_t * name, benc_val_t * val,
1076        benc_val_t * tagval, void * arg )
1077{
1078    struct msgfunc key, * handler;
1079    struct msg   * msg;
1080    int64_t        tag;
1081
1082    if( TYPE_STR != name->type )
1083    {
1084        errno = EINVAL;
1085        return -1;
1086    }
1087
1088    if( NULL == tagval )
1089    {
1090        tag = -1;
1091    }
1092    else
1093    {
1094        if( TYPE_INT != tagval->type )
1095        {
1096            errno = EINVAL;
1097            return -1;
1098        }
1099        tag = tagval->val.i;
1100    }
1101
1102    msg = msglookup( name->val.s.s );
1103    if( NULL != msg && msg->minvers <= info->vers )
1104    {
1105        bzero( &key, sizeof key );
1106        key.id  = msg->id;
1107        handler = RB_FIND( functree, &info->funcs->msgs, &key );
1108        if( NULL != handler )
1109        {
1110            handler->func( msg->id, val, tag, arg );
1111        }
1112        else if( NULL != info->funcs->def )
1113        {
1114            info->funcs->def( msg->id, val, tag, arg );
1115        }
1116    }
1117
1118    return 0;
1119}
1120
1121int
1122ipc_havemsg( struct ipc_info * info, enum ipc_msg id )
1123{
1124    assert( MSGVALID( id ) );
1125    assert( HASVERS( info ) );
1126
1127    return ( gl_msgs[id].minvers <= info->vers );
1128}
1129
1130enum ipc_msg
1131ipc_msgid( struct ipc_info * info, const char * name )
1132{
1133    struct msg * msg;
1134
1135    msg = msglookup( name );
1136    if( NULL == msg || !ipc_havemsg( info, msg->id ) )
1137    {
1138        return IPC__MSG_COUNT;
1139    }
1140
1141    return msg->id;
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.