source: trunk/libtransmission/rpcimpl.c @ 7238

Last change on this file since 7238 was 7238, checked in by charles, 12 years ago

(libT) #1379: support "partial seeds" BEP 22

  • Property svn:keywords set to Date Rev Author Id
File size: 30.8 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: rpcimpl.c 7238 2008-12-02 19:46:51Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <stdlib.h> /* strtol */
16#include <string.h> /* strcmp */
17
18#include "transmission.h"
19#include "bencode.h"
20#include "rpcimpl.h"
21#include "json.h"
22#include "session.h"
23#include "torrent.h"
24#include "utils.h"
25
26#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
27
28/***
29****
30***/
31
32static tr_rpc_callback_status
33notify( tr_handle *  session,
34        int          type,
35        tr_torrent * tor )
36{
37    tr_rpc_callback_status status = 0;
38
39    if( session->rpc_func )
40        status = session->rpc_func( session, type, tor,
41                                    session->rpc_func_user_data );
42
43    return status;
44}
45
46/***
47****
48***/
49
50static tr_torrent **
51getTorrents( tr_handle * handle,
52             tr_benc *   args,
53             int *       setmeCount )
54{
55    int           torrentCount = 0;
56    int64_t       id;
57    tr_torrent ** torrents = NULL;
58    tr_benc *     ids;
59
60    if( tr_bencDictFindList( args, "ids", &ids ) )
61    {
62        int       i;
63        const int n = tr_bencListSize( ids );
64
65        torrents = tr_new0( tr_torrent *, n );
66
67        for( i = 0; i < n; ++i )
68        {
69            tr_torrent * tor = NULL;
70            tr_benc *    node = tr_bencListChild( ids, i );
71            int64_t      id;
72            const char * str;
73            if( tr_bencGetInt( node, &id ) )
74                tor = tr_torrentFindFromId( handle, id );
75            else if( tr_bencGetStr( node, &str ) )
76                tor = tr_torrentFindFromHashString( handle, str );
77            if( tor )
78                torrents[torrentCount++] = tor;
79        }
80    }
81    else if( tr_bencDictFindInt( args, "ids", &id )
82           || tr_bencDictFindInt( args, "id", &id ) )
83    {
84        tr_torrent * tor;
85        torrents = tr_new0( tr_torrent *, 1 );
86        if( ( tor = tr_torrentFindFromId( handle, id ) ) )
87            torrents[torrentCount++] = tor;
88    }
89    else /* all of them */
90    {
91        tr_torrent * tor = NULL;
92        const int    n = tr_sessionCountTorrents( handle );
93        torrents = tr_new0( tr_torrent *, n );
94        while( ( tor = tr_torrentNext( handle, tor ) ) )
95            torrents[torrentCount++] = tor;
96    }
97
98    *setmeCount = torrentCount;
99    return torrents;
100}
101
102static const char*
103torrentStart( tr_handle *        h,
104              tr_benc *          args_in,
105              tr_benc * args_out UNUSED )
106{
107    int           i, torrentCount;
108    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
109
110    for( i = 0; i < torrentCount; ++i )
111    {
112        tr_torrent * tor = torrents[i];
113        tr_torrentStart( tor );
114        notify( h, TR_RPC_TORRENT_STARTED, tor );
115    }
116    tr_free( torrents );
117    return NULL;
118}
119
120static const char*
121torrentStop( tr_handle *        h,
122             tr_benc *          args_in,
123             tr_benc * args_out UNUSED )
124{
125    int           i, torrentCount;
126    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
127
128    for( i = 0; i < torrentCount; ++i )
129    {
130        tr_torrent * tor = torrents[i];
131        tr_torrentStop( tor );
132        notify( h, TR_RPC_TORRENT_STOPPED, tor );
133    }
134    tr_free( torrents );
135    return NULL;
136}
137
138static const char*
139torrentRemove( tr_handle *        h,
140               tr_benc *          args_in,
141               tr_benc * args_out UNUSED )
142{
143    int           i, torrentCount;
144    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
145
146    for( i = 0; i < torrentCount; ++i )
147    {
148        tr_torrent *                 tor = torrents[i];
149        const tr_rpc_callback_status status = notify(
150            h, TR_RPC_TORRENT_REMOVING, tor );
151        if( !( status & TR_RPC_NOREMOVE ) )
152            tr_torrentRemove( tor );
153    }
154    tr_free( torrents );
155    return NULL;
156}
157
158static const char*
159torrentVerify( tr_handle *        h,
160               tr_benc *          args_in,
161               tr_benc * args_out UNUSED )
162{
163    int           i, torrentCount;
164    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
165
166    for( i = 0; i < torrentCount; ++i )
167    {
168        tr_torrent * tor = torrents[i];
169        tr_torrentVerify( tor );
170        notify( h, TR_RPC_TORRENT_CHANGED, tor );
171    }
172    tr_free( torrents );
173    return NULL;
174}
175
176/***
177****
178***/
179
180static void
181addFiles( const tr_torrent * tor,
182          tr_benc *          list )
183{
184    tr_file_index_t i;
185    tr_file_index_t n;
186    const tr_info * info = tr_torrentInfo( tor );
187    tr_file_stat *  files = tr_torrentFiles( tor, &n );
188
189    for( i = 0; i < info->fileCount; ++i )
190    {
191        const tr_file * file = &info->files[i];
192        tr_benc *       d = tr_bencListAddDict( list, 3 );
193        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
194        tr_bencDictAddInt( d, "length", file->length );
195        tr_bencDictAddStr( d, "name", file->name );
196    }
197
198    tr_torrentFilesFree( files, n );
199}
200
201static void
202addWebseeds( const tr_info * info,
203             tr_benc *       webseeds )
204{
205    int i;
206
207    for( i = 0; i < info->webseedCount; ++i )
208        tr_bencListAddStr( webseeds, info->webseeds[i] );
209}
210
211static void
212addTrackers( const tr_info * info,
213             tr_benc *       trackers )
214{
215    int i;
216
217    for( i = 0; i < info->trackerCount; ++i )
218    {
219        const tr_tracker_info * t = &info->trackers[i];
220        tr_benc *               d = tr_bencListAddDict( trackers, 3 );
221        tr_bencDictAddStr( d, "announce", t->announce );
222        tr_bencDictAddStr( d, "scrape", t->scrape );
223        tr_bencDictAddInt( d, "tier", t->tier );
224    }
225}
226
227static void
228addPeers( const tr_torrent * tor,
229          tr_benc *          list )
230{
231    int            i;
232    int            peerCount;
233    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
234
235    tr_bencInitList( list, peerCount );
236
237    for( i = 0; i < peerCount; ++i )
238    {
239        tr_benc *            d = tr_bencListAddDict( list, 14 );
240        const tr_peer_stat * peer = peers + i;
241        tr_bencDictAddStr( d, "address", peer->addr );
242        tr_bencDictAddStr( d, "clientName", peer->client );
243        tr_bencDictAddInt( d, "clientIsChoked", peer->clientIsChoked );
244        tr_bencDictAddInt( d, "clientIsInterested",
245                           peer->clientIsInterested );
246        tr_bencDictAddStr( d, "flagStr", peer->flagStr );
247        tr_bencDictAddInt( d, "isDownloadingFrom", peer->isDownloadingFrom );
248        tr_bencDictAddInt( d, "isEncrypted", peer->isEncrypted );
249        tr_bencDictAddInt( d, "isIncoming", peer->isIncoming );
250        tr_bencDictAddInt( d, "isUploadingTo", peer->isUploadingTo );
251        tr_bencDictAddInt( d, "peerIsChoked", peer->peerIsChoked );
252        tr_bencDictAddInt( d, "peerIsInterested", peer->peerIsInterested );
253        tr_bencDictAddInt( d, "port", peer->port );
254        tr_bencDictAddDouble( d, "progress", peer->progress );
255        tr_bencDictAddInt( d, "rateToClient",
256                          (int)( peer->rateToClient * 1024.0 ) );
257        tr_bencDictAddInt( d, "rateToPeer",
258                          (int)( peer->rateToPeer * 1024.0 ) );
259    }
260
261    tr_torrentPeersFree( peers, peerCount );
262}
263
264static void
265addField( const tr_torrent * tor,
266          tr_benc *          d,
267          const char *       key )
268{
269    const tr_info * inf = tr_torrentInfo( tor );
270    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
271
272    if( !strcmp( key, "activityDate" ) )
273        tr_bencDictAddInt( d, key, st->activityDate );
274    else if( !strcmp( key, "addedDate" ) )
275        tr_bencDictAddInt( d, key, st->addedDate );
276    else if( !strcmp( key, "announceResponse" ) )
277        tr_bencDictAddStr( d, key, st->announceResponse );
278    else if( !strcmp( key, "announceURL" ) )
279        tr_bencDictAddStr( d, key, st->announceURL );
280    else if( !strcmp( key, "comment" ) )
281        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
282    else if( !strcmp( key, "corruptEver" ) )
283        tr_bencDictAddInt( d, key, st->corruptEver );
284    else if( !strcmp( key, "creator" ) )
285        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
286    else if( !strcmp( key, "dateCreated" ) )
287        tr_bencDictAddInt( d, key, inf->dateCreated );
288    else if( !strcmp( key, "desiredAvailable" ) )
289        tr_bencDictAddInt( d, key, st->desiredAvailable );
290    else if( !strcmp( key, "doneDate" ) )
291        tr_bencDictAddInt( d, key, st->doneDate );
292    else if( !strcmp( key, "downloadedEver" ) )
293        tr_bencDictAddInt( d, key, st->downloadedEver );
294    else if( !strcmp( key, "downloaders" ) )
295        tr_bencDictAddInt( d, key, st->downloaders );
296    else if( !strcmp( key, "downloadLimitMode" ) )
297        tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
298    else if( !strcmp( key, "downloadLimit" ) )
299        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
300    else if( !strcmp( key, "error" ) )
301        tr_bencDictAddInt( d, key, st->error );
302    else if( !strcmp( key, "errorString" ) )
303        tr_bencDictAddStr( d, key, st->errorString );
304    else if( !strcmp( key, "eta" ) )
305        tr_bencDictAddInt( d, key, st->eta );
306    else if( !strcmp( key, "files" ) )
307        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
308    else if( !strcmp( key, "hashString" ) )
309        tr_bencDictAddStr( d, key, tor->info.hashString );
310    else if( !strcmp( key, "haveUnchecked" ) )
311        tr_bencDictAddInt( d, key, st->haveUnchecked );
312    else if( !strcmp( key, "haveValid" ) )
313        tr_bencDictAddInt( d, key, st->haveValid );
314    else if( !strcmp( key, "id" ) )
315        tr_bencDictAddInt( d, key, st->id );
316    else if( !strcmp( key, "isPrivate" ) )
317        tr_bencDictAddInt( d, key, tr_torrentIsPrivate( tor ) );
318    else if( !strcmp( key, "lastAnnounceTime" ) )
319        tr_bencDictAddInt( d, key, st->lastAnnounceTime );
320    else if( !strcmp( key, "lastScrapeTime" ) )
321        tr_bencDictAddInt( d, key, st->lastScrapeTime );
322    else if( !strcmp( key, "leechers" ) )
323        tr_bencDictAddInt( d, key, st->leechers );
324    else if( !strcmp( key, "leftUntilDone" ) )
325        tr_bencDictAddInt( d, key, st->leftUntilDone );
326    else if( !strcmp( key, "manualAnnounceTime" ) )
327        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
328    else if( !strcmp( key, "maxConnectedPeers" ) )
329        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
330    else if( !strcmp( key, "name" ) )
331        tr_bencDictAddStr( d, key, inf->name );
332    else if( !strcmp( key, "nextAnnounceTime" ) )
333        tr_bencDictAddInt( d, key, st->nextAnnounceTime );
334    else if( !strcmp( key, "nextScrapeTime" ) )
335        tr_bencDictAddInt( d, key, st->nextScrapeTime );
336    else if( !strcmp( key, "peers" ) )
337        addPeers( tor, tr_bencDictAdd( d, key ) );
338    else if( !strcmp( key, "peersConnected" ) )
339        tr_bencDictAddInt( d, key, st->peersConnected );
340    else if( !strcmp( key, "peersFrom" ) )
341    {
342        tr_benc *   tmp = tr_bencDictAddDict( d, key, 4 );
343        const int * f = st->peersFrom;
344        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_CACHE] );
345        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
346        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
347        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
348    }
349    else if( !strcmp( key, "peersGettingFromUs" ) )
350        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
351    else if( !strcmp( key, "peersKnown" ) )
352        tr_bencDictAddInt( d, key, st->peersKnown );
353    else if( !strcmp( key, "peersSendingToUs" ) )
354        tr_bencDictAddInt( d, key, st->peersSendingToUs );
355    else if( !strcmp( key, "pieceCount" ) )
356        tr_bencDictAddInt( d, key, inf->pieceCount );
357    else if( !strcmp( key, "pieceSize" ) )
358        tr_bencDictAddInt( d, key, inf->pieceSize );
359    else if( !strcmp( key, "priorities" ) )
360    {
361        tr_file_index_t i;
362        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
363        for( i = 0; i < inf->fileCount; ++i )
364            tr_bencListAddInt( p, inf->files[i].priority );
365    }
366    else if( !strcmp( key, "rateDownload" ) )
367        tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
368    else if( !strcmp( key, "rateUpload" ) )
369        tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
370    else if( !strcmp( key, "recheckProgress" ) )
371        tr_bencDictAddDouble( d, key, st->recheckProgress );
372    else if( !strcmp( key, "scrapeResponse" ) )
373        tr_bencDictAddStr( d, key, st->scrapeResponse );
374    else if( !strcmp( key, "scrapeURL" ) )
375        tr_bencDictAddStr( d, key, st->scrapeURL );
376    else if( !strcmp( key, "seeders" ) )
377        tr_bencDictAddInt( d, key, st->seeders );
378    else if( !strcmp( key, "sizeWhenDone" ) )
379        tr_bencDictAddInt( d, key, st->sizeWhenDone );
380    else if( !strcmp( key, "startDate" ) )
381        tr_bencDictAddInt( d, key, st->startDate );
382    else if( !strcmp( key, "status" ) )
383        tr_bencDictAddInt( d, key, st->activity );
384    else if( !strcmp( key, "swarmSpeed" ) )
385        tr_bencDictAddInt( d, key, (int)( st->swarmSpeed * 1024 ) );
386    else if( !strcmp( key, "timesCompleted" ) )
387        tr_bencDictAddInt( d, key, st->timesCompleted );
388    else if( !strcmp( key, "trackers" ) )
389        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
390    else if( !strcmp( key, "totalSize" ) )
391        tr_bencDictAddInt( d, key, inf->totalSize );
392    else if( !strcmp( key, "uploadedEver" ) )
393        tr_bencDictAddInt( d, key, st->uploadedEver );
394    else if( !strcmp( key, "uploadLimitMode" ) )
395        tr_bencDictAddInt( d, key,   tr_torrentGetSpeedMode( tor, TR_UP ) );
396    else if( !strcmp( key, "uploadLimit" ) )
397        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_UP ) );
398    else if( !strcmp( key, "uploadRatio" ) )
399        tr_bencDictAddDouble( d, key,
400                             tr_getRatio( st->uploadedEver,
401                                          st->downloadedEver ) );
402    else if( !strcmp( key, "wanted" ) )
403    {
404        tr_file_index_t i;
405        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
406        for( i = 0; i < inf->fileCount; ++i )
407            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
408    }
409    else if( !strcmp( key, "webseeds" ) )
410        addWebseeds( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
411    else if( !strcmp( key, "webseedsSendingToUs" ) )
412        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
413}
414
415static void
416addInfo( const tr_torrent * tor,
417         tr_benc *          d,
418         tr_benc *          fields )
419{
420    int          i;
421    const int    n = tr_bencListSize( fields );
422    const char * str;
423
424    tr_bencInitDict( d, n );
425
426    for( i = 0; i < n; ++i )
427        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
428            addField( tor, d, str );
429}
430
431static const char*
432torrentGet( tr_handle * handle,
433            tr_benc *   args_in,
434            tr_benc *   args_out )
435{
436    int           i, torrentCount;
437    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
438    tr_benc *     list = tr_bencDictAddList( args_out, "torrents",
439                                             torrentCount );
440    tr_benc *     fields;
441    char *        msg = NULL;
442
443    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
444        msg = "no fields specified";
445    else for( i = 0; i < torrentCount; ++i )
446            addInfo( torrents[i], tr_bencListAdd( list ), fields );
447
448    tr_free( torrents );
449    return msg;
450}
451
452/***
453****
454***/
455
456static void
457setFilePriorities( tr_torrent * tor,
458                   int          priority,
459                   tr_benc *    list )
460{
461    int               i;
462    int64_t           tmp;
463    int               fileCount = 0;
464    const int         n = tr_bencListSize( list );
465    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
466
467    if( n )
468    {
469        for( i = 0; i < n; ++i )
470            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
471                if( 0 <= tmp && tmp < tor->info.fileCount )
472                    files[fileCount++] = tmp;
473    }
474    else /* if empty set, apply to all */
475    {
476        tr_file_index_t t;
477        for( t = 0; t < tor->info.fileCount; ++t )
478            files[fileCount++] = t;
479    }
480
481    if( fileCount )
482        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
483
484    tr_free( files );
485}
486
487static void
488setFileDLs( tr_torrent * tor,
489            int          do_download,
490            tr_benc *    list )
491{
492    int               i;
493    int64_t           tmp;
494    int               fileCount = 0;
495    const int         n = tr_bencListSize( list );
496    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
497
498    if( n ) /* if argument list, process them */
499    {
500        for( i = 0; i < n; ++i )
501            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
502                if( 0 <= tmp && tmp < tor->info.fileCount )
503                    files[fileCount++] = tmp;
504    }
505    else /* if empty set, apply to all */
506    {
507        tr_file_index_t t;
508        for( t = 0; t < tor->info.fileCount; ++t )
509            files[fileCount++] = t;
510    }
511
512    if( fileCount )
513        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
514
515    tr_free( files );
516}
517
518static const char*
519torrentSet( tr_handle *        h,
520            tr_benc *          args_in,
521            tr_benc * args_out UNUSED )
522{
523    int           i, torrentCount;
524    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
525
526    for( i = 0; i < torrentCount; ++i )
527    {
528        int64_t      tmp;
529        tr_benc *    files;
530        tr_torrent * tor = torrents[i];
531
532        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
533            setFileDLs( tor, FALSE, files );
534        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
535            setFileDLs( tor, TRUE, files );
536        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
537            tr_torrentSetPeerLimit( tor, tmp );
538        if( tr_bencDictFindList( args_in, "priority-high", &files ) )
539            setFilePriorities( tor, TR_PRI_HIGH, files );
540        if( tr_bencDictFindList( args_in, "priority-low", &files ) )
541            setFilePriorities( tor, TR_PRI_LOW, files );
542        if( tr_bencDictFindList( args_in, "priority-normal", &files ) )
543            setFilePriorities( tor, TR_PRI_NORMAL, files );
544        if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
545            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
546        if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &tmp ) )
547            tr_torrentSetSpeedMode(
548                tor, TR_DOWN, tmp ? TR_SPEEDLIMIT_SINGLE
549                : TR_SPEEDLIMIT_GLOBAL );
550        if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
551            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
552        if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &tmp ) )
553            tr_torrentSetSpeedMode( tor, TR_UP, tmp ? TR_SPEEDLIMIT_SINGLE
554                                    : TR_SPEEDLIMIT_GLOBAL );
555
556        notify( h, TR_RPC_TORRENT_CHANGED, tor );
557    }
558
559    tr_free( torrents );
560    return NULL;
561}
562
563/***
564****
565***/
566
567static const char*
568torrentAdd( tr_handle * h,
569            tr_benc *   args_in,
570            tr_benc *   args_out )
571{
572    const char * filename = NULL;
573    const char * metainfo_base64 = NULL;
574
575    tr_bencDictFindStr( args_in, "filename", &filename );
576    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
577    if( !filename && !metainfo_base64 )
578        return "no filename or metainfo specified";
579    else
580    {
581        int64_t      i;
582        int          err = 0;
583        const char * str;
584        tr_ctor *    ctor;
585        tr_torrent * tor;
586
587        ctor = tr_ctorNew( h );
588
589        /* set the metainfo */
590        if( filename )
591            tr_ctorSetMetainfoFromFile( ctor, filename );
592        else
593        {
594            int    len;
595            char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
596            tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
597            tr_free( metainfo );
598        }
599
600        /* set the optional arguments */
601        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
602            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
603        if( tr_bencDictFindInt( args_in, "paused", &i ) )
604            tr_ctorSetPaused( ctor, TR_FORCE, i );
605        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
606            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
607
608        tor = tr_torrentNew( h, ctor, &err );
609        tr_ctorFree( ctor );
610
611        if( tor )
612        {
613            tr_benc fields;
614            tr_bencInitList( &fields, 3 );
615            tr_bencListAddStr( &fields, "id" );
616            tr_bencListAddStr( &fields, "name" );
617            tr_bencListAddStr( &fields, "hashString" );
618            addInfo( tor, tr_bencDictAdd( args_out,
619                                          "torrent-added" ), &fields );
620            notify( h, TR_RPC_TORRENT_ADDED, tor );
621            tr_bencFree( &fields );
622        }
623        else if( err == TR_EDUPLICATE )
624        {
625            return "duplicate torrent";
626        }
627        else if( err == TR_EINVALID )
628        {
629            return "invalid or corrupt torrent file";
630        }
631    }
632
633    return NULL;
634}
635
636/***
637****
638***/
639
640static const char*
641sessionSet( tr_handle *        h,
642            tr_benc *          args_in,
643            tr_benc * args_out UNUSED )
644{
645    int64_t      i;
646    const char * str;
647
648    if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
649        tr_sessionSetDownloadDir( h, str );
650    if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
651        tr_sessionSetPeerLimit( h, i );
652    if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
653        tr_sessionSetPexEnabled( h, i );
654    if( tr_bencDictFindInt( args_in, "port", &i ) )
655        tr_sessionSetPeerPort( h, i );
656    if( tr_bencDictFindInt( args_in, "port-forwarding-enabled", &i ) )
657        tr_sessionSetPortForwardingEnabled( h, i );
658    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
659        tr_sessionSetSpeedLimit( h, TR_DOWN, i );
660    if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &i ) )
661        tr_sessionSetSpeedLimitEnabled( h, TR_DOWN, i );
662    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
663        tr_sessionSetSpeedLimit( h, TR_UP, i );
664    if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &i ) )
665        tr_sessionSetSpeedLimitEnabled( h, TR_UP, i );
666    if( tr_bencDictFindStr( args_in, "encryption", &str ) )
667    {
668        if( !strcmp( str, "required" ) )
669            tr_sessionSetEncryption( h, TR_ENCRYPTION_REQUIRED );
670        else if( !strcmp( str, "tolerated" ) )
671            tr_sessionSetEncryption( h, TR_CLEAR_PREFERRED );
672        else
673            tr_sessionSetEncryption( h, TR_ENCRYPTION_PREFERRED );
674    }
675
676    notify( h, TR_RPC_SESSION_CHANGED, NULL );
677
678    return NULL;
679}
680
681static const char*
682sessionStats( tr_handle *       h,
683              tr_benc * args_in UNUSED,
684              tr_benc *         args_out )
685{
686    tr_benc *    d = tr_bencDictAddDict( args_out, "session-stats", 10 );
687    tr_torrent * tor = NULL;
688    int          running = 0;
689    int          total = 0;
690
691    while( ( tor = tr_torrentNext( h, tor ) ) )
692    {
693        ++total;
694        if( tor->isRunning )
695            ++running;
696    }
697
698    tr_bencDictAddInt( d, "activeTorrentCount", running );
699    tr_bencDictAddInt( d, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( h, TR_DOWN ) * 1024 ) );
700    tr_bencDictAddInt( d, "pausedTorrentCount", total - running );
701    tr_bencDictAddInt( d, "torrentCount", total );
702    tr_bencDictAddInt( d, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( h, TR_UP ) * 1024 ) );
703    return NULL;
704}
705
706static const char*
707sessionGet( tr_handle *       h,
708            tr_benc * args_in UNUSED,
709            tr_benc *         args_out )
710{
711    const char * str;
712    tr_benc *    d = args_out;
713
714    tr_bencDictAddStr( d, "download-dir",
715                      tr_sessionGetDownloadDir( h ) );
716    tr_bencDictAddInt( d, "peer-limit",
717                      tr_sessionGetPeerLimit( h ) );
718    tr_bencDictAddInt( d, "pex-allowed",
719                      tr_sessionIsPexEnabled( h ) );
720    tr_bencDictAddInt( d, "port",
721                      tr_sessionGetPeerPort( h ) );
722    tr_bencDictAddInt( d, "port-forwarding-enabled",
723                      tr_sessionIsPortForwardingEnabled( h ) );
724    tr_bencDictAddInt( d, "speed-limit-up",
725                       tr_sessionGetSpeedLimit( h, TR_UP ) );
726    tr_bencDictAddInt( d, "speed-limit-up-enabled",
727                      tr_sessionIsSpeedLimitEnabled( h, TR_UP ) );
728    tr_bencDictAddInt( d, "speed-limit-down",
729                       tr_sessionGetSpeedLimit( h, TR_DOWN ) );
730    tr_bencDictAddInt( d, "speed-limit-down-enabled",
731                      tr_sessionIsSpeedLimitEnabled( h, TR_DOWN ) );
732    switch( tr_sessionGetEncryption( h ) )
733    {
734        case TR_CLEAR_PREFERRED:
735            str = "tolerated"; break;
736
737        case TR_ENCRYPTION_REQUIRED:
738            str = "required"; break;
739
740        default:
741            str = "preferred"; break;
742    }
743    tr_bencDictAddStr( d, "encryption", str );
744
745    return NULL;
746}
747
748/***
749****
750***/
751
752typedef const char* ( handler )( tr_handle*, tr_benc*, tr_benc* );
753
754static struct method
755{
756    const char *  name;
757    handler *     func;
758} methods[] = {
759    { "session-get",    sessionGet          },
760    { "session-set",    sessionSet          },
761    { "session-stats",  sessionStats        },
762    { "torrent-add",    torrentAdd          },
763    { "torrent-get",    torrentGet          },
764    { "torrent-remove", torrentRemove       },
765    { "torrent-set",    torrentSet          },
766    { "torrent-start",  torrentStart        },
767    { "torrent-stop",   torrentStop         },
768    { "torrent-verify", torrentVerify       }
769};
770
771static char*
772request_exec( struct tr_handle * handle,
773              tr_benc *          request,
774              int *              response_len )
775{
776    int64_t      i;
777    const char * str;
778    char *       out;
779    tr_benc      response;
780    tr_benc *    args_in = tr_bencDictFind( request, "arguments" );
781    tr_benc *    args_out = NULL;
782    const char * result = NULL;
783
784    /* build the response skeleton */
785    tr_bencInitDict( &response, 3 );
786    args_out = tr_bencDictAddDict( &response, "arguments", 0 );
787
788    /* parse the request */
789    if( !tr_bencDictFindStr( request, "method", &str ) )
790        result = "no method name";
791    else
792    {
793        const int n = TR_N_ELEMENTS( methods );
794        for( i = 0; i < n; ++i )
795            if( !strcmp( str, methods[i].name ) )
796                break;
797        result = i == n
798                 ? "method name not recognized"
799                 : ( *methods[i].func )( handle, args_in, args_out );
800    }
801
802    /* serialize & return the response */
803    if( !result )
804        result = "success";
805    tr_bencDictAddStr( &response, "result", result );
806    if( tr_bencDictFindInt( request, "tag", &i ) )
807        tr_bencDictAddInt( &response, "tag", i );
808    out = tr_bencSaveAsJSON( &response, response_len );
809    tr_bencFree( &response );
810    return out;
811}
812
813char*
814tr_rpc_request_exec_json( struct tr_handle * handle,
815                          const void *       request_json,
816                          int                request_len,
817                          int *              response_len )
818{
819    tr_benc top;
820    int     have_content;
821    char *  ret;
822
823    if( request_len < 0 )
824        request_len = strlen( request_json );
825
826    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
827    ret = request_exec( handle, have_content ? &top : NULL, response_len );
828
829    if( have_content )
830        tr_bencFree( &top );
831    return ret;
832}
833
834static void
835addToken( tr_benc *    list,
836          const char * token,
837          size_t       len )
838{
839    char *       p;
840    const char * end = token + len;
841    const long   a = strtol( token, &p, 10 );
842
843    if( p == end )
844        tr_bencListAddInt( list, a );
845    else if( *p == '-' && isdigit( p[1] ) )
846    {
847        const long b = strtol( p + 1, &p, 10 );
848        if( ( p == end ) && ( b > a ) )
849        {
850            long i;
851            for( i = a; i <= b; ++i )
852                tr_bencListAddInt( list, i );
853        }
854    }
855}
856
857/**
858 * Munge the URI into a usable form.
859 *
860 * We have very loose typing on this to make the URIs as simple as possible:
861 * - anything not a 'tag' or 'method' is automatically in 'arguments'
862 * - values that are all-digits are numbers
863 * - values that are all-digits or commas are number lists
864 * - all other values are strings
865 */
866void
867tr_rpc_parse_list_str( tr_benc *    setme,
868                       const char * str_in,
869                       size_t       len )
870
871{
872    char *       str = tr_strndup( str_in, len );
873    int          isNum;
874    int          isNumList;
875    int          commaCount;
876    const char * walk;
877
878    isNum = 1;
879    isNumList = 1;
880    commaCount = 0;
881    walk = str;
882    for( ; *walk && ( isNumList || isNum ); ++walk )
883    {
884        if( isNumList ) isNumList = *walk == '-' || isdigit( *walk )
885                                    || *walk == ',';
886        if( isNum     ) isNum     = *walk == '-' || isdigit( *walk );
887        if( *walk == ',' ) ++commaCount;
888    }
889
890    if( isNum )
891        tr_bencInitInt( setme, strtol( str, NULL, 10 ) );
892    else if( !isNumList )
893        tr_bencInitStr( setme, str, len );
894    else
895    {
896        tr_bencInitList( setme, commaCount + 1 );
897        walk = str;
898        while( *walk )
899        {
900            const char * p = strchr( walk, ',' );
901            if( !p )
902                p = walk + strlen( walk );
903            addToken( setme, walk, p - walk );
904            if( *p != ',' )
905                break;
906            walk = p + 1;
907        }
908    }
909
910    tr_free( str );
911}
912
913char*
914tr_rpc_request_exec_uri( struct tr_handle * handle,
915                         const void *       request_uri,
916                         int                request_len,
917                         int *              response_len )
918{
919    char *       ret = NULL;
920    tr_benc      top, * args;
921    char *       request = tr_strndup( request_uri, request_len );
922    const char * pch;
923
924    tr_bencInitDict( &top, 3 );
925    args = tr_bencDictAddDict( &top, "arguments", 0 );
926
927    pch = strchr( request, '?' );
928    if( !pch ) pch = request;
929    while( pch )
930    {
931        const char * delim = strchr( pch, '=' );
932        const char * next = strchr( pch, '&' );
933        if( delim )
934        {
935            char *    key = tr_strndup( pch, delim - pch );
936            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
937            tr_benc * parent = isArg ? args : &top;
938            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
939                                  delim + 1,
940                                  next ? (size_t)(
941                                       next -
942                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
943            tr_free( key );
944        }
945        pch = next ? next + 1 : NULL;
946    }
947
948    ret = request_exec( handle, &top, response_len );
949
950    /* cleanup */
951    tr_bencFree( &top );
952    tr_free( request );
953    return ret;
954}
955
Note: See TracBrowser for help on using the repository browser.