source: trunk/libtransmission/ipc.c @ 5817

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

json ipc: implement torrent-add, torrent-set-file, torrent-get-file

File size: 18.1 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <assert.h>
14#include <string.h> /* strcmp */
15
16#include "transmission.h"
17#include "bencode.h"
18#include "ipc.h"
19#include "torrent.h"
20#include "utils.h"
21
22#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( (ary)[0] ) )
23
24/***
25****
26***/
27
28static tr_torrent **
29getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount )
30{
31    tr_torrent ** torrents = NULL;
32    int torrentCount = 0;
33    tr_benc * ids;
34
35    if( tr_bencDictFindList( args, "ids", &ids ) )
36    {
37        int i;
38        const int n = tr_bencListSize( ids );
39
40        torrents = tr_new0( tr_torrent*, n );
41
42        for( i=0; i<n; ++i )
43        {
44            tr_torrent * tor = NULL;
45            tr_benc * node = tr_bencListChild( ids, i );
46            int64_t id;
47            const char * str;
48            if( tr_bencGetInt( node, &id ) )
49                tor = tr_torrentFindFromId( handle, id );
50            else if( tr_bencGetStr( node, &str ) )
51                tor = tr_torrentFindFromHashString( handle, str );
52            if( tor )
53                torrents[torrentCount++] = tor;
54        }
55    }
56    else /* all of them */
57    {
58        tr_torrent * tor = NULL;
59        const int n = tr_torrentCount( handle );
60        torrents = tr_new0( tr_torrent*, n );
61        while(( tor = tr_torrentNext( handle, tor )))
62            torrents[torrentCount++] = tor;
63    }
64
65    *setmeCount = torrentCount;
66    return torrents;
67}
68
69typedef void( *tor_func )( tr_torrent * tor );
70
71static void callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func )
72{
73    int i, torrentCount;
74    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
75    for( i=0; i<torrentCount; ++i )
76        ( *func )( torrents[i] );
77    tr_free( torrents );
78}
79
80static const char*
81torrentStart( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
82{
83    callTorrentFunc( h, args_in, tr_torrentStart );
84    return NULL;
85}
86
87static const char*
88torrentStop( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
89{
90    callTorrentFunc( h, args_in, tr_torrentStop );
91    return NULL;
92}
93
94static const char*
95torrentClose( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
96{
97    callTorrentFunc( h, args_in, tr_torrentClose );
98    return NULL;
99}
100
101static const char*
102torrentVerify( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
103{
104    callTorrentFunc( h, args_in, tr_torrentVerify );
105    return NULL;
106}
107
108static const char*
109torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
110{
111    int i, torrentCount;
112    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
113    tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount );
114
115    for( i=0; i<torrentCount; ++i )
116    {
117        tr_torrent * tor = torrents[i];
118        const tr_stat * st = tr_torrentStat( tor );
119        tr_benc * d = tr_bencListAddDict( list, 32 );
120        tr_benc * t;
121        const int * f = st->peersFrom;
122        const struct tr_tracker_stat * s = &st->tracker_stat;
123
124        tr_bencDictAddInt( d, "id", tor->uniqueId );
125        tr_bencDictAddInt( d, "status", st->status );
126        tr_bencDictAddInt( d, "error", st->error );
127        tr_bencDictAddStr( d, "errorString", st->errorString );
128        tr_bencDictAddDouble( d, "recheckProgress", st->recheckProgress );
129        tr_bencDictAddDouble( d, "percentComplete", st->percentComplete );
130        tr_bencDictAddDouble( d, "percentDone", st->percentDone );
131        tr_bencDictAddDouble( d, "rateDownload", st->rateDownload );
132        tr_bencDictAddDouble( d, "rateUpload", st->rateUpload );
133        tr_bencDictAddDouble( d, "swarmspeed", st->swarmspeed );
134        tr_bencDictAddDouble( d, "ratio", st->ratio );
135        tr_bencDictAddInt( d, "eta", st->eta );
136        tr_bencDictAddInt( d, "peersKnown", st->peersKnown );
137        tr_bencDictAddInt( d, "peersConnected", st->peersConnected );
138        tr_bencDictAddInt( d, "peersSendingToUs", st->peersSendingToUs );
139        tr_bencDictAddInt( d, "peersGettingFromUs", st->peersGettingFromUs );
140        tr_bencDictAddInt( d, "seeders", st->seeders );
141        tr_bencDictAddInt( d, "leechers", st->leechers );
142        tr_bencDictAddInt( d, "completedFromTracker", st->completedFromTracker );
143        tr_bencDictAddInt( d, "manualAnnounceTime", st->manualAnnounceTime );
144        tr_bencDictAddInt( d, "sizeWhenDone", st->sizeWhenDone );
145        tr_bencDictAddInt( d, "leftUntilDone", st->leftUntilDone );
146        tr_bencDictAddInt( d, "desiredAvailable", st->desiredAvailable );
147        tr_bencDictAddInt( d, "corruptEver", st->corruptEver );
148        tr_bencDictAddInt( d, "uploadedEver", st->uploadedEver );
149        tr_bencDictAddInt( d, "downloadedEver", st->downloadedEver );
150        tr_bencDictAddInt( d, "haveValid", st->haveValid );
151        tr_bencDictAddInt( d, "haveUnchecked", st->haveUnchecked );
152        tr_bencDictAddInt( d, "startDate", st->startDate );
153        tr_bencDictAddInt( d, "activityDate", st->activityDate );
154        t = tr_bencDictAddDict( d, "peersFrom", 4 );
155            tr_bencDictAddInt( t, "cache",    f[TR_PEER_FROM_CACHE] );
156            tr_bencDictAddInt( t, "incoming", f[TR_PEER_FROM_INCOMING] );
157            tr_bencDictAddInt( t, "pex",      f[TR_PEER_FROM_PEX] );
158            tr_bencDictAddInt( t, "tracker",  f[TR_PEER_FROM_TRACKER] );
159        t = tr_bencDictAddDict( d, "tracker_stat", 7 );
160            tr_bencDictAddStr( t, "scrapeResponse", s->scrapeResponse );
161            tr_bencDictAddStr( t, "announceResponse", s->announceResponse );
162            tr_bencDictAddInt( t, "lastScrapeTime", s->lastScrapeTime );
163            tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime );
164            tr_bencDictAddInt( t, "lastAnnounceTime", s->lastAnnounceTime );
165            tr_bencDictAddInt( t, "nextAnnounceTime", s->nextAnnounceTime );
166            tr_bencDictAddInt( t, "nextManualAnnounceTime", s->nextManualAnnounceTime );
167    }
168
169    /* cleanup */
170    tr_free( torrents );
171    return NULL;
172}
173
174static void
175addFiles( const tr_info * info, tr_benc * files )
176{
177    unsigned int i;
178    for( i=0; i<info->fileCount; ++i )
179    {
180        const tr_file * file = &info->files[i];
181        tr_benc * d = tr_bencListAddDict( files, 4 );
182        tr_bencDictAddInt( d, "length", file->length );
183        tr_bencDictAddStr( d, "name", file->name );
184        tr_bencDictAddInt( d, "priority", file->priority );
185        tr_bencDictAddInt( d, "dnd", file->dnd );
186    }
187}
188
189static void
190addTrackers( const tr_info * info, tr_benc * trackers )
191{
192    int i;
193    for( i=0; i<info->trackerCount; ++i )
194    {
195        const tr_tracker_info * t = &info->trackers[i];
196        tr_benc * d = tr_bencListAddDict( trackers, 3 );
197        tr_bencDictAddInt( d, "tier", t->tier );
198        tr_bencDictAddStr( d, "announce", t->announce );
199        tr_bencDictAddStr( d, "scrape", t->scrape );
200    }
201}
202
203static void
204serializeInfo( const tr_torrent * tor, tr_benc * d )
205{
206    const tr_info * inf = tr_torrentInfo( tor );
207    tr_bencInitDict( d, 14 ); 
208    tr_bencDictAddInt( d, "id", tor->uniqueId );
209    tr_bencDictAddStr( d, "torrent", inf->torrent );
210    tr_bencDictAddStr( d, "hashString", inf->hashString );
211    tr_bencDictAddStr( d, "name", inf->name );
212    tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" );
213    tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" );
214    tr_bencDictAddInt( d, "isPrivate", inf->isPrivate );
215    tr_bencDictAddInt( d, "isMultifile", inf->isMultifile );
216    tr_bencDictAddInt( d, "dateCreated", inf->dateCreated );
217    tr_bencDictAddInt( d, "pieceSize", inf->pieceSize );
218    tr_bencDictAddInt( d, "pieceCount", inf->pieceCount );
219    tr_bencDictAddInt( d, "totalSize", inf->totalSize );
220    addFiles   ( inf, tr_bencDictAddList( d, "files", inf->fileCount ) );
221    addTrackers( inf, tr_bencDictAddList( d, "files", inf->trackerCount ) );
222}
223
224static const char*
225torrentInfo( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
226{
227    int i, torrentCount;
228    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
229    tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount );
230
231    for( i=0; i<torrentCount; ++i )
232        serializeInfo( torrents[i], tr_bencListAdd( list ) );
233
234    tr_free( torrents );
235    return NULL;
236}
237
238static const char*
239torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
240{
241    int i, torrentCount;
242    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
243    tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount );
244
245    for( i=0; i<torrentCount; ++i )
246    {
247        tr_torrent * tor = torrents[i];
248        tr_benc * d = tr_bencListAddDict( list, 4 );
249        tr_bencDictAddInt( d, "id",
250                           tor->uniqueId );
251        tr_bencDictAddInt( d, "max-peers",
252                           tr_torrentGetMaxConnectedPeers( tor ) );
253        tr_bencDictAddInt( d, "speed-limit-down",
254                           tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
255        tr_bencDictAddInt( d, "speed-limit-up",
256                           tr_torrentGetSpeedLimit( tor, TR_UP ) );
257    }
258
259    tr_free( torrents );
260    return NULL;
261}
262
263static const char*
264torrentSet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
265{
266    int i, torrentCount;
267    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
268
269    for( i=0; i<torrentCount; ++i )
270    {
271        int64_t tmp;
272        tr_torrent * tor = torrents[i];
273        if( tr_bencDictFindInt( args_in, "max-peers", &tmp ) )
274            tr_torrentSetMaxConnectedPeers( tor, tmp );
275        if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
276            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
277        if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
278            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
279    }
280
281    tr_free( torrents );
282    return NULL;
283}
284
285typedef int( *fileTestFunc )( const tr_torrent * tor, int i );
286
287static int
288testFileHigh( const tr_torrent * tor, int i )
289{
290    return tor->info.files[i].priority == TR_PRI_HIGH;
291}
292static int
293testFileLow( const tr_torrent * tor, int i )
294{
295    return tor->info.files[i].priority == TR_PRI_LOW;
296}
297static int
298testFileNormal( const tr_torrent * tor, int i )
299{
300    return tor->info.files[i].priority == TR_PRI_NORMAL;
301}
302static int
303testFileDND( const tr_torrent * tor, int i )
304{
305    return tor->info.files[i].dnd != 0;
306}
307static int
308testFileDownload( const tr_torrent * tor, int i )
309{
310    return tor->info.files[i].dnd == 0;
311}
312
313static void
314buildFileList( const tr_torrent * tor, tr_benc * dict,
315               const char * key, fileTestFunc func )
316{
317    int i;
318    const int n = tor->info.fileCount;
319    tr_benc * list;
320    int * files = tr_new0( int, n );
321    int fileCount = 0;
322   
323    for( i=0; i<n; ++i )
324        if( func( tor, i ) )
325            files[fileCount++] = i;
326
327    list = tr_bencDictAddList( dict, key, fileCount );
328
329    for( i=0; i<fileCount; ++i )
330        tr_bencListAddInt( list, files[i] );
331
332    tr_free( files );
333}
334
335static const char*
336torrentGetFile( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
337{
338    int i, torrentCount;
339    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
340    tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount );
341
342    for( i=0; i<torrentCount; ++i )
343    {
344        const tr_torrent * tor = torrents[i];
345        tr_benc * d = tr_bencListAddDict( list, 6 );
346        tr_bencDictAddInt( d, "id", tor->uniqueId );
347        buildFileList( tor, d, "priority-high", testFileHigh );
348        buildFileList( tor, d, "priority-low", testFileLow );
349        buildFileList( tor, d, "priority-normal", testFileNormal );
350        buildFileList( tor, d, "download", testFileDownload );
351        buildFileList( tor, d, "no-download", testFileDND );
352    }
353
354    tr_free( torrents );
355    return NULL;
356}
357
358static void
359setFilePriorities( tr_torrent * tor, int priority, tr_benc * list )
360{
361    const int n = tr_bencListSize( list );
362    int i;
363    int64_t tmp;
364    int fileCount = 0;
365    tr_file_index_t * files = tr_new0( tr_file_index_t, n );
366
367    for( i=0; i<n; ++i )
368        if( tr_bencGetInt( tr_bencListChild(list,i), &tmp ) )
369            files[fileCount++] = tmp;
370
371    if( fileCount )
372        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
373
374    tr_free( files );
375}
376
377static void
378setFileDLs( tr_torrent * tor, int do_download, tr_benc * list )
379{
380    const int n = tr_bencListSize( list );
381    int i;
382    int64_t tmp;
383    int fileCount = 0;
384    tr_file_index_t * files = tr_new0( tr_file_index_t, n );
385
386    for( i=0; i<n; ++i )
387        if( tr_bencGetInt( tr_bencListChild(list,i), &tmp ) )
388            files[fileCount++] = tmp;
389
390    if( fileCount )
391        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
392
393    tr_free( files );
394}
395
396static const char*
397torrentSetFile( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
398{
399    int i, torrentCount;
400    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
401
402    for( i=0; i<torrentCount; ++i )
403    {
404        tr_benc * files;
405        tr_torrent * tor = torrents[i];
406
407        if( tr_bencDictFindList( args_in, "priority-high", &files ) )
408            setFilePriorities( tor, TR_PRI_HIGH, files );
409        if( tr_bencDictFindList( args_in, "priority-low", &files ) )
410            setFilePriorities( tor, TR_PRI_LOW, files );
411        if( tr_bencDictFindList( args_in, "priority-normal", &files ) )
412            setFilePriorities( tor, TR_PRI_NORMAL, files );
413        if( tr_bencDictFindList( args_in, "download", &files ) )
414            setFileDLs( tor, TRUE, files );
415        if( tr_bencDictFindList( args_in, "no-download", &files ) )
416            setFileDLs( tor, FALSE, files );
417    }
418
419    tr_free( torrents );
420    return NULL;
421}
422
423/***
424****
425***/
426
427static const char*
428torrentAdd( tr_handle * h, tr_benc * args_in, tr_benc * args_out )
429{
430    const char * filename;
431    if( !tr_bencDictFindStr( args_in, "filename", &filename ) )
432        return "no filename specified";
433    else {
434        int64_t i;
435        int err = 0;
436        const char * str;
437        tr_ctor * ctor;
438        tr_torrent * tor;
439
440        ctor = tr_ctorNew( h );
441        tr_ctorSetMetainfoFromFile( ctor, filename );
442        if( tr_bencDictFindInt( args_in, "autostart", &i ) )
443            tr_ctorSetPaused( ctor, TR_FORCE, !i );
444        if( tr_bencDictFindInt( args_in, "max-peers", &i ) )
445            tr_ctorSetMaxConnectedPeers( ctor, TR_FORCE, i );
446        if( tr_bencDictFindStr( args_in, "destination", &str ) )
447            tr_ctorSetDestination( ctor, TR_FORCE, str );
448        tor = tr_torrentNew( h, ctor, &err );
449        tr_ctorFree( ctor );
450
451        if( tor )
452            serializeInfo( tor, tr_bencDictAdd( args_out, "torrent-added" ) );
453        else if( err == TR_EDUPLICATE )
454            return "duplicate torrent";
455        else if( err == TR_EINVALID )
456            return "invalid or corrupt torrent file";
457    }
458
459    return NULL;
460}
461
462/***
463****
464***/
465
466static const char*
467sessionSet( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
468{
469    /* TODO */
470    return NULL;
471}
472
473static const char*
474sessionGet( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
475{
476    /* TODO */
477    return NULL;
478}
479
480/***
481****
482***/
483
484typedef const char* (handler)( tr_handle*, tr_benc*, tr_benc* );
485
486struct request_handler
487{
488    const char * name;
489    handler * func;
490} request_handlers[] = { 
491    { "torrent-start", torrentStart },
492    { "torrent-stop", torrentStop },
493    { "torrent-close", torrentClose },
494    { "torrent-verify", torrentVerify },
495    { "torrent-status", torrentStatus },
496    { "torrent-info", torrentInfo },
497    { "torrent-add", torrentAdd },
498    { "torrent-set", torrentSet },
499    { "torrent-get", torrentGet },
500    { "torrent-set-file", torrentSetFile },
501    { "torrent-get-file", torrentGetFile },
502    { "session-set", sessionSet },
503    { "session-get", sessionGet }
504};
505
506static char*
507request_exec( struct tr_handle * handle,
508              tr_benc          * request,
509              int              * response_len )
510{
511    int64_t i;
512    const char * str;
513    char * out;
514    tr_benc response;
515    tr_benc * headers_in = NULL;
516    tr_benc * body_in = NULL;
517    tr_benc * args_in = NULL;
518    tr_benc * headers_out = NULL;
519    tr_benc * body_out = NULL;
520    tr_benc * args_out = NULL;
521    const char * result = NULL;
522
523    headers_in = tr_bencDictFind( request, "headers" );
524    body_in = tr_bencDictFind( request, "body" );
525    args_in = tr_bencDictFind( body_in, "args" );
526
527    /* build the response skeleton */
528    tr_bencInitDict( &response, 2 );
529    headers_out = tr_bencDictAddDict( &response, "headers", 2 );
530    tr_bencDictAddStr( headers_out, "type", "response" );
531    if( tr_bencDictFindInt( headers_in, "tag", &i ) )
532        tr_bencDictAddInt( headers_out, "tag", i );
533    body_out = tr_bencDictAddDict( &response, "body", 2 );
534    args_out = tr_bencDictAddDict( body_out, "args", 0 );
535
536    /* parse the request */
537    if( !tr_bencDictFindStr( body_in, "name", &str ) )
538        result = "no request name given";
539    else {
540        const int n = TR_N_ELEMENTS( request_handlers );
541        for( i=0; i<n; ++i )
542            if( !strcmp( str, request_handlers[i].name ) )
543                break;
544        result = i==n
545            ? "request name not recognized"
546            : (*request_handlers[i].func)( handle, args_in, args_out );
547    }
548
549    /* serialize & return the response */
550    if( !result )
551        result = "success";
552    tr_bencDictAddStr( body_out, "result", result );
553    out = tr_bencSave( &response, response_len ); /* TODO: json, not benc */
554    tr_bencFree( &response );
555    return out;
556}
557
558char*
559tr_ipc_request_exec( struct tr_handle  * handle,
560                     const void        * request_json,
561                     int                 request_len,
562                     int               * response_len )
563{
564    tr_benc top;
565    int have_content = !tr_jsonParse( request_json, (const char*)request_json + request_len, &top, NULL );
566    char * ret = request_exec( handle, have_content ? &top : NULL, response_len );
567    if( have_content )
568        tr_bencFree( &top );
569    return ret;
570}
Note: See TracBrowser for help on using the repository browser.