source: trunk/libtransmission/ipcparse.c @ 5138

Last change on this file since 5138 was 5138, checked in by charles, 14 years ago

remove unused "label" field. make ipc_havemsg() private. make ipc_parse()'s buf arg const.

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