source: trunk/libtransmission/ipcparse.c @ 5329

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

fix bug that caused very large torrents to crash Transmission

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