source: trunk/libtransmission/ipcparse.c @ 5579

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

#853: transmission-(daemon|gtk) segfault when querying status

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