source: trunk/libtransmission/ipcparse.c @ 5794

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

bang on ipcparse.c for a bit.

  • Property svn:keywords set to Date Rev Author Id
File size: 32.5 KB
Line 
1/******************************************************************************
2 * $Id: ipcparse.c 5794 2008-05-09 20:59:34Z 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        const 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
188#define ASSERT_SORTED(A) \
189  do { \
190    int i, n; \
191    for( i=0, n=TR_N_ELEMENTS(A)-1; i<n; ++i ) \
192      assert( strcmp( A[i].name, A[i+1].name ) < 0 ); \
193  } while( 0 );
194
195struct ipc_funcs *
196ipc_initmsgs( void )
197{
198    ASSERT_SORTED( gl_msgs );
199    ASSERT_SORTED( gl_inf );
200    ASSERT_SORTED( gl_stat );
201
202    return tr_new0( struct ipc_funcs, 1 );
203}
204
205void
206ipc_addmsg( struct ipc_funcs  * funcs,
207            enum ipc_msg        msg_id,
208            trd_msgfunc         func )
209{
210    assert( MSGVALID( msg_id ) );
211    assert( IPC_MSG_VERSION != msg_id );
212
213    funcs->msgs[msg_id] = func;
214}
215
216void
217ipc_setdefmsg( struct ipc_funcs * funcs, trd_msgfunc func )
218{
219    funcs->def = func;
220}
221
222void
223ipc_freemsgs( struct ipc_funcs * funcs )
224{
225    tr_free( funcs );
226}
227
228struct ipc_info *
229ipc_newcon( struct ipc_funcs * funcs )
230{
231    struct ipc_info * info = tr_new0( struct ipc_info, 1 );
232    info->funcs = funcs;
233    info->vers  = -1;
234    return info;
235}
236
237void
238ipc_freecon( struct ipc_info * session )
239{
240    tr_free( session );
241}
242
243int
244ipc_ishandled( const struct ipc_info * session, enum ipc_msg msg_id )
245{
246    assert( MSGVALID( msg_id ) );
247
248    return session->funcs->msgs[msg_id] != NULL;
249}
250
251int
252ipc_havetags( const struct ipc_info * session )
253{
254    return !DICTPAYLOAD( session );
255}
256
257static int
258sessionSupportsTags( const struct ipc_info * session )
259{
260    return session->vers >= 2;
261}
262
263static int
264sessionSupportsMessage( const struct ipc_info * session, enum ipc_msg id )
265{
266    assert( MSGVALID( id ) );
267    assert( ipc_hasvers( session ) );
268
269    return gl_msgs[id].minvers <= session->vers;
270}
271
272/**
273 * Creates the benc metainfo structure for a message
274 * and returns its child where payload should be set.
275 *
276 * In protocol version 1, the metainfo is a single-entry
277 * dictionary with a string from gl_msgs as the key
278 * and the return tr_benc pointer as the value.
279 *
280 * In protocol version 2, the metainfo is a list
281 * holding a string from gl_msgs, the return benc pointer,
282 * and (optionally) the integer tag.
283 */
284tr_benc *
285ipc_initval( const struct ipc_info * session,
286             enum ipc_msg            msg_id,
287             int64_t                 tag,
288             tr_benc               * pk,
289             int                     benc_type )
290{
291    tr_benc * ret;
292
293    assert( MSGVALID( msg_id ) );
294
295    if( !sessionSupportsMessage( session, msg_id )
296        || ( (tag>0) && !sessionSupportsTags( session ) ) )
297    {
298        errno = EPERM;
299        return NULL;
300    }
301
302    if( DICTPAYLOAD( session ) )
303    {
304        tr_bencInitDict( pk, 1 );
305        ret = tr_bencDictAdd( pk, MSGNAME( msg_id ) );
306    }
307    else
308    {
309        tr_bencInitList( pk, 3 );
310        tr_bencListAddStr( pk, MSGNAME( msg_id ) );
311        ret = tr_bencListAdd( pk );
312        if( 0 < tag )
313            tr_bencListAddInt( pk, tag );
314    }
315
316    tr_bencInit( ret, benc_type );
317    return ret;
318}
319
320/**
321 * Serialize a benc message into a string appended to a
322 * printf()'ed string IPC_MIN_MSG_LEN bytes long that
323 * gives the length of the string.
324 */
325uint8_t *
326ipc_serialize( const tr_benc * benc, size_t * setmeSize )
327{
328    uint8_t * ret = NULL;
329    int len = 0;
330    char * str = tr_bencSave( benc, &len );
331
332    if( len > IPC_MAX_MSG_LEN )
333        errno = EFBIG;
334    else {
335        const size_t size = IPC_MIN_MSG_LEN + len;
336        ret = tr_new( uint8_t, size );
337        snprintf( (char*)ret, size, "%0*X", IPC_MIN_MSG_LEN, len );
338        memcpy( ret + IPC_MIN_MSG_LEN, str, len );
339        *setmeSize = size;
340    }
341
342    tr_free( str );
343    return ret;
344}
345
346/**
347 * Create a serialized message whose payload is a NULL string
348 */
349uint8_t *
350ipc_mkempty( const struct ipc_info * session,
351             size_t                * setmeSize,
352             enum ipc_msg            msg_id,
353             int64_t                 tag )
354{
355    return ipc_mkstr( session, setmeSize, msg_id, tag, NULL );
356}
357
358/**
359 * Create a serialized message whose payload is an integer
360 */
361uint8_t *
362ipc_mkint( const struct ipc_info  * session,
363           size_t                 * setmeSize,
364           enum ipc_msg             msg_id,
365           int64_t                  tag,
366           int64_t                  num )
367{
368    tr_benc pk, * val;
369    uint8_t  * ret = NULL;
370
371    if(( val = ipc_initval( session, msg_id, tag, &pk, TYPE_INT )))
372    {
373        tr_bencInitInt( val, num );
374        ret = ipc_serialize( &pk, setmeSize );
375        SAFEBENCFREE( &pk );
376    }
377
378    return ret;
379}
380
381/**
382 * Create a serialized message whose payload is a string
383 */
384uint8_t *
385ipc_mkstr( const struct ipc_info  * session,
386           size_t                 * setmeSize,
387           enum ipc_msg             msg_id,
388           int64_t                  tag,
389           const char             * str )
390{
391    tr_benc pk, * val;
392    uint8_t  * ret = NULL;
393
394    if(( val = ipc_initval( session, msg_id, tag, &pk, TYPE_STR )))
395    {
396        tr_bencInitStrDup( val, str );
397        ret = ipc_serialize( &pk, setmeSize );
398        SAFEBENCFREE( &pk );
399    }
400
401    return ret;
402}
403
404/**
405 * Create a serialized message whose payload is a dictionary
406 * giving the minimum and maximum protocol version we support,
407 * and (optionally) the label passed in.
408 *
409 * Note that this message is just the dictionary payload.
410 * It doesn't contain metainfo as the other ipc_mk*() functions do.
411 * That's because the metainfo is dependent on the protocol version,
412 * and this is a handshake message to negotiate protocol versions.
413 *
414 * @see handlevers()
415 */
416uint8_t *
417ipc_mkvers( size_t * len, const char * label )
418{
419    tr_benc pk, * dict;
420    uint8_t  * ret;
421 
422    tr_bencInitDict( &pk, 1 );
423    dict = tr_bencDictAddDict( &pk, MSGNAME( IPC_MSG_VERSION ), 3 );
424    tr_bencDictAddInt( dict, "min", PROTO_VERS_MIN );
425    tr_bencDictAddInt( dict, "max", PROTO_VERS_MAX );
426    if( label )
427        tr_bencDictAddStr( dict, "label", label );
428
429    ret = ipc_serialize( &pk, len );
430    SAFEBENCFREE( &pk );
431
432    return ret;
433}
434
435/**
436 * Create a serialized message that is used to request
437 * torrent information or statistics.
438 *
439 * msg_id must be one of:
440 *   IPC_MSG_GETINFO
441 *   IPC_MSG_GETINFOALL
442 *   IPC_MSG_GETSTAT
443 *   IPC_MSG_GETSTATALL
444 *
445 * "ids" is an optional array of torrent IDs.
446 * The array, if included, must be terminated by a 0 torrent id.
447 *
448 * "types" is a bitwise-and'ed set of fields from either
449 * the IPC_INF_* or IPC_ST_* enums in ipc-parse.h.
450 * Which enums are used is dependent on the value of msg_id.
451 *
452 * If torrent ids are specified in the "ids" array,
453 * the payload is a dictionary of two lists, "id" and "type".
454 * The "id" list holds the torrent IDs, and
455 * the "type" list holds string keys from either
456 * gl_inf or gl_stat, depending on the value of msg_id
457 *
458 * If no torrent ids are specified, the payload is
459 * a single list identical to the "type" list described above.
460 */
461uint8_t *
462ipc_createInfoRequest( const struct ipc_info * session,
463                       size_t                * setmeSize,
464                       enum ipc_msg            msg_id,
465                       int64_t                 tag,
466                       int                     types,
467                       const int             * ids )
468{
469    tr_benc   pk;
470    tr_benc * typelist;
471    size_t       i, typecount;
472    const struct inf * typearray;
473    uint8_t    * ret;
474
475    /* no ID list, send an -all message */
476    if( !ids ) {
477        typelist = ipc_initval( session, msg_id, tag, &pk, TYPE_LIST );
478        if( !typelist )
479            return NULL;
480    }
481    else
482    {
483        tr_benc * top;
484        tr_benc * idlist;
485
486        top = ipc_initval( session, msg_id, tag, &pk, TYPE_DICT );
487        if( !top )
488            return NULL;
489
490        /* add the requested IDs */
491        tr_bencDictReserve( top, 2 );
492        typelist = tr_bencDictAdd( top, "type" );
493        tr_bencInit( typelist, TYPE_LIST );
494        for( i = 0; TORRENT_ID_VALID( ids[i] ); i++ ) { }
495        idlist = tr_bencDictAddList( top, "id", i );
496        for( i = 0; TORRENT_ID_VALID( ids[i] ); i++ )
497            tr_bencListAddInt( idlist, ids[i] );
498    }
499
500    /* get the type name array */
501    switch( msg_id )
502    {
503        case IPC_MSG_GETINFO:
504        case IPC_MSG_GETINFOALL:
505            typecount = TR_N_ELEMENTS( gl_inf );
506            typearray = gl_inf;
507            break;
508        case IPC_MSG_GETSTAT:
509        case IPC_MSG_GETSTATALL:
510            typecount = TR_N_ELEMENTS( gl_stat );
511            typearray = gl_stat;
512            break;
513        default:
514            assert( 0 );
515            break;
516    }       
517
518    tr_bencListReserve( typelist, typecount );
519    for( i=0; i<typecount; ++i )
520        if( types & typearray[i].type )
521            tr_bencListAddStr( typelist, typearray[i].name );
522
523    /* generate packet */
524    ret = ipc_serialize( &pk, setmeSize );
525    SAFEBENCFREE( &pk );
526
527    return ret;
528}
529
530static void
531filltracker( tr_benc * val, const tr_tracker_info * tk )
532{
533    tr_bencInitDict( val, 4 );
534    tr_bencDictAddStr( val, "announce", tk->announce );
535    if( tk->scrape )
536        tr_bencDictAddStr( val, "scrape", tk->scrape );
537}
538
539/**
540 * append to "list" a dictionary whose keys are
541 * the string keys from gl_inf and whose values are
542 * torrent info set from "torrent_id" and "inf".
543 *
544 * "types" is a bitwise-and'ed set of fields
545 * from the IPC_INF_* enum in ipcparse.h.
546 * It specifies what to put in the dictionary.
547 */
548int
549ipc_addinfo( tr_benc         * list,
550             int               torrent_id,
551             tr_torrent      * tor,
552             int               types )
553{
554    const tr_info * inf = tr_torrentInfo( tor );
555    const struct inf * it;
556    const struct inf * end;
557    tr_benc * dict;
558
559    /* always send torrent id */
560    types |= IPC_INF_ID;
561
562    /* create a dict to hold the info */
563    dict = tr_bencListAddDict( list, TR_N_ELEMENTS( gl_inf ) );
564
565    /* populate the dict */
566    for( it=gl_inf, end=it+TR_N_ELEMENTS(gl_inf); it!=end; ++it )
567    {
568        if( types & it->type )
569        {
570            tr_benc * val = tr_bencDictAdd( dict, it->name );
571
572            switch( it->type )
573            {
574                case IPC_INF_COMMENT:
575                    tr_bencInitStrDup( val, inf->comment ? inf->comment : "" );
576                    break;
577                case IPC_INF_CREATOR:
578                    tr_bencInitStrDup( val, inf->creator ? inf->creator : "" );
579                    break;
580                case IPC_INF_DATE:
581                    tr_bencInitInt( val, inf->dateCreated );
582                    break;
583                case IPC_INF_FILES: {
584                    tr_file_index_t f;
585                    tr_bencInitList( val, inf->fileCount );
586                    for( f=0; f<inf->fileCount; ++f ) {
587                        tr_benc * file = tr_bencListAddDict( val, 2 );
588                        tr_bencDictAddStr( file, "name", inf->files[f].name );
589                        tr_bencDictAddInt( file, "size", inf->files[f].length );
590                    }
591                    break;
592                }
593                case IPC_INF_HASH:
594                    tr_bencInitStr( val, inf->hashString, -1, 1 );
595                    break;
596                case IPC_INF_ID:
597                    tr_bencInitInt( val, torrent_id );
598                    break;
599                case IPC_INF_NAME:
600                    tr_bencInitStrDup( val, inf->name );
601                    break;
602                case IPC_INF_PATH:
603                    tr_bencInitStrDup( val, inf->torrent );
604                    break;
605                case IPC_INF_PRIVATE:
606                    tr_bencInitInt( val, inf->isPrivate ? 1 : 0 );
607                    break;
608                case IPC_INF_SIZE:
609                    tr_bencInitInt( val, inf->totalSize );
610                    break;
611                case IPC_INF_TRACKERS: {
612                    int j;
613                    int prevTier = -1;
614                    tr_benc * tier = NULL;
615                    tr_bencInitList( val, 0 );
616                    for( j=0; j<inf->trackerCount; ++j ) {
617                        if( prevTier != inf->trackers[j].tier ) {
618                            prevTier = inf->trackers[j].tier;
619                            tier = tr_bencListAddList( val, 0 );
620                        }
621                        filltracker( tr_bencListAdd( tier ), &inf->trackers[j] );
622                    }
623                    break;
624                }
625                default:
626                    assert( 0 );
627                    break;
628            }
629        }
630    }
631
632    return 0;
633}
634
635/**
636 * append to "list" a dictionary whose keys are
637 * the string keys from gl_stat and whose values
638 * are torrent statistics set from "st".
639 *
640 * "types" is a bitwise-and'ed set of fields
641 * from the IPC_INF_* enum in ipcparse.h.
642 * It specifies what to put in the dictionary.
643 */
644int
645ipc_addstat( tr_benc      * list,
646             int            torrent_id,
647             tr_torrent   * tor,
648             int            types )
649{
650    const tr_stat * st = tr_torrentStatCached( tor );
651    const struct inf * it;
652    const struct inf * end;
653    tr_benc  * dict;
654
655    /* always send torrent id */
656    types |= IPC_ST_ID;
657
658    /* add the dictionary child */
659    dict = tr_bencListAddDict( list, TR_N_ELEMENTS( gl_stat ) );
660
661    /* populate the dict */
662    for( it=gl_stat, end=it+TR_N_ELEMENTS(gl_stat); it!=end; ++it )
663    {
664        if( types & it->type )
665        {
666            tr_benc * val = tr_bencDictAdd( dict, it->name );
667
668            switch( it->type )
669            {
670                case IPC_ST_COMPLETED:
671                case IPC_ST_DOWNVALID:
672                    tr_bencInitInt( val, st->haveValid );
673                    break;
674                case IPC_ST_DOWNSPEED:
675                    tr_bencInitInt( val, st->rateDownload * 1024 );
676                    break;
677                case IPC_ST_DOWNTOTAL:
678                    tr_bencInitInt( val, st->downloadedEver );
679                    break;
680                case IPC_ST_ERROR: {
681                    const char * s;
682                    const tr_errno e = st->error;
683                    if( !e ) s = "";
684                    else if( e == TR_ERROR_ASSERT )          s = "assert";
685                    else if( e == TR_ERROR_IO_PERMISSIONS )  s = "io-permissions";
686                    else if( e == TR_ERROR_IO_SPACE )        s = "io-space";
687                    else if( e == TR_ERROR_IO_FILE_TOO_BIG ) s = "io-file-too-big";
688                    else if( e == TR_ERROR_IO_OPEN_FILES )   s = "io-open-files";
689                    else if( TR_ERROR_IS_IO( e ) )           s = "io-other";
690                    else if( e == TR_ERROR_TC_ERROR )        s = "tracker-error";
691                    else if( e == TR_ERROR_TC_WARNING )      s = "tracker-warning";
692                    else if( TR_ERROR_IS_TC( e ) )           s = "tracker-other";
693                    else                                     s = "other";
694                    tr_bencInitStrDup( val, s );
695                    break;
696                }
697                case IPC_ST_ERRMSG: {
698                    const char * s;
699                    if( !st->error ) s = "";
700                    else if( !*st->errorString ) s = "other";
701                    else s = st->errorString;
702                    tr_bencInitStrDup( val, s );
703                    break;
704                }
705                case IPC_ST_ETA:
706                    tr_bencInitInt( val, st->eta );
707                    break;
708                case IPC_ST_ID:
709                    tr_bencInitInt( val, torrent_id );
710                    break;
711                case IPC_ST_PEERDOWN:
712                    tr_bencInitInt( val, st->peersSendingToUs );
713                    break;
714                case IPC_ST_PEERFROM: {
715                    const int * c = st->peersFrom;
716                    tr_bencInitDict( val, 4 );
717                    tr_bencDictAddInt( val, "cache",    c[TR_PEER_FROM_CACHE] );
718                    tr_bencDictAddInt( val, "incoming", c[TR_PEER_FROM_INCOMING] );
719                    tr_bencDictAddInt( val, "pex",      c[TR_PEER_FROM_PEX] );
720                    tr_bencDictAddInt( val, "tracker",  c[TR_PEER_FROM_TRACKER] );
721                    break;
722                }
723                case IPC_ST_PEERMAX:
724                    tr_bencInitInt( val, tor->maxConnectedPeers );
725                    break;
726                case IPC_ST_PEERTOTAL:
727                    tr_bencInitInt( val, st->peersConnected );
728                    break;
729                case IPC_ST_PEERUP:
730                    tr_bencInitInt( val, st->peersGettingFromUs );
731                    break;
732                case IPC_ST_RUNNING:
733                    tr_bencInitInt( val, TR_STATUS_IS_ACTIVE(st->status) );
734                    break;
735                case IPC_ST_STATE: {
736                    const char * s;
737                    if( TR_STATUS_CHECK_WAIT & st->status )    s = "waiting to check";
738                    else if( TR_STATUS_CHECK & st->status )    s = "checking";
739                    else if( TR_STATUS_DOWNLOAD & st->status ) s = "downloading";
740                    else if( TR_STATUS_SEED & st->status )     s = "seeding";
741                    else if( TR_STATUS_STOPPED & st->status )  s = "paused";
742                    else                                       s = "error";
743                    tr_bencInitStr( val, s, -1, 1 );
744                    break;
745                }
746                case IPC_ST_SWARM:
747                    tr_bencInitInt( val, st->swarmspeed * 1024 );
748                    break;
749                case IPC_ST_TRACKER:
750                    filltracker( val, st->tracker );
751                    break;
752                case IPC_ST_TKDONE:
753                    tr_bencInitInt( val, st->completedFromTracker );
754                    break;
755                case IPC_ST_TKLEECH:
756                    tr_bencInitInt( val, st->leechers );
757                    break;
758                case IPC_ST_TKSEED:
759                    tr_bencInitInt( val, st->seeders );
760                    break;
761                case IPC_ST_UPSPEED:
762                    tr_bencInitInt( val, st->rateUpload * 1024 );
763                    break;
764                case IPC_ST_UPTOTAL:
765                    tr_bencInitInt( val, st->uploadedEver );
766                    break;
767                default:
768                    assert( 0 );
769                    break;
770            }
771        }
772    }
773
774    return 0;
775}
776
777/**
778 * This reads a handshake message from the client to decide
779 * which IPC protocol version to use.
780 * Returns 0 on success; otherwise, returns -1 and sets errno.
781 *
782 * @see ipc_handleMessages()
783 * @see ipc_mkvers()
784 */
785static int
786handlevers( struct ipc_info * info, tr_benc * dict )
787{
788    tr_benc * vers;
789    int64_t      min, max;
790
791    if( !tr_bencIsDict( dict ) )
792    {
793        errno = EINVAL;
794        return -1;
795    }
796
797    vers = tr_bencDictFind( dict, MSGNAME( IPC_MSG_VERSION ) );
798    if( NULL == vers )
799    {
800        errno = EINVAL;
801        return -1;
802    }
803
804    switch( vers->type )
805    {
806        case TYPE_INT:
807            min = max = vers->val.i;
808            break;
809        case TYPE_DICT:
810            min = max = -1;
811            tr_bencDictFindInt( vers, "min", &min );
812            tr_bencDictFindInt( vers, "max", &max );
813            break;
814        default:
815            min = max = -1;
816            break;
817    }
818
819    if( 0 >= min || 0 >= max || INT_MAX < min || INT_MAX < max )
820    {
821        errno = EINVAL;
822        return -1;
823    }
824
825    assert( PROTO_VERS_MIN <= PROTO_VERS_MAX );
826    if( min > max )
827    {
828        errno = EINVAL;
829        return -1;
830    }
831    if( PROTO_VERS_MAX < min || PROTO_VERS_MIN > max )
832    {
833        errno = EPERM;
834        return -1;
835    }
836
837    info->vers = MIN( PROTO_VERS_MAX, max );
838
839    return 0;
840}
841
842static int
843compareNameToMsg( const void * a, const void * b )
844{
845    const struct msg * msg = b;
846    return strcmp( a, msg->name );
847}
848
849static const struct msg *
850msglookup( const char * name )
851{
852    return bsearch( name,
853                    gl_msgs, TR_N_ELEMENTS( gl_msgs ), sizeof( struct msg ),
854                    compareNameToMsg );
855}
856
857enum ipc_msg
858ipc_msgid( const struct ipc_info * info, const char * name )
859{
860    const struct msg * msg = msglookup( name );
861
862    return msg && sessionSupportsMessage( info, msg->id )
863        ? msg->id
864        : IPC__MSG_COUNT;
865}
866
867/**
868 * Invokes the trd_msgfunc for the message passed in.
869 * Returns 0 on success; otherwise, returns -1 and sets errno.
870 */
871static int
872callmsgfunc( const struct ipc_info  * info,
873             tr_benc                * name,
874             tr_benc                * val,
875             tr_benc                * tagval,
876             void                   * user_data )
877{
878    const struct msg * msg;
879    int64_t            tag;
880
881    /* extract tag from tagval */
882    if( !tagval )
883        tag = -1;
884    else if( tr_bencIsInt( tagval ) )
885        tag = tagval->val.i;
886    else {
887        errno = EINVAL;
888        return -1;
889    }
890
891    /* find the msg corresponding to `name' */
892    if( !tr_bencIsString( name ) ) {
893        errno = EINVAL;
894        return -1;
895    }
896    msg = msglookup( name->val.s.s );
897
898    if( msg && msg->minvers <= info->vers )
899    {
900        if( info->funcs->msgs[msg->id] != NULL )
901        {
902            (*info->funcs->msgs[msg->id])( msg->id, val, tag, user_data );
903        }
904        else if( info->funcs->def )
905        {
906            info->funcs->def( msg->id, val, tag, user_data );
907        }
908    }
909    else if( NULL != info->funcs->def )
910        info->funcs->def( IPC__MSG_UNKNOWN, NULL, tag, user_data );
911
912    return 0;
913}
914
915static int
916handlemsgs( const struct ipc_info  * session,
917            tr_benc                * message,
918            void                   * user_data )
919{
920    tr_benc * name, * val, * tag;
921
922    assert( ipc_hasvers( session ) );
923
924    if( DICTPAYLOAD( session ) )
925    {
926        int ii;
927
928        if( TYPE_DICT != message->type || message->val.l.count % 2 )
929        {
930            errno = EINVAL;
931            return -1;
932        }
933
934        for( ii = 0; ii < message->val.l.count; ii += 2 )
935        {
936            assert( ii + 1 < message->val.l.count );
937            name = &message->val.l.vals[ii];
938            val  = &message->val.l.vals[ii+1];
939            if( 0 > callmsgfunc( session, name, val, NULL, user_data ) )
940                return -1;
941        }
942    }
943    else
944    {
945        if( TYPE_LIST != message->type || 2 > message->val.l.count )
946        {
947            errno = EINVAL;
948            return -1;
949        }
950
951        name = &message->val.l.vals[0];
952        val  = &message->val.l.vals[1];
953        tag  = ( 2 == message->val.l.count ? NULL : &message->val.l.vals[2] );
954        if( 0 > callmsgfunc( session, name, val, tag, user_data ) )
955            return -1;
956    }
957
958    return 0;
959}
960
961ssize_t
962ipc_handleMessages( struct ipc_info  * info,
963                    const uint8_t    * msgs,
964                    ssize_t            msgslen,
965                    void             * user_data )
966{
967    char        hex[IPC_MIN_MSG_LEN+1], * end;
968    ssize_t     off, len;
969    tr_benc  benc;
970
971    for( off = 0; off + IPC_MIN_MSG_LEN < msgslen; off += IPC_MIN_MSG_LEN + len )
972    {
973        memcpy( hex, msgs + off, IPC_MIN_MSG_LEN );
974        hex[IPC_MIN_MSG_LEN] = '\0';
975        end = NULL;
976        len = strtol( hex, &end, 16 );
977        if( hex + IPC_MIN_MSG_LEN != end ||
978            0 > len || IPC_MAX_MSG_LEN < len )
979        {
980            errno = EINVAL;
981            return -1;
982        }
983        if( off + IPC_MIN_MSG_LEN + len > msgslen )
984        {
985            break;
986        }
987        errno = 0;
988        if( tr_bencLoad( msgs + off + IPC_MIN_MSG_LEN, len, &benc, NULL ) )
989        {
990            if( 0 == errno )
991            {
992                errno = EINVAL;
993            }
994            return -1;
995        }
996        if( 0 > ( ipc_hasvers( info ) ? handlemsgs( info, &benc, user_data ) :
997                                        handlevers( info, &benc ) ) )
998        {
999            SAFEBENCFREE( &benc );
1000            return -1;
1001        }
1002        tr_bencFree( &benc );
1003    }
1004
1005    return off;
1006}
1007
1008static int
1009compareNameToInf( const void * a, const void * b )
1010{
1011    const struct inf * inf = b;
1012    return strcmp( a, inf->name );
1013}
1014
1015/**
1016 * Convert a benc list of string keys from gl_inf or gl_stat
1017 * into a bitwise-or'ed int representation.
1018 * msg_id must be either IPC_MSG_INFO or IPC_MSG_STAT.
1019 * @see ipc_infoname()
1020 */
1021int
1022ipc_infotypes( enum ipc_msg id, const tr_benc * list )
1023{
1024    const struct inf     * array;
1025    size_t                 len;
1026    int                    i;
1027    int                    ret;
1028
1029    switch( id )
1030    {
1031        case IPC_MSG_INFO:
1032            array = gl_inf;
1033            len   = TR_N_ELEMENTS( gl_inf );
1034            break;
1035        case IPC_MSG_STAT:
1036            array = gl_stat;
1037            len   = TR_N_ELEMENTS( gl_stat );
1038            break;
1039        default:
1040            assert( 0 );
1041            break;
1042    }
1043
1044    ret = IPC_INF_ID;
1045
1046    if( !tr_bencIsList( list ) )
1047        return ret;
1048
1049    for( i=0; i<list->val.l.count; ++i )
1050    {
1051        const tr_benc * name = &list->val.l.vals[i];
1052        const struct inf * inf;
1053
1054        if( !tr_bencIsString( name ) )
1055            continue;
1056
1057        inf = bsearch( name->val.s.s,
1058                       array, len, sizeof( struct inf ),
1059                       compareNameToInf );
1060        if( inf )
1061            ret |= inf->type;
1062    }
1063
1064    return ret;
1065}
1066
1067/**
1068 * This function is the reverse of ipc_infotypes:
1069 * it returns the string key that corresponds to the type passed in.
1070 * Type is one of the IPC_INF_* or IPC_ST_* enums from ipcparse.h.
1071 * msg_id must be either IPC_MSG_INFO or IPC_MSG_STAT.
1072 * @see ipc_infotypes()
1073 */
1074const char *
1075ipc_infoname( enum ipc_msg id, int type )
1076{
1077    const struct inf * it;
1078    const struct inf * end;
1079
1080    switch( id )
1081    {
1082        case IPC_MSG_INFO:
1083            it = gl_inf;
1084            end = it + TR_N_ELEMENTS( gl_inf );
1085            break;
1086        case IPC_MSG_STAT:
1087            it = gl_stat;
1088            end = it + TR_N_ELEMENTS( gl_stat );
1089            break;
1090        default:
1091            assert( 0 );
1092            break;
1093    }
1094
1095    for( ; it!=end; ++it )
1096        if( it->type == type )
1097            return it->name;
1098
1099    assert( 0 );
1100    return NULL;
1101}
Note: See TracBrowser for help on using the repository browser.