1/* 2 * This file Copyright (C) Mnemosyne LLC 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 13504 2012-09-19 05:11:19Z jordan $ 11 */ 12 13#include <assert.h> 14#include <ctype.h> /* isdigit */ 15#include <errno.h> 16#include <stdlib.h> /* strtol */ 17#include <string.h> /* strcmp */ 18#include <unistd.h> /* unlink */ 19 20#ifdef HAVE_ZLIB 21 #include <zlib.h> 22#endif 23 24#include <event2/buffer.h> 25 26#include "transmission.h" 27#include "bencode.h" 28#include "completion.h" 29#include "fdlimit.h" 30#include "json.h" 31#include "rpcimpl.h" 32#include "session.h" 33#include "torrent.h" 34#include "utils.h" 35#include "version.h" 36#include "web.h" 37 38#define RPC_VERSION 14 39#define RPC_VERSION_MIN 1 40 41#define RECENTLY_ACTIVE_SECONDS 60 42 43#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) ) 44 45#if 0 46#define dbgmsg(fmt, ...) \ 47 do { \ 48 fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \ 49 fprintf( stderr, "\n" ); \ 50 } while( 0 ) 51#else 52#define dbgmsg( ... ) \ 53 do { \ 54 if( tr_deepLoggingIsActive( ) ) \ 55 tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \ 56 } while( 0 ) 57#endif 58 59 60/*** 61**** 62***/ 63 64static tr_rpc_callback_status 65notify( tr_session * session, 66 int type, 67 tr_torrent * tor ) 68{ 69 tr_rpc_callback_status status = 0; 70 71 if( session->rpc_func ) 72 status = session->rpc_func( session, type, tor, 73 session->rpc_func_user_data ); 74 75 return status; 76} 77 78/*** 79**** 80***/ 81 82/* For functions that can't be immediately executed, like torrentAdd, 83 * this is the callback data used to pass a response to the caller 84 * when the task is complete */ 85struct tr_rpc_idle_data 86{ 87 tr_session * session; 88 tr_benc * response; 89 tr_benc * args_out; 90 tr_rpc_response_func callback; 91 void * callback_user_data; 92}; 93 94static void 95tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result ) 96{ 97 struct evbuffer * buf; 98 99 if( result == NULL ) 100 result = "success"; 101 tr_bencDictAddStr( data->response, "result", result ); 102 103 buf = tr_bencToBuf( data->response, TR_FMT_JSON_LEAN ); 104 (*data->callback)( data->session, buf, data->callback_user_data ); 105 evbuffer_free( buf ); 106 107 tr_bencFree( data->response ); 108 tr_free( data->response ); 109 tr_free( data ); 110} 111 112/*** 113**** 114***/ 115 116static tr_torrent ** 117getTorrents( tr_session * session, 118 tr_benc * args, 119 int * setmeCount ) 120{ 121 int torrentCount = 0; 122 int64_t id; 123 tr_torrent ** torrents = NULL; 124 tr_benc * ids; 125 const char * str; 126 127 if( tr_bencDictFindList( args, "ids", &ids ) ) 128 { 129 int i; 130 const int n = tr_bencListSize( ids ); 131 132 torrents = tr_new0( tr_torrent *, n ); 133 134 for( i = 0; i < n; ++i ) 135 { 136 tr_torrent * tor = NULL; 137 tr_benc * node = tr_bencListChild( ids, i ); 138 const char * str; 139 if( tr_bencGetInt( node, &id ) ) 140 tor = tr_torrentFindFromId( session, id ); 141 else if( tr_bencGetStr( node, &str ) ) 142 tor = tr_torrentFindFromHashString( session, str ); 143 if( tor ) 144 torrents[torrentCount++] = tor; 145 } 146 } 147 else if( tr_bencDictFindInt( args, "ids", &id ) 148 || tr_bencDictFindInt( args, "id", &id ) ) 149 { 150 tr_torrent * tor; 151 torrents = tr_new0( tr_torrent *, 1 ); 152 if( ( tor = tr_torrentFindFromId( session, id ) ) ) 153 torrents[torrentCount++] = tor; 154 } 155 else if( tr_bencDictFindStr( args, "ids", &str ) ) 156 { 157 if( !strcmp( str, "recently-active" ) ) 158 { 159 tr_torrent * tor = NULL; 160 const time_t now = tr_time( ); 161 const time_t window = RECENTLY_ACTIVE_SECONDS; 162 const int n = tr_sessionCountTorrents( session ); 163 torrents = tr_new0( tr_torrent *, n ); 164 while( ( tor = tr_torrentNext( session, tor ) ) ) 165 if( tor->anyDate >= now - window ) 166 torrents[torrentCount++] = tor; 167 } 168 else 169 { 170 tr_torrent * tor; 171 torrents = tr_new0( tr_torrent *, 1 ); 172 if(( tor = tr_torrentFindFromHashString( session, str ))) 173 torrents[torrentCount++] = tor; 174 } 175 } 176 else /* all of them */ 177 { 178 tr_torrent * tor = NULL; 179 const int n = tr_sessionCountTorrents( session ); 180 torrents = tr_new0( tr_torrent *, n ); 181 while( ( tor = tr_torrentNext( session, tor ) ) ) 182 torrents[torrentCount++] = tor; 183 } 184 185 *setmeCount = torrentCount; 186 return torrents; 187} 188 189static void 190notifyBatchQueueChange( tr_session * session, tr_torrent ** torrents, int n ) 191{ 192 int i; 193 for( i=0; i<n; ++i ) 194 notify( session, TR_RPC_TORRENT_CHANGED, torrents[i] ); 195 notify( session, TR_RPC_SESSION_QUEUE_POSITIONS_CHANGED, NULL ); 196} 197 198static const char* 199queueMoveTop( tr_session * session, 200 tr_benc * args_in, 201 tr_benc * args_out UNUSED, 202 struct tr_rpc_idle_data * idle_data UNUSED ) 203{ 204 int n; 205 tr_torrent ** torrents = getTorrents( session, args_in, &n ); 206 tr_torrentsQueueMoveTop( torrents, n ); 207 notifyBatchQueueChange( session, torrents, n ); 208 tr_free( torrents ); 209 return NULL; 210} 211 212static const char* 213queueMoveUp( tr_session * session, 214 tr_benc * args_in, 215 tr_benc * args_out UNUSED, 216 struct tr_rpc_idle_data * idle_data UNUSED ) 217{ 218 int n; 219 tr_torrent ** torrents = getTorrents( session, args_in, &n ); 220 tr_torrentsQueueMoveUp( torrents, n ); 221 notifyBatchQueueChange( session, torrents, n ); 222 tr_free( torrents ); 223 return NULL; 224} 225 226static const char* 227queueMoveDown( tr_session * session, 228 tr_benc * args_in, 229 tr_benc * args_out UNUSED, 230 struct tr_rpc_idle_data * idle_data UNUSED ) 231{ 232 int n; 233 tr_torrent ** torrents = getTorrents( session, args_in, &n ); 234 tr_torrentsQueueMoveDown( torrents, n ); 235 notifyBatchQueueChange( session, torrents, n ); 236 tr_free( torrents ); 237 return NULL; 238} 239 240static const char* 241queueMoveBottom( tr_session * session, 242 tr_benc * args_in, 243 tr_benc * args_out UNUSED, 244 struct tr_rpc_idle_data * idle_data UNUSED ) 245{ 246 int n; 247 tr_torrent ** torrents = getTorrents( session, args_in, &n ); 248 tr_torrentsQueueMoveBottom( torrents, n ); 249 notifyBatchQueueChange( session, torrents, n ); 250 tr_free( torrents ); 251 return NULL; 252} 253 254static const char* 255torrentStart( tr_session * session, 256 tr_benc * args_in, 257 tr_benc * args_out UNUSED, 258 struct tr_rpc_idle_data * idle_data UNUSED ) 259{ 260 int i, torrentCount; 261 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 262 263 assert( idle_data == NULL ); 264 265 for( i = 0; i < torrentCount; ++i ) 266 { 267 tr_torrent * tor = torrents[i]; 268 if( !tor->isRunning ) 269 { 270 tr_torrentStart( tor ); 271 notify( session, TR_RPC_TORRENT_STARTED, tor ); 272 } 273 } 274 tr_free( torrents ); 275 return NULL; 276} 277 278static const char* 279torrentStartNow( tr_session * session, 280 tr_benc * args_in, 281 tr_benc * args_out UNUSED, 282 struct tr_rpc_idle_data * idle_data UNUSED ) 283{ 284 int i, torrentCount; 285 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 286 287 assert( idle_data == NULL ); 288 289 for( i = 0; i < torrentCount; ++i ) 290 { 291 tr_torrent * tor = torrents[i]; 292 if( !tor->isRunning ) 293 { 294 tr_torrentStartNow( tor ); 295 notify( session, TR_RPC_TORRENT_STARTED, tor ); 296 } 297 } 298 tr_free( torrents ); 299 return NULL; 300} 301 302static const char* 303torrentStop( tr_session * session, 304 tr_benc * args_in, 305 tr_benc * args_out UNUSED, 306 struct tr_rpc_idle_data * idle_data UNUSED ) 307{ 308 int i, torrentCount; 309 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 310 311 assert( idle_data == NULL ); 312 313 for( i = 0; i < torrentCount; ++i ) 314 { 315 tr_torrent * tor = torrents[i]; 316 317 if( tor->isRunning || tr_torrentIsQueued( tor ) ) 318 { 319 tor->isStopping = true; 320 notify( session, TR_RPC_TORRENT_STOPPED, tor ); 321 } 322 } 323 tr_free( torrents ); 324 return NULL; 325} 326 327static const char* 328torrentRemove( tr_session * session, 329 tr_benc * args_in, 330 tr_benc * args_out UNUSED, 331 struct tr_rpc_idle_data * idle_data UNUSED ) 332{ 333 int i; 334 int torrentCount; 335 tr_rpc_callback_type type; 336 bool deleteFlag = false; 337 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 338 339 assert( idle_data == NULL ); 340 341 tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag ); 342 type = deleteFlag ? TR_RPC_TORRENT_TRASHING 343 : TR_RPC_TORRENT_REMOVING; 344 345 for( i=0; i<torrentCount; ++i ) 346 { 347 tr_torrent * tor = torrents[i]; 348 const tr_rpc_callback_status status = notify( session, type, tor ); 349 if( !( status & TR_RPC_NOREMOVE ) ) 350 tr_torrentRemove( tor, deleteFlag, NULL ); 351 } 352 353 tr_free( torrents ); 354 return NULL; 355} 356 357static const char* 358torrentReannounce( tr_session * session, 359 tr_benc * args_in, 360 tr_benc * args_out UNUSED, 361 struct tr_rpc_idle_data * idle_data UNUSED ) 362{ 363 int i, torrentCount; 364 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 365 366 assert( idle_data == NULL ); 367 368 for( i=0; i<torrentCount; ++i ) 369 { 370 tr_torrent * tor = torrents[i]; 371 if( tr_torrentCanManualUpdate( tor ) ) 372 { 373 tr_torrentManualUpdate( tor ); 374 notify( session, TR_RPC_TORRENT_CHANGED, tor ); 375 } 376 } 377 378 tr_free( torrents ); 379 return NULL; 380} 381 382static const char* 383torrentVerify( tr_session * session, 384 tr_benc * args_in, 385 tr_benc * args_out UNUSED, 386 struct tr_rpc_idle_data * idle_data UNUSED ) 387{ 388 int i, torrentCount; 389 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 390 391 assert( idle_data == NULL ); 392 393 for( i = 0; i < torrentCount; ++i ) 394 { 395 tr_torrent * tor = torrents[i]; 396 tr_torrentVerify( tor ); 397 notify( session, TR_RPC_TORRENT_CHANGED, tor ); 398 } 399 400 tr_free( torrents ); 401 return NULL; 402} 403 404/*** 405**** 406***/ 407 408static void 409addFileStats( const tr_torrent * tor, tr_benc * list ) 410{ 411 tr_file_index_t i; 412 tr_file_index_t n; 413 const tr_info * info = tr_torrentInfo( tor ); 414 tr_file_stat * files = tr_torrentFiles( tor, &n ); 415 416 for( i = 0; i < info->fileCount; ++i ) 417 { 418 const tr_file * file = &info->files[i]; 419 tr_benc * d = tr_bencListAddDict( list, 3 ); 420 tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted ); 421 tr_bencDictAddInt( d, "priority", file->priority ); 422 tr_bencDictAddBool( d, "wanted", !file->dnd ); 423 } 424 425 tr_torrentFilesFree( files, n ); 426} 427 428static void 429addFiles( const tr_torrent * tor, 430 tr_benc * list ) 431{ 432 tr_file_index_t i; 433 tr_file_index_t n; 434 const tr_info * info = tr_torrentInfo( tor ); 435 tr_file_stat * files = tr_torrentFiles( tor, &n ); 436 437 for( i = 0; i < info->fileCount; ++i ) 438 { 439 const tr_file * file = &info->files[i]; 440 tr_benc * d = tr_bencListAddDict( list, 3 ); 441 tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted ); 442 tr_bencDictAddInt( d, "length", file->length ); 443 tr_bencDictAddStr( d, "name", file->name ); 444 } 445 446 tr_torrentFilesFree( files, n ); 447} 448 449static void 450addWebseeds( const tr_info * info, 451 tr_benc * webseeds ) 452{ 453 int i; 454 455 for( i = 0; i < info->webseedCount; ++i ) 456 tr_bencListAddStr( webseeds, info->webseeds[i] ); 457} 458 459static void 460addTrackers( const tr_info * info, 461 tr_benc * trackers ) 462{ 463 int i; 464 465 for( i = 0; i < info->trackerCount; ++i ) 466 { 467 const tr_tracker_info * t = &info->trackers[i]; 468 tr_benc * d = tr_bencListAddDict( trackers, 4 ); 469 tr_bencDictAddStr( d, "announce", t->announce ); 470 tr_bencDictAddInt( d, "id", t->id ); 471 tr_bencDictAddStr( d, "scrape", t->scrape ); 472 tr_bencDictAddInt( d, "tier", t->tier ); 473 } 474} 475 476static void 477addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list ) 478{ 479 int i; 480 481 for( i=0; i<n; ++i ) 482 { 483 const tr_tracker_stat * s = &st[i]; 484 tr_benc * d = tr_bencListAddDict( list, 26 ); 485 tr_bencDictAddStr ( d, "announce", s->announce ); 486 tr_bencDictAddInt ( d, "announceState", s->announceState ); 487 tr_bencDictAddInt ( d, "downloadCount", s->downloadCount ); 488 tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced ); 489 tr_bencDictAddBool( d, "hasScraped", s->hasScraped ); 490 tr_bencDictAddStr ( d, "host", s->host ); 491 tr_bencDictAddInt ( d, "id", s->id ); 492 tr_bencDictAddBool( d, "isBackup", s->isBackup ); 493 tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount ); 494 tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult ); 495 tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime ); 496 tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded ); 497 tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime ); 498 tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut ); 499 tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult ); 500 tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime ); 501 tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded ); 502 tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime ); 503 tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut ); 504 tr_bencDictAddInt ( d, "leecherCount", s->leecherCount ); 505 tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime ); 506 tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime ); 507 tr_bencDictAddStr ( d, "scrape", s->scrape ); 508 tr_bencDictAddInt ( d, "scrapeState", s->scrapeState ); 509 tr_bencDictAddInt ( d, "seederCount", s->seederCount ); 510 tr_bencDictAddInt ( d, "tier", s->tier ); 511 } 512} 513 514static void 515addPeers( const tr_torrent * tor, 516 tr_benc * list ) 517{ 518 int i; 519 int peerCount; 520 tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount ); 521 522 tr_bencInitList( list, peerCount ); 523 524 for( i = 0; i < peerCount; ++i ) 525 { 526 tr_benc * d = tr_bencListAddDict( list, 16 ); 527 const tr_peer_stat * peer = peers + i; 528 tr_bencDictAddStr ( d, "address", peer->addr ); 529 tr_bencDictAddStr ( d, "clientName", peer->client ); 530 tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked ); 531 tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested ); 532 tr_bencDictAddStr ( d, "flagStr", peer->flagStr ); 533 tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom ); 534 tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted ); 535 tr_bencDictAddBool( d, "isIncoming", peer->isIncoming ); 536 tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo ); 537 tr_bencDictAddBool( d, "isUTP", peer->isUTP ); 538 tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked ); 539 tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested ); 540 tr_bencDictAddInt ( d, "port", peer->port ); 541 tr_bencDictAddReal( d, "progress", peer->progress ); 542 tr_bencDictAddInt ( d, "rateToClient", toSpeedBytes( peer->rateToClient_KBps ) ); 543 tr_bencDictAddInt ( d, "rateToPeer", toSpeedBytes( peer->rateToPeer_KBps ) ); 544 } 545 546 tr_torrentPeersFree( peers, peerCount ); 547} 548 549/* faster-than-strcmp() optimization. This is kind of clumsy, 550 but addField() was in the profiler's top 10 list, and this 551 makes it 4x faster... */ 552#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen)) 553 554static void 555addField( const tr_torrent * const tor, 556 const tr_info * const inf, 557 const tr_stat * const st, 558 tr_benc * const d, 559 const char * const key ) 560{ 561 const size_t keylen = strlen( key ); 562 563 if( tr_streq( key, keylen, "activityDate" ) ) 564 tr_bencDictAddInt( d, key, st->activityDate ); 565 else if( tr_streq( key, keylen, "addedDate" ) ) 566 tr_bencDictAddInt( d, key, st->addedDate ); 567 else if( tr_streq( key, keylen, "bandwidthPriority" ) ) 568 tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) ); 569 else if( tr_streq( key, keylen, "comment" ) ) 570 tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" ); 571 else if( tr_streq( key, keylen, "corruptEver" ) ) 572 tr_bencDictAddInt( d, key, st->corruptEver ); 573 else if( tr_streq( key, keylen, "creator" ) ) 574 tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" ); 575 else if( tr_streq( key, keylen, "dateCreated" ) ) 576 tr_bencDictAddInt( d, key, inf->dateCreated ); 577 else if( tr_streq( key, keylen, "desiredAvailable" ) ) 578 tr_bencDictAddInt( d, key, st->desiredAvailable ); 579 else if( tr_streq( key, keylen, "doneDate" ) ) 580 tr_bencDictAddInt( d, key, st->doneDate ); 581 else if( tr_streq( key, keylen, "downloadDir" ) ) 582 tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) ); 583 else if( tr_streq( key, keylen, "downloadedEver" ) ) 584 tr_bencDictAddInt( d, key, st->downloadedEver ); 585 else if( tr_streq( key, keylen, "downloadLimit" ) ) 586 tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) ); 587 else if( tr_streq( key, keylen, "downloadLimited" ) ) 588 tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) ); 589 else if( tr_streq( key, keylen, "error" ) ) 590 tr_bencDictAddInt( d, key, st->error ); 591 else if( tr_streq( key, keylen, "errorString" ) ) 592 tr_bencDictAddStr( d, key, st->errorString ); 593 else if( tr_streq( key, keylen, "eta" ) ) 594 tr_bencDictAddInt( d, key, st->eta ); 595 else if( tr_streq( key, keylen, "files" ) ) 596 addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) ); 597 else if( tr_streq( key, keylen, "fileStats" ) ) 598 addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) ); 599 else if( tr_streq( key, keylen, "hashString" ) ) 600 tr_bencDictAddStr( d, key, tor->info.hashString ); 601 else if( tr_streq( key, keylen, "haveUnchecked" ) ) 602 tr_bencDictAddInt( d, key, st->haveUnchecked ); 603 else if( tr_streq( key, keylen, "haveValid" ) ) 604 tr_bencDictAddInt( d, key, st->haveValid ); 605 else if( tr_streq( key, keylen, "honorsSessionLimits" ) ) 606 tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) ); 607 else if( tr_streq( key, keylen, "id" ) ) 608 tr_bencDictAddInt( d, key, st->id ); 609 else if( tr_streq( key, keylen, "isFinished" ) ) 610 tr_bencDictAddBool( d, key, st->finished ); 611 else if( tr_streq( key, keylen, "isPrivate" ) ) 612 tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) ); 613 else if( tr_streq( key, keylen, "isStalled" ) ) 614 tr_bencDictAddBool( d, key, st->isStalled ); 615 else if( tr_streq( key, keylen, "leftUntilDone" ) ) 616 tr_bencDictAddInt( d, key, st->leftUntilDone ); 617 else if( tr_streq( key, keylen, "manualAnnounceTime" ) ) 618 tr_bencDictAddInt( d, key, st->manualAnnounceTime ); 619 else if( tr_streq( key, keylen, "maxConnectedPeers" ) ) 620 tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) ); 621 else if( tr_streq( key, keylen, "magnetLink" ) ) { 622 char * str = tr_torrentGetMagnetLink( tor ); 623 tr_bencDictAddStr( d, key, str ); 624 tr_free( str ); 625 } 626 else if( tr_streq( key, keylen, "metadataPercentComplete" ) ) 627 tr_bencDictAddReal( d, key, st->metadataPercentComplete ); 628 else if( tr_streq( key, keylen, "name" ) ) 629 tr_bencDictAddStr( d, key, tr_torrentName( tor ) ); 630 else if( tr_streq( key, keylen, "percentDone" ) ) 631 tr_bencDictAddReal( d, key, st->percentDone ); 632 else if( tr_streq( key, keylen, "peer-limit" ) ) 633 tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) ); 634 else if( tr_streq( key, keylen, "peers" ) ) 635 addPeers( tor, tr_bencDictAdd( d, key ) ); 636 else if( tr_streq( key, keylen, "peersConnected" ) ) 637 tr_bencDictAddInt( d, key, st->peersConnected ); 638 else if( tr_streq( key, keylen, "peersFrom" ) ) 639 { 640 tr_benc * tmp = tr_bencDictAddDict( d, key, 7 ); 641 const int * f = st->peersFrom; 642 tr_bencDictAddInt( tmp, "fromCache", f[TR_PEER_FROM_RESUME] ); 643 tr_bencDictAddInt( tmp, "fromDht", f[TR_PEER_FROM_DHT] ); 644 tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] ); 645 tr_bencDictAddInt( tmp, "fromLpd", f[TR_PEER_FROM_LPD] ); 646 tr_bencDictAddInt( tmp, "fromLtep", f[TR_PEER_FROM_LTEP] ); 647 tr_bencDictAddInt( tmp, "fromPex", f[TR_PEER_FROM_PEX] ); 648 tr_bencDictAddInt( tmp, "fromTracker", f[TR_PEER_FROM_TRACKER] ); 649 } 650 else if( tr_streq( key, keylen, "peersGettingFromUs" ) ) 651 tr_bencDictAddInt( d, key, st->peersGettingFromUs ); 652 else if( tr_streq( key, keylen, "peersSendingToUs" ) ) 653 tr_bencDictAddInt( d, key, st->peersSendingToUs ); 654 else if( tr_streq( key, keylen, "pieces" ) ) { 655 if (tr_torrentHasMetadata( tor ) ) { 656 size_t byte_count = 0; 657 void * bytes = tr_cpCreatePieceBitfield( &tor->completion, &byte_count ); 658 char * str = tr_base64_encode( bytes, byte_count, NULL ); 659 tr_bencDictAddStr( d, key, str!=NULL ? str : "" ); 660 tr_free( str ); 661 tr_free( bytes ); 662 } else { 663 tr_bencDictAddStr( d, key, "" ); 664 } 665 } 666 else if( tr_streq( key, keylen, "pieceCount" ) ) 667 tr_bencDictAddInt( d, key, inf->pieceCount ); 668 else if( tr_streq( key, keylen, "pieceSize" ) ) 669 tr_bencDictAddInt( d, key, inf->pieceSize ); 670 else if( tr_streq( key, keylen, "priorities" ) ) 671 { 672 tr_file_index_t i; 673 tr_benc * p = tr_bencDictAddList( d, key, inf->fileCount ); 674 for( i = 0; i < inf->fileCount; ++i ) 675 tr_bencListAddInt( p, inf->files[i].priority ); 676 } 677 else if( tr_streq( key, keylen, "queuePosition" ) ) 678 tr_bencDictAddInt( d, key, st->queuePosition ); 679 else if( tr_streq( key, keylen, "rateDownload" ) ) 680 tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) ); 681 else if( tr_streq( key, keylen, "rateUpload" ) ) 682 tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) ); 683 else if( tr_streq( key, keylen, "recheckProgress" ) ) 684 tr_bencDictAddReal( d, key, st->recheckProgress ); 685 else if( tr_streq( key, keylen, "seedIdleLimit" ) ) 686 tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) ); 687 else if( tr_streq( key, keylen, "seedIdleMode" ) ) 688 tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) ); 689 else if( tr_streq( key, keylen, "seedRatioLimit" ) ) 690 tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) ); 691 else if( tr_streq( key, keylen, "seedRatioMode" ) ) 692 tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) ); 693 else if( tr_streq( key, keylen, "sizeWhenDone" ) ) 694 tr_bencDictAddInt( d, key, st->sizeWhenDone ); 695 else if( tr_streq( key, keylen, "startDate" ) ) 696 tr_bencDictAddInt( d, key, st->startDate ); 697 else if( tr_streq( key, keylen, "status" ) ) 698 tr_bencDictAddInt( d, key, st->activity ); 699 else if( tr_streq( key, keylen, "secondsDownloading" ) ) 700 tr_bencDictAddInt( d, key, st->secondsDownloading ); 701 else if( tr_streq( key, keylen, "secondsSeeding" ) ) 702 tr_bencDictAddInt( d, key, st->secondsSeeding ); 703 else if( tr_streq( key, keylen, "trackers" ) ) 704 addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) ); 705 else if( tr_streq( key, keylen, "trackerStats" ) ) { 706 int n; 707 tr_tracker_stat * s = tr_torrentTrackers( tor, &n ); 708 addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) ); 709 tr_torrentTrackersFree( s, n ); 710 } 711 else if( tr_streq( key, keylen, "torrentFile" ) ) 712 tr_bencDictAddStr( d, key, inf->torrent ); 713 else if( tr_streq( key, keylen, "totalSize" ) ) 714 tr_bencDictAddInt( d, key, inf->totalSize ); 715 else if( tr_streq( key, keylen, "uploadedEver" ) ) 716 tr_bencDictAddInt( d, key, st->uploadedEver ); 717 else if( tr_streq( key, keylen, "uploadLimit" ) ) 718 tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) ); 719 else if( tr_streq( key, keylen, "uploadLimited" ) ) 720 tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) ); 721 else if( tr_streq( key, keylen, "uploadRatio" ) ) 722 tr_bencDictAddReal( d, key, st->ratio ); 723 else if( tr_streq( key, keylen, "wanted" ) ) 724 { 725 tr_file_index_t i; 726 tr_benc * w = tr_bencDictAddList( d, key, inf->fileCount ); 727 for( i = 0; i < inf->fileCount; ++i ) 728 tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 ); 729 } 730 else if( tr_streq( key, keylen, "webseeds" ) ) 731 addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) ); 732 else if( tr_streq( key, keylen, "webseedsSendingToUs" ) ) 733 tr_bencDictAddInt( d, key, st->webseedsSendingToUs ); 734} 735 736static void 737addInfo( const tr_torrent * tor, tr_benc * d, tr_benc * fields ) 738{ 739 const char * str; 740 const int n = tr_bencListSize( fields ); 741 742 tr_bencInitDict( d, n ); 743 744 if( n > 0 ) 745 { 746 int i; 747 const tr_info const * inf = tr_torrentInfo( tor ); 748 const tr_stat const * st = tr_torrentStat( (tr_torrent*)tor ); 749 750 for( i=0; i<n; ++i ) 751 if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) ) 752 addField( tor, inf, st, d, str ); 753 } 754} 755 756static const char* 757torrentGet( tr_session * session, 758 tr_benc * args_in, 759 tr_benc * args_out, 760 struct tr_rpc_idle_data * idle_data UNUSED ) 761{ 762 int i, torrentCount; 763 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 764 tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount ); 765 tr_benc * fields; 766 const char * msg = NULL; 767 const char * strVal; 768 769 assert( idle_data == NULL ); 770 771 if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) { 772 int n = 0; 773 tr_benc * d; 774 const time_t now = tr_time( ); 775 const int interval = RECENTLY_ACTIVE_SECONDS; 776 tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 ); 777 while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) { 778 int64_t intVal; 779 if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) { 780 tr_bencDictFindInt( d, "id", &intVal ); 781 tr_bencListAddInt( removed_out, intVal ); 782 } 783 } 784 } 785 786 if( !tr_bencDictFindList( args_in, "fields", &fields ) ) 787 msg = "no fields specified"; 788 else for( i = 0; i < torrentCount; ++i ) 789 addInfo( torrents[i], tr_bencListAdd( list ), fields ); 790 791 tr_free( torrents ); 792 return msg; 793} 794 795/*** 796**** 797***/ 798 799static const char* 800setFilePriorities( tr_torrent * tor, 801 int priority, 802 tr_benc * list ) 803{ 804 int i; 805 int64_t tmp; 806 int fileCount = 0; 807 const int n = tr_bencListSize( list ); 808 const char * errmsg = NULL; 809 tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount ); 810 811 if( n ) 812 { 813 for( i = 0; i < n; ++i ) { 814 if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) { 815 if( 0 <= tmp && tmp < tor->info.fileCount ) { 816 files[fileCount++] = tmp; 817 } else { 818 errmsg = "file index out of range"; 819 } 820 } 821 } 822 } 823 else /* if empty set, apply to all */ 824 { 825 tr_file_index_t t; 826 for( t = 0; t < tor->info.fileCount; ++t ) 827 files[fileCount++] = t; 828 } 829 830 if( fileCount ) 831 tr_torrentSetFilePriorities( tor, files, fileCount, priority ); 832 833 tr_free( files ); 834 return errmsg; 835} 836 837static const char* 838setFileDLs( tr_torrent * tor, 839 int do_download, 840 tr_benc * list ) 841{ 842 int i; 843 int64_t tmp; 844 int fileCount = 0; 845 const int n = tr_bencListSize( list ); 846 const char * errmsg = NULL; 847 tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount ); 848 849 if( n ) /* if argument list, process them */ 850 { 851 for( i = 0; i < n; ++i ) { 852 if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) { 853 if( 0 <= tmp && tmp < tor->info.fileCount ) { 854 files[fileCount++] = tmp; 855 } else { 856 errmsg = "file index out of range"; 857 } 858 } 859 } 860 } 861 else /* if empty set, apply to all */ 862 { 863 tr_file_index_t t; 864 for( t = 0; t < tor->info.fileCount; ++t ) 865 files[fileCount++] = t; 866 } 867 868 if( fileCount ) 869 tr_torrentSetFileDLs( tor, files, fileCount, do_download ); 870 871 tr_free( files ); 872 return errmsg; 873} 874 875static bool 876findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos ) 877{ 878 int i; 879 bool found = false; 880 881 for( i=0; i<n; ++i ) 882 { 883 if( !strcmp( t[i].announce, url ) ) 884 { 885 found = true; 886 if( pos ) *pos = i; 887 break; 888 } 889 } 890 891 return found; 892} 893 894static int 895copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n ) 896{ 897 int i; 898 int maxTier = -1; 899 900 for( i=0; i<n; ++i ) 901 { 902 tgt[i].tier = src[i].tier; 903 tgt[i].announce = tr_strdup( src[i].announce ); 904 maxTier = MAX( maxTier, src[i].tier ); 905 } 906 907 return maxTier; 908} 909 910static void 911freeTrackers( tr_tracker_info * trackers, int n ) 912{ 913 int i; 914 915 for( i=0; i<n; ++i ) 916 tr_free( trackers[i].announce ); 917 918 tr_free( trackers ); 919} 920 921static const char* 922addTrackerUrls( tr_torrent * tor, tr_benc * urls ) 923{ 924 int i; 925 int n; 926 int tier; 927 tr_benc * val; 928 tr_tracker_info * trackers; 929 bool changed = false; 930 const tr_info * inf = tr_torrentInfo( tor ); 931 const char * errmsg = NULL; 932 933 /* make a working copy of the existing announce list */ 934 n = inf->trackerCount; 935 trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) ); 936 tier = copyTrackers( trackers, inf->trackers, n ); 937 938 /* and add the new ones */ 939 i = 0; 940 while(( val = tr_bencListChild( urls, i++ ) )) 941 { 942 const char * announce = NULL; 943 944 if( tr_bencGetStr( val, &announce ) 945 && tr_urlIsValidTracker( announce ) 946 && !findAnnounceUrl( trackers, n, announce, NULL ) ) 947 { 948 trackers[n].tier = ++tier; /* add a new tier */ 949 trackers[n].announce = tr_strdup( announce ); 950 ++n; 951 changed = true; 952 } 953 } 954 955 if( !changed ) 956 errmsg = "invalid argument"; 957 else if( !tr_torrentSetAnnounceList( tor, trackers, n ) ) 958 errmsg = "error setting announce list"; 959 960 freeTrackers( trackers, n ); 961 return errmsg; 962} 963 964static const char* 965replaceTrackers( tr_torrent * tor, tr_benc * urls ) 966{ 967 int i; 968 tr_benc * pair[2]; 969 tr_tracker_info * trackers; 970 bool changed = false; 971 const tr_info * inf = tr_torrentInfo( tor ); 972 const int n = inf->trackerCount; 973 const char * errmsg = NULL; 974 975 /* make a working copy of the existing announce list */ 976 trackers = tr_new0( tr_tracker_info, n ); 977 copyTrackers( trackers, inf->trackers, n ); 978 979 /* make the substitutions... */ 980 i = 0; 981 while(((pair[0] = tr_bencListChild(urls,i))) && 982 ((pair[1] = tr_bencListChild(urls,i+1)))) 983 { 984 int64_t pos; 985 const char * newval; 986 987 if( tr_bencGetInt( pair[0], &pos ) 988 && tr_bencGetStr( pair[1], &newval ) 989 && tr_urlIsValidTracker( newval ) 990 && pos < n 991 && pos >= 0 ) 992 { 993 tr_free( trackers[pos].announce ); 994 trackers[pos].announce = tr_strdup( newval ); 995 changed = true; 996 } 997 998 i += 2; 999 } 1000 1001 if( !changed ) 1002 errmsg = "invalid argument"; 1003 else if( !tr_torrentSetAnnounceList( tor, trackers, n ) ) 1004 errmsg = "error setting announce list"; 1005 1006 freeTrackers( trackers, n ); 1007 return errmsg; 1008} 1009 1010static const char* 1011removeTrackers( tr_torrent * tor, tr_benc * ids ) 1012{ 1013 int i; 1014 int n; 1015 int t = 0; 1016 int dup = -1; 1017 int * tids; 1018 tr_benc * val; 1019 tr_tracker_info * trackers; 1020 bool changed = false; 1021 const tr_info * inf = tr_torrentInfo( tor ); 1022 const char * errmsg = NULL; 1023 1024 /* make a working copy of the existing announce list */ 1025 n = inf->trackerCount; 1026 tids = tr_new0( int, n ); 1027 trackers = tr_new0( tr_tracker_info, n ); 1028 copyTrackers( trackers, inf->trackers, n ); 1029 1030 /* remove the ones specified in the urls list */ 1031 i = 0; 1032 while(( val = tr_bencListChild( ids, i++ ))) 1033 { 1034 int64_t pos; 1035 1036 if( tr_bencGetInt( val, &pos ) 1037 && pos < n 1038 && pos >= 0 ) 1039 tids[t++] = pos; 1040 } 1041 1042 /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */ 1043 qsort( tids, t, sizeof(int), compareInt ); 1044 while( t-- ) 1045 { 1046 /* check for duplicates */ 1047 if( tids[t] == dup ) 1048 continue; 1049 tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- ); 1050 dup = tids[t]; 1051 changed = true; 1052 } 1053 1054 if( !changed ) 1055 errmsg = "invalid argument"; 1056 else if( !tr_torrentSetAnnounceList( tor, trackers, n ) ) 1057 errmsg = "error setting announce list"; 1058 1059 freeTrackers( trackers, n ); 1060 tr_free( tids ); 1061 return errmsg; 1062} 1063 1064static const char* 1065torrentSet( tr_session * session, 1066 tr_benc * args_in, 1067 tr_benc * args_out UNUSED, 1068 struct tr_rpc_idle_data * idle_data UNUSED ) 1069{ 1070 const char * errmsg = NULL; 1071 int i, torrentCount; 1072 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 1073 1074 assert( idle_data == NULL ); 1075 1076 for( i = 0; i < torrentCount; ++i ) 1077 { 1078 int64_t tmp; 1079 double d; 1080 tr_benc * files; 1081 tr_benc * trackers; 1082 bool boolVal; 1083 tr_torrent * tor = torrents[i]; 1084 1085 if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) ) 1086 if( tr_isPriority( tmp ) ) 1087 tr_torrentSetPriority( tor, tmp ); 1088 if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) ) 1089 errmsg = setFileDLs( tor, false, files ); 1090 if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) ) 1091 errmsg = setFileDLs( tor, true, files ); 1092 if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) ) 1093 tr_torrentSetPeerLimit( tor, tmp ); 1094 if( !errmsg && tr_bencDictFindList( args_in, "priority-high", &files ) ) 1095 errmsg = setFilePriorities( tor, TR_PRI_HIGH, files ); 1096 if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) ) 1097 errmsg = setFilePriorities( tor, TR_PRI_LOW, files ); 1098 if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) ) 1099 errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files ); 1100 if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) ) 1101 tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp ); 1102 if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) ) 1103 tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal ); 1104 if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) ) 1105 tr_torrentUseSessionLimits( tor, boolVal ); 1106 if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) ) 1107 tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp ); 1108 if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) ) 1109 tr_torrentUseSpeedLimit( tor, TR_UP, boolVal ); 1110 if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) ) 1111 tr_torrentSetIdleLimit( tor, tmp ); 1112 if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) ) 1113 tr_torrentSetIdleMode( tor, tmp ); 1114 if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) ) 1115 tr_torrentSetRatioLimit( tor, d ); 1116 if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) ) 1117 tr_torrentSetRatioMode( tor, tmp ); 1118 if( tr_bencDictFindInt( args_in, "queuePosition", &tmp ) ) 1119 tr_torrentSetQueuePosition( tor, tmp ); 1120 if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) ) 1121 errmsg = addTrackerUrls( tor, trackers ); 1122 if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) ) 1123 errmsg = removeTrackers( tor, trackers ); 1124 if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) ) 1125 errmsg = replaceTrackers( tor, trackers ); 1126 notify( session, TR_RPC_TORRENT_CHANGED, tor ); 1127 } 1128 1129 tr_free( torrents ); 1130 return errmsg; 1131} 1132 1133static const char* 1134torrentSetLocation( tr_session * session, 1135 tr_benc * args_in, 1136 tr_benc * args_out UNUSED, 1137 struct tr_rpc_idle_data * idle_data UNUSED ) 1138{ 1139 const char * errmsg = NULL; 1140 const char * location = NULL; 1141 1142 assert( idle_data == NULL ); 1143 1144 if( !tr_bencDictFindStr( args_in, "location", &location ) ) 1145 { 1146 errmsg = "no location"; 1147 } 1148 else 1149 { 1150 bool move = false; 1151 int i, torrentCount; 1152 tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount ); 1153 1154 tr_bencDictFindBool( args_in, "move", &move ); 1155 1156 for( i=0; i<torrentCount; ++i ) 1157 { 1158 tr_torrent * tor = torrents[i]; 1159 tr_torrentSetLocation( tor, location, move, NULL, NULL ); 1160 notify( session, TR_RPC_TORRENT_MOVED, tor ); 1161 } 1162 1163 tr_free( torrents ); 1164 } 1165 1166 return errmsg; 1167} 1168 1169/*** 1170**** 1171***/ 1172 1173static void 1174portTested( tr_session * session UNUSED, 1175 bool did_connect UNUSED, 1176 bool did_timeout UNUSED, 1177 long response_code, 1178 const void * response, 1179 size_t response_byte_count, 1180 void * user_data ) 1181{ 1182 char result[1024]; 1183 struct tr_rpc_idle_data * data = user_data; 1184 1185 if( response_code != 200 ) 1186 { 1187 tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s", 1188 response_code, tr_webGetResponseStr( response_code ) ); 1189 } 1190 else /* success */ 1191 { 1192 const bool isOpen = response_byte_count && *(char*)response == '1'; 1193 tr_bencDictAddBool( data->args_out, "port-is-open", isOpen ); 1194 tr_snprintf( result, sizeof( result ), "success" ); 1195 } 1196 1197 tr_idle_function_done( data, result ); 1198} 1199 1200static const char* 1201portTest( tr_session * session, 1202 tr_benc * args_in UNUSED, 1203 tr_benc * args_out UNUSED, 1204 struct tr_rpc_idle_data * idle_data ) 1205{ 1206 const int port = tr_sessionGetPeerPort( session ); 1207 char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port ); 1208 tr_webRun( session, url, NULL, NULL, portTested, idle_data ); 1209 tr_free( url ); 1210 return NULL; 1211} 1212 1213/*** 1214**** 1215***/ 1216 1217static void 1218gotNewBlocklist( tr_session * session, 1219 bool did_connect UNUSED, 1220 bool did_timeout UNUSED, 1221 long response_code, 1222 const void * response, 1223 size_t response_byte_count, 1224 void * user_data ) 1225{ 1226 char result[1024]; 1227 struct tr_rpc_idle_data * data = user_data; 1228 1229 *result = '\0'; 1230 1231 if( response_code != 200 ) 1232 { 1233 tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s", 1234 response_code, tr_webGetResponseStr( response_code ) ); 1235 } 1236 else /* successfully fetched the blocklist... */ 1237 { 1238 int fd; 1239 int err; 1240 char * filename; 1241 z_stream stream; 1242 const char * configDir = tr_sessionGetConfigDir( session ); 1243 const size_t buflen = 1024 * 128; /* 128 KiB buffer */ 1244 uint8_t * buf = tr_valloc( buflen ); 1245 1246 /* this is an odd Magic Number required by zlib to enable gz support. 1247 See zlib's inflateInit2() documentation for a full description */ 1248 const int windowBits = 15 + 32; 1249 1250 stream.zalloc = (alloc_func) Z_NULL; 1251 stream.zfree = (free_func) Z_NULL; 1252 stream.opaque = (voidpf) Z_NULL; 1253 stream.next_in = (void*) response; 1254 stream.avail_in = response_byte_count; 1255 inflateInit2( &stream, windowBits ); 1256 1257 filename = tr_buildPath( configDir, "blocklist.tmp", NULL ); 1258 fd = tr_open_file_for_writing( filename ); 1259 if( fd < 0 ) 1260 tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) ); 1261 1262 for( ;; ) 1263 { 1264 stream.next_out = (void*) buf; 1265 stream.avail_out = buflen; 1266 err = inflate( &stream, Z_NO_FLUSH ); 1267 1268 if( stream.avail_out < buflen ) { 1269 const int e = write( fd, buf, buflen - stream.avail_out ); 1270 if( e < 0 ) { 1271 tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) ); 1272 break; 1273 } 1274 } 1275 1276 if( err != Z_OK ) { 1277 if( ( err != Z_STREAM_END ) && ( err != Z_DATA_ERROR ) ) 1278 tr_snprintf( result, sizeof( result ), _( "Error uncompressing blocklist: %s (%d)" ), zError( err ), err ); 1279 break; 1280 } 1281 } 1282 1283 inflateEnd( &stream ); 1284 1285 if( err == Z_DATA_ERROR ) /* couldn't inflate it... it's probably already uncompressed */ 1286 if( write( fd, response, response_byte_count ) < 0 ) 1287 tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) ); 1288 1289 if( *result ) 1290 tr_err( "%s", result ); 1291 else { 1292 /* feed it to the session and give the client a response */ 1293 const int rule_count = tr_blocklistSetContent( session, filename ); 1294 tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count ); 1295 tr_snprintf( result, sizeof( result ), "success" ); 1296 } 1297 1298 unlink( filename ); 1299 tr_free( filename ); 1300 tr_free( buf ); 1301 } 1302 1303 tr_idle_function_done( data, result ); 1304} 1305 1306static const char* 1307blocklistUpdate( tr_session * session, 1308 tr_benc * args_in UNUSED, 1309 tr_benc * args_out UNUSED, 1310 struct tr_rpc_idle_data * idle_data ) 1311{ 1312 tr_webRun( session, session->blocklist_url, NULL, NULL, gotNewBlocklist, idle_data ); 1313 return NULL; 1314} 1315 1316/*** 1317**** 1318***/ 1319 1320static void 1321addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor ) 1322{ 1323 int err = 0; 1324 const char * result = NULL; 1325 tr_torrent * tor = tr_torrentNew( ctor, &err ); 1326 1327 tr_ctorFree( ctor ); 1328 1329 if( tor ) 1330 { 1331 tr_benc fields; 1332 tr_bencInitList( &fields, 3 ); 1333 tr_bencListAddStr( &fields, "id" ); 1334 tr_bencListAddStr( &fields, "name" ); 1335 tr_bencListAddStr( &fields, "hashString" ); 1336 addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields ); 1337 notify( data->session, TR_RPC_TORRENT_ADDED, tor ); 1338 tr_bencFree( &fields ); 1339 } 1340 else if( err == TR_PARSE_DUPLICATE ) 1341 { 1342 result = "duplicate torrent"; 1343 } 1344 else if( err == TR_PARSE_ERR ) 1345 { 1346 result = "invalid or corrupt torrent file"; 1347 } 1348 1349 tr_idle_function_done( data, result ); 1350} 1351 1352 1353struct add_torrent_idle_data 1354{ 1355 struct tr_rpc_idle_data * data; 1356 tr_ctor * ctor; 1357}; 1358 1359static void 1360gotMetadataFromURL( tr_session * session UNUSED, 1361 bool did_connect UNUSED, 1362 bool did_timeout UNUSED, 1363 long response_code, 1364 const void * response, 1365 size_t response_byte_count, 1366 void * user_data ) 1367{ 1368 struct add_torrent_idle_data * data = user_data; 1369 1370 dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes", 1371 response_code, tr_webGetResponseStr( response_code ), response_byte_count ); 1372 1373 if( response_code==200 || response_code==221 ) /* http or ftp success.. */ 1374 { 1375 tr_ctorSetMetainfo( data->ctor, response, response_byte_count ); 1376 addTorrentImpl( data->data, data->ctor ); 1377 } 1378 else 1379 { 1380 char result[1024]; 1381 tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s", 1382 response_code, tr_webGetResponseStr( response_code ) ); 1383 tr_idle_function_done( data->data, result ); 1384 } 1385 1386 tr_free( data ); 1387} 1388 1389static bool 1390isCurlURL( const char * filename ) 1391{ 1392 if( filename == NULL ) 1393 return false; 1394 1395 return !strncmp( filename, "ftp://", 6 ) || 1396 !strncmp( filename, "http://", 7 ) || 1397 !strncmp( filename, "https://", 8 ); 1398} 1399 1400static tr_file_index_t* 1401fileListFromList( tr_benc * list, tr_file_index_t * setmeCount ) 1402{ 1403 size_t i; 1404 const size_t childCount = tr_bencListSize( list ); 1405 tr_file_index_t n = 0; 1406 tr_file_index_t * files = tr_new0( tr_file_index_t, childCount ); 1407 1408 for( i=0; i<childCount; ++i ) { 1409 int64_t intVal; 1410 if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) ) 1411 files[n++] = (tr_file_index_t)intVal; 1412 } 1413 1414 *setmeCount = n; 1415 return files; 1416} 1417 1418static const char* 1419torrentAdd( tr_session * session, 1420 tr_benc * args_in, 1421 tr_benc * args_out UNUSED, 1422 struct tr_rpc_idle_data * idle_data ) 1423{ 1424 const char * filename = NULL; 1425 const char * metainfo_base64 = NULL; 1426 1427 assert( idle_data != NULL ); 1428 1429 tr_bencDictFindStr( args_in, "filename", &filename ); 1430 tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 ); 1431 if( !filename && !metainfo_base64 ) 1432 return "no filename or metainfo specified"; 1433 else 1434 { 1435 int64_t i; 1436 bool boolVal; 1437 tr_benc * l; 1438 const char * str; 1439 const char * cookies = NULL; 1440 tr_ctor * ctor = tr_ctorNew( session ); 1441 1442 /* set the optional arguments */ 1443 1444 tr_bencDictFindStr( args_in, "cookies", &cookies ); 1445 1446 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) ) 1447 tr_ctorSetDownloadDir( ctor, TR_FORCE, str ); 1448 1449 if( tr_bencDictFindBool( args_in, "paused", &boolVal ) ) 1450 tr_ctorSetPaused( ctor, TR_FORCE, boolVal ); 1451 1452 if( tr_bencDictFindInt( args_in, "peer-limit", &i ) ) 1453 tr_ctorSetPeerLimit( ctor, TR_FORCE, i ); 1454 1455 if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) ) 1456 tr_ctorSetBandwidthPriority( ctor, i ); 1457 1458 if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) { 1459 tr_file_index_t fileCount; 1460 tr_file_index_t * files = fileListFromList( l, &fileCount ); 1461 tr_ctorSetFilesWanted( ctor, files, fileCount, false ); 1462 tr_free( files ); 1463 } 1464 if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) { 1465 tr_file_index_t fileCount; 1466 tr_file_index_t * files = fileListFromList( l, &fileCount ); 1467 tr_ctorSetFilesWanted( ctor, files, fileCount, true ); 1468 tr_free( files ); 1469 } 1470 1471 if( tr_bencDictFindList( args_in, "priority-low", &l ) ) { 1472 tr_file_index_t fileCount; 1473 tr_file_index_t * files = fileListFromList( l, &fileCount ); 1474 tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW ); 1475 tr_free( files ); 1476 } 1477 if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) { 1478 tr_file_index_t fileCount; 1479 tr_file_index_t * files = fileListFromList( l, &fileCount ); 1480 tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL ); 1481 tr_free( files ); 1482 } 1483 if( tr_bencDictFindList( args_in, "priority-high", &l ) ) { 1484 tr_file_index_t fileCount; 1485 tr_file_index_t * files = fileListFromList( l, &fileCount ); 1486 tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH ); 1487 tr_free( files ); 1488 } 1489 1490 dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" ); 1491 1492 if( isCurlURL( filename ) ) 1493 { 1494 struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 ); 1495 d->data = idle_data; 1496 d->ctor = ctor; 1497 tr_webRun( session, filename, NULL, cookies, gotMetadataFromURL, d ); 1498 } 1499 else 1500 { 1501 char * fname = tr_strstrip( tr_strdup( filename ) ); 1502 1503 if( fname == NULL ) 1504 { 1505 int len; 1506 char * metainfo = tr_base64_decode( metainfo_base64, -1, &len ); 1507 tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len ); 1508 tr_free( metainfo ); 1509 } 1510 else if( !strncmp( fname, "magnet:?", 8 ) ) 1511 { 1512 tr_ctorSetMetainfoFromMagnetLink( ctor, fname ); 1513 } 1514 else 1515 { 1516 tr_ctorSetMetainfoFromFile( ctor, fname ); 1517 } 1518 1519 addTorrentImpl( idle_data, ctor ); 1520 1521 tr_free( fname ); 1522 } 1523 1524 } 1525 1526 return NULL; 1527} 1528 1529/*** 1530**** 1531***/ 1532 1533static const char* 1534sessionSet( tr_session * session, 1535 tr_benc * args_in, 1536 tr_benc * args_out UNUSED, 1537 struct tr_rpc_idle_data * idle_data UNUSED ) 1538{ 1539 int64_t i; 1540 double d; 1541 bool boolVal; 1542 const char * str; 1543 1544 assert( idle_data == NULL ); 1545 1546 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) ) 1547 tr_sessionSetCacheLimit_MB( session, i ); 1548 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) ) 1549 tr_sessionSetAltSpeed_KBps( session, TR_UP, i ); 1550 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) ) 1551 tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i ); 1552 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) ) 1553 tr_sessionUseAltSpeed( session, boolVal ); 1554 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) ) 1555 tr_sessionSetAltSpeedBegin( session, i ); 1556 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) ) 1557 tr_sessionSetAltSpeedEnd( session, i ); 1558 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) ) 1559 tr_sessionSetAltSpeedDay( session, i ); 1560 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) ) 1561 tr_sessionUseAltSpeedTime( session, boolVal ); 1562 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) ) 1563 tr_blocklistSetEnabled( session, boolVal ); 1564 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) ) 1565 tr_blocklistSetURL( session, str ); 1566 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) ) 1567 tr_sessionSetDownloadDir( session, str ); 1568 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, &i ) ) 1569 tr_sessionSetQueueStalledMinutes( session, i ); 1570 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_STALLED_ENABLED, &boolVal ) ) 1571 tr_sessionSetQueueStalledEnabled( session, boolVal ); 1572 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, &i ) ) 1573 tr_sessionSetQueueSize( session, TR_DOWN, i ); 1574 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, &boolVal ) ) 1575 tr_sessionSetQueueEnabled ( session, TR_DOWN, boolVal ); 1576 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) ) 1577 tr_sessionSetIncompleteDir( session, str ); 1578 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) ) 1579 tr_sessionSetIncompleteDirEnabled( session, boolVal ); 1580 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) ) 1581 tr_sessionSetPeerLimit( session, i ); 1582 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) ) 1583 tr_sessionSetPeerLimitPerTorrent( session, i ); 1584 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) ) 1585 tr_sessionSetPexEnabled( session, boolVal ); 1586 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) ) 1587 tr_sessionSetDHTEnabled( session, boolVal ); 1588 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) ) 1589 tr_sessionSetUTPEnabled( session, boolVal ); 1590 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) ) 1591 tr_sessionSetLPDEnabled( session, boolVal ); 1592 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) ) 1593 tr_sessionSetPeerPortRandomOnStart( session, boolVal ); 1594 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) ) 1595 tr_sessionSetPeerPort( session, i ); 1596 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) ) 1597 tr_sessionSetPortForwardingEnabled( session, boolVal ); 1598 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) ) 1599 tr_sessionSetIncompleteFileNamingEnabled( session, boolVal ); 1600 if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) ) 1601 tr_sessionSetRatioLimit( session, d ); 1602 if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) ) 1603 tr_sessionSetRatioLimited( session, boolVal ); 1604 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) ) 1605 tr_sessionSetIdleLimit( session, i ); 1606 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) ) 1607 tr_sessionSetIdleLimited( session, boolVal ); 1608 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) ) 1609 tr_sessionSetPaused( session, !boolVal ); 1610 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SEED_QUEUE_ENABLED, &boolVal ) ) 1611 tr_sessionSetQueueEnabled ( session, TR_UP, boolVal ); 1612 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_SEED_QUEUE_SIZE, &i ) ) 1613 tr_sessionSetQueueSize( session, TR_UP, i ); 1614 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) ) 1615 tr_sessionSetTorrentDoneScript( session, str ); 1616 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) ) 1617 tr_sessionSetTorrentDoneScriptEnabled( session, boolVal ); 1618 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) ) 1619 tr_sessionSetDeleteSource( session, boolVal ); 1620 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) ) 1621 tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i ); 1622 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) ) 1623 tr_sessionLimitSpeed( session, TR_DOWN, boolVal ); 1624 if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) ) 1625 tr_sessionSetSpeedLimit_KBps( session, TR_UP, i ); 1626 if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) ) 1627 tr_sessionLimitSpeed( session, TR_UP, boolVal ); 1628 if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) { 1629 if( !strcmp( str, "required" ) ) 1630 tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED ); 1631 else if( !strcmp( str, "tolerated" ) ) 1632 tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED ); 1633 else 1634 tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED ); 1635 } 1636 1637 notify( session, TR_RPC_SESSION_CHANGED, NULL ); 1638 1639 return NULL; 1640} 1641 1642static const char* 1643sessionStats( tr_session * session, 1644 tr_benc * args_in UNUSED, 1645 tr_benc * args_out, 1646 struct tr_rpc_idle_data * idle_data UNUSED ) 1647{ 1648 int running = 0; 1649 int total = 0; 1650 tr_benc * d; 1651 tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 1652 tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 1653 tr_torrent * tor = NULL; 1654 1655 assert( idle_data == NULL ); 1656 1657 while(( tor = tr_torrentNext( session, tor ))) { 1658 ++total; 1659 if( tor->isRunning ) 1660 ++running; 1661 } 1662 1663 tr_sessionGetStats( session, ¤tStats ); 1664 tr_sessionGetCumulativeStats( session, &cumulativeStats ); 1665 1666 tr_bencDictAddInt ( args_out, "activeTorrentCount", running ); 1667 tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) ); 1668 tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running ); 1669 tr_bencDictAddInt ( args_out, "torrentCount", total ); 1670 tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) ); 1671 1672 d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 1673 tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 1674 tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 1675 tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 1676 tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 1677 tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 1678 1679 d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 1680 tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 1681 tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 1682 tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 1683 tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 1684 tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 1685 1686 return NULL; 1687} 1688 1689static const char* 1690sessionGet( tr_session * s, 1691 tr_benc * args_in UNUSED, 1692 tr_benc * args_out, 1693 struct tr_rpc_idle_data * idle_data UNUSED ) 1694{ 1695 const char * str; 1696 tr_benc * d = args_out; 1697 1698 assert( idle_data == NULL ); 1699 tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) ); 1700 tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) ); 1701 tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) ); 1702 tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) ); 1703 tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) ); 1704 tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) ); 1705 tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) ); 1706 tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) ); 1707 tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) ); 1708 tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) ); 1709 tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) ); 1710 tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) ); 1711 tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) ); 1712 tr_bencDictAddBool( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_DOWN ) ); 1713 tr_bencDictAddInt ( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_DOWN ) ); 1714 tr_bencDictAddInt ( d, "download-dir-free-space", tr_sessionGetDownloadDirFreeSpace( s ) ); 1715 tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) ); 1716 tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) ); 1717 tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) ); 1718 tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) ); 1719 tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) ); 1720 tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) ); 1721 tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) ); 1722 tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) ); 1723 tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) ); 1724 tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) ); 1725 tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) ); 1726 tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) ); 1727 tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION ); 1728 tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN ); 1729 tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) ); 1730 tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) ); 1731 tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) ); 1732 tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) ); 1733 tr_bencDictAddBool( d, TR_PREFS_KEY_SEED_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_UP ) ); 1734 tr_bencDictAddInt ( d, TR_PREFS_KEY_SEED_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_UP ) ); 1735 tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) ); 1736 tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) ); 1737 tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) ); 1738 tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) ); 1739 tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) ); 1740 tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) ); 1741 tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) ); 1742 tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) ); 1743 tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, tr_sessionGetQueueStalledMinutes( s ) ); 1744 tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_STALLED_ENABLED, tr_sessionGetQueueStalledEnabled( s ) ); 1745 tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) ); 1746 tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING ); 1747 switch( tr_sessionGetEncryption( s ) ) { 1748 case TR_CLEAR_PREFERRED: str = "tolerated"; break; 1749 case TR_ENCRYPTION_REQUIRED: str = "required"; break; 1750 default: str = "preferred"; break; 1751 } 1752 tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str ); 1753 1754 return NULL; 1755} 1756 1757/*** 1758**** 1759***/ 1760 1761static const char* 1762sessionClose( tr_session * session, 1763 tr_benc * args_in UNUSED, 1764 tr_benc * args_out UNUSED, 1765 struct tr_rpc_idle_data * idle_data UNUSED ) 1766{ 1767 notify( session, TR_RPC_SESSION_CLOSE, NULL ); 1768 return NULL; 1769} 1770 1771/*** 1772**** 1773***/ 1774 1775typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * ); 1776 1777static struct method 1778{ 1779 const char * name; 1780 bool immediate; 1781 handler func; 1782} 1783methods[] = 1784{ 1785 { "port-test", false, portTest }, 1786 { "blocklist-update", false, blocklistUpdate }, 1787 { "session-close", true, sessionClose }, 1788 { "session-get", true, sessionGet }, 1789 { "session-set", true, sessionSet }, 1790 { "session-stats", true, sessionStats }, 1791 { "torrent-add", false, torrentAdd }, 1792 { "torrent-get", true, torrentGet }, 1793 { "torrent-remove", true, torrentRemove }, 1794 { "torrent-set", true, torrentSet }, 1795 { "torrent-set-location", true, torrentSetLocation }, 1796 { "torrent-start", true, torrentStart }, 1797 { "torrent-start-now", true, torrentStartNow }, 1798 { "torrent-stop", true, torrentStop }, 1799 { "torrent-verify", true, torrentVerify }, 1800 { "torrent-reannounce", true, torrentReannounce }, 1801 { "queue-move-top", true, queueMoveTop }, 1802 { "queue-move-up", true, queueMoveUp }, 1803 { "queue-move-down", true, queueMoveDown }, 1804 { "queue-move-bottom", true, queueMoveBottom } 1805}; 1806 1807static void 1808noop_response_callback( tr_session * session UNUSED, 1809 struct evbuffer * response UNUSED, 1810 void * user_data UNUSED ) 1811{ 1812} 1813 1814static void 1815request_exec( tr_session * session, 1816 tr_benc * request, 1817 tr_rpc_response_func callback, 1818 void * callback_user_data ) 1819{ 1820 int i; 1821 const char * str; 1822 tr_benc * args_in = tr_bencDictFind( request, "arguments" ); 1823 const char * result = NULL; 1824 1825 if( callback == NULL ) 1826 callback = noop_response_callback; 1827 1828 /* parse the request */ 1829 if( !tr_bencDictFindStr( request, "method", &str ) ) 1830 result = "no method name"; 1831 else { 1832 const int n = TR_N_ELEMENTS( methods ); 1833 for( i = 0; i < n; ++i ) 1834 if( !strcmp( str, methods[i].name ) ) 1835 break; 1836 if( i ==n ) 1837 result = "method name not recognized"; 1838 } 1839 1840 /* if we couldn't figure out which method to use, return an error */ 1841 if( result != NULL ) 1842 { 1843 int64_t tag; 1844 tr_benc response; 1845 struct evbuffer * buf; 1846 1847 tr_bencInitDict( &response, 3 ); 1848 tr_bencDictAddDict( &response, "arguments", 0 ); 1849 tr_bencDictAddStr( &response, "result", result ); 1850 if( tr_bencDictFindInt( request, "tag", &tag ) ) 1851 tr_bencDictAddInt( &response, "tag", tag ); 1852 1853 buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN ); 1854 (*callback)( session, buf, callback_user_data ); 1855 evbuffer_free( buf ); 1856 1857 tr_bencFree( &response ); 1858 } 1859 else if( methods[i].immediate ) 1860 { 1861 int64_t tag; 1862 tr_benc response; 1863 tr_benc * args_out; 1864 struct evbuffer * buf; 1865 1866 tr_bencInitDict( &response, 3 ); 1867 args_out = tr_bencDictAddDict( &response, "arguments", 0 ); 1868 result = (*methods[i].func)( session, args_in, args_out, NULL ); 1869 if( result == NULL ) 1870 result = "success"; 1871 tr_bencDictAddStr( &response, "result", result ); 1872 if( tr_bencDictFindInt( request, "tag", &tag ) ) 1873 tr_bencDictAddInt( &response, "tag", tag ); 1874 1875 buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN ); 1876 (*callback)( session, buf, callback_user_data ); 1877 evbuffer_free( buf ); 1878 1879 tr_bencFree( &response ); 1880 } 1881 else 1882 { 1883 int64_t tag; 1884 struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 ); 1885 data->session = session; 1886 data->response = tr_new0( tr_benc, 1 ); 1887 tr_bencInitDict( data->response, 3 ); 1888 if( tr_bencDictFindInt( request, "tag", &tag ) ) 1889 tr_bencDictAddInt( data->response, "tag", tag ); 1890 data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 ); 1891 data->callback = callback; 1892 data->callback_user_data = callback_user_data; 1893 (*methods[i].func)( session, args_in, data->args_out, data ); 1894 } 1895} 1896 1897void 1898tr_rpc_request_exec_json( tr_session * session, 1899 const void * request_json, 1900 int request_len, 1901 tr_rpc_response_func callback, 1902 void * callback_user_data ) 1903{ 1904 tr_benc top; 1905 int have_content; 1906 1907 if( request_len < 0 ) 1908 request_len = strlen( request_json ); 1909 1910 have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL ); 1911 request_exec( session, have_content ? &top : NULL, callback, callback_user_data ); 1912 1913 if( have_content ) 1914 tr_bencFree( &top ); 1915} 1916 1917/** 1918 * Munge the URI into a usable form. 1919 * 1920 * We have very loose typing on this to make the URIs as simple as possible: 1921 * - anything not a 'tag' or 'method' is automatically in 'arguments' 1922 * - values that are all-digits are numbers 1923 * - values that are all-digits or commas are number lists 1924 * - all other values are strings 1925 */ 1926void 1927tr_rpc_parse_list_str( tr_benc * setme, 1928 const char * str, 1929 int len ) 1930 1931{ 1932 int valueCount; 1933 int * values = tr_parseNumberRange( str, len, &valueCount ); 1934 1935 if( valueCount == 0 ) 1936 tr_bencInitStr( setme, str, len ); 1937 else if( valueCount == 1 ) 1938 tr_bencInitInt( setme, values[0] ); 1939 else { 1940 int i; 1941 tr_bencInitList( setme, valueCount ); 1942 for( i=0; i<valueCount; ++i ) 1943 tr_bencListAddInt( setme, values[i] ); 1944 } 1945 1946 tr_free( values ); 1947} 1948 1949void 1950tr_rpc_request_exec_uri( tr_session * session, 1951 const void * request_uri, 1952 int request_len, 1953 tr_rpc_response_func callback, 1954 void * callback_user_data ) 1955{ 1956 tr_benc top, * args; 1957 char * request = tr_strndup( request_uri, request_len ); 1958 const char * pch; 1959 1960 tr_bencInitDict( &top, 3 ); 1961 args = tr_bencDictAddDict( &top, "arguments", 0 ); 1962 1963 pch = strchr( request, '?' ); 1964 if( !pch ) pch = request; 1965 while( pch ) 1966 { 1967 const char * delim = strchr( pch, '=' ); 1968 const char * next = strchr( pch, '&' ); 1969 if( delim ) 1970 { 1971 char * key = tr_strndup( pch, delim - pch ); 1972 int isArg = strcmp( key, "method" ) && strcmp( key, "tag" ); 1973 tr_benc * parent = isArg ? args : ⊤ 1974 tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ), 1975 delim + 1, 1976 next ? (size_t)( 1977 next - 1978 ( delim + 1 ) ) : strlen( delim + 1 ) ); 1979 tr_free( key ); 1980 } 1981 pch = next ? next + 1 : NULL; 1982 } 1983 1984 request_exec( session, &top, callback, callback_user_data ); 1985 1986 /* cleanup */ 1987 tr_bencFree( &top ); 1988 tr_free( request ); 1989} 1990