/lib/rtorrent.php
PHP | 662 lines | 441 code | 184 blank | 37 comment | 20 complexity | ea91f0eb8f2cbbc10c12fa26e91d46cc MD5 | raw file
1<?php 2 /* 3 * Rtorrent.php PHP Library 0.9 4 * 5 * Avalanche 0.9 Beta 6 * 7 * This code is licensed under the GPL3, or "GNU GENERAL PUBLIC LICENSE Version 3" 8 * For more details, see http://opensource.org/licenses/gpl-3.0.html 9 * 10 * For more information, see http://code.google.com/p/avalanche-rt 11 * 12 * Date: Tue, 09th Mar 2010. 13 * 14 * @author Keith Cirkel ('keithamus') <avalanche@keithcirkel.co.uk> 15 * @license http://opensource.org/licenses/gpl-3.0.html 16 * @copyright Copyright Š 2010, Keith Cirkel 17 * 18 * This program is free software: you can redistribute it and/or modify 19 * it under the terms of the GNU General Public License as published by 20 * the Free Software Foundation, either version 3 of the License, or 21 * (at your option) any later version. 22 * 23 * This program is distributed in the hope that it will be useful, 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * GNU General Public License for more details. 27 28 * You should have received a copy of the GNU General Public License 29 * along with this program. If not, see <http://www.gnu.org/licenses/>. 30 * 31 */ 32 33require_once('xmlrpc.php'); 34 35class Rtorrent 36{ 37 38 private $server; 39 private $params; 40 41 function __construct($params) 42 { 43 44 $this->params = $params; 45 46 $this->server = new xmlrpc_client( 47 $params['rtorrent_scgi_folder'], 48 $params['server_ip'], 49 $params['server_port'], 50 (isset($params['https']) && $params['https']=false)?'https':'http11' 51 ); 52 53 54 if(isset($params['username']) && isset($params['password']) ) 55 { 56 $this->server->setCredentials( 57 $params['username'], 58 $params['password'], 59 $params['set_auth_digest']?CURLAUTH_ANY:CURLAUTH_BASIC 60 ); 61 } 62 63 } 64 65 private function SendAndRecieve($message) 66 { 67 68 $this->server->return_type = 'phpvals'; 69 $this->server->no_multicall = true; 70 71 //Fixes the HTTP/1.1 417 Expect: error. 72 $curlopts = array(CURLOPT_HTTPHEADER => array('Expect:')); 73 74 //If the user has "trust_cert" set to true, then set curl to do so 75 if(isset($this->params['trust_cert']) && $this->params['trust_cert'] === true) 76 { 77 $this->client->setSSLVerifyPeer(FALSE); 78 } 79 80 $this->server->SetCurlOptions($curlopts); 81 $response = $this->server->send($message); 82 83 if(isset($_GET['debugResponse'])) { print_r($response); } 84 85 if($response->faultCode() ) 86 { 87 88 return $response->faultString(); 89 90 } 91 else 92 { 93 94 return $response->value(); 95 96 } 97 98 } 99 100 public function Retrieve($id='') 101 { 102 103 $message = new xmlrpcmsg('d.multicall', array( 104 new xmlrpcval('name'), //Make an array for all loaded torrents 105 new xmlrpcval('d.get_hash='), //The torrent hash 106 new xmlrpcval('d.get_name='), //Torrent's name 107 new xmlrpcval('d.get_state='), //0 = stopped, 1 = started 108 new xmlrpcval('d.get_size_bytes='), //The size in bytes 109 new xmlrpcval('d.get_bytes_done='), //How many bytes completed 110 new xmlrpcval('d.get_up_total='), //How much in total has been uploaded 111 new xmlrpcval('d.get_down_rate='), //Download rate in bytes 112 new xmlrpcval('d.get_up_rate='), //Upload rate in bytes 113 new xmlrpcval('d.get_peers_connected='), //Amount of connected peers 114 new xmlrpcval('d.get_peers_not_connected='), //Amount of unconnected peers 115 new xmlrpcval('d.get_peers_accounted='), //Number of leechers 116 new xmlrpcval('d.get_complete='), //Is the torrent completely downloaded? 117 new xmlrpcval('d.is_hash_checking='), //Is it rehashing? 118 new xmlrpcval('d.get_creation_date='), //Date torrent added 119 new xmlrpcval('d.get_base_path='), //Where the torrent exists 120 new xmlrpcval('d.get_free_diskspace='), //Free disk space where torrent is 121 new xmlrpcval('d.is_private='), //Is torrent private? 122 new xmlrpcval('d.get_message='), //Comment 123 new xmlrpcval('d.get_priority='), //Priority (number) 124 new xmlrpcval('d.is_hash_checked='), //Has it been hash checked before? 125 new xmlrpcval('d.get_skip_total='), //How many wasted bytes? 126 new xmlrpcval('d.get_custom5='), //We use this for the torrents "new name" 127 new xmlrpcval('d.get_custom4='), //We use this for the torrents "label" 128 //http://libtorrent.rakshasa.no/ticket/1538 < Describes a solution for 129 //rtorrent builds that don't use I8, we multiply chunk_size by size_chunks 130 new xmlrpcval('d.get_chunk_size='), //Get the size of a single chunk in bytes 131 new xmlrpcval('d.get_size_chunks='), //Get how many chunks are in the torrent 132 new xmlrpcval('d.get_completed_chunks=') //Get how many chunks have downloaded. 133 ) ); 134 135 $torrents = $this->SendAndRecieve($message); 136 137 if(!is_array($torrents) ) 138 { 139 140 return json_encode(array('error'=>$torrents)); 141 142 } 143 144 foreach($torrents as $torrent) 145 { 146 147 $df = @disk_free_space(dirname($torrent[14])); 148 $dt = @disk_total_space(dirname($torrent[14])); 149 150 $return_array[$torrent[0]] = array( 151 152 'name' => htmlentities($torrent[1]), 153 'is_downloading' => $torrent[2], 154 //Old versions of rtorrent use i4 which buffer overflows > 4gb 155 'size' => $torrent[3]<0?$torrent[23]* $torrent[24]:$torrent[3], 156 'downloaded' => $torrent[4]<0?$torrent[23]* $torrent[25]:$torrent[4], 157 'uploaded' => $torrent[5]<0?2147483648:$torrent[5], 158 'down_rate' => $torrent[6], 159 'up_rate' => $torrent[7], 160 'peers_connected' => $torrent[8]-($torrent[8]-$torrent[10]), 161 'peers_total' => $torrent[9]+$torrent[8], 162 'seeders_connected' => $torrent[8]-$torrent[10], 163 'is_completed' => $torrent[11], 164 'is_hashing' => $torrent[12], 165 'date_added' => $torrent[13], 166 'base_path' => $torrent[14], 167 //Remember the bug above? We're going to mangle this a bit... 168 'free_diskspace' => $df?$df:$torrent[15], 169 'total_diskspace' => $dt?$dt:0, 170 'private' => $torrent[16], 171 'tracker_status' => $torrent[17], 172 'priority' => $torrent[18], 173 'has_been_hashed' => $torrent[19], 174 'wasted_bytes' => $torrent[20], 175 'new_name' => $torrent[21], 176 'label' => $torrent[22], 177 178 ); 179 180 } 181 182 return $return_array; 183 184 } 185 186 public function LoadURL($url, $start) 187 { 188 $command = $start=='true'?'load_start_verbose':'load_verbose'; 189 190 $message = new xmlrpcmsg($command, array(new xmlrpcval($url) ) ); 191 192 $response = $this->SendAndRecieve($message); 193 194 $this->server->setDebug(2); 195 196 $return = array(); 197 198 $return['openurl']=$response==0?true:false; 199 200 return $return; 201 202 } 203 204 public function Remove($id) 205 { 206 207 $message = new xmlrpcmsg('d.erase', array(new xmlrpcval($id) ) ); 208 209 $response = $this->SendAndRecieve($message); 210 211 $return = array(); 212 213 $return['remove']=$response==0?true:alse; 214 215 return $return; 216 217 } 218 219 public function Pause($id) 220 { 221 222 $message = new xmlrpcmsg('d.stop', array(new xmlrpcval($id))); 223 224 $this->SendAndRecieve($message); 225 226 $message = new xmlrpcmsg('d.close', array(new xmlrpcval($id))); 227 228 $response = $this->SendAndRecieve($message); 229 230 $return = array(); 231 232 $return['pause']=$response==0?true:false; 233 234 return $return; 235 236 } 237 238 public function PauseAll() 239 { 240 241 $message = new xmlrpcmsg('d.multicall', array( 242 new xmlrpcval('started'), //Get all ongoing torrents 243 new xmlrpcval('d.stop='), //Stop all ongoing torrents 244 )); 245 246 $this->SendAndRecieve($message); 247 248 $message = new xmlrpcmsg('d.multicall', array( 249 new xmlrpcval('stopped'), //Get all ongoing torrents 250 new xmlrpcval('d.close='), //Stop all ongoing torrents 251 )); 252 253 $response = $this->SendAndRecieve($message); 254 255 $return = array(); 256 257 $return['pause']=$response==0?true:false; 258 259 return $return; 260 261 } 262 263 public function Resume($id) 264 { 265 266 $message = new xmlrpcmsg('d.open', array(new xmlrpcval($id))); 267 268 $this->SendAndRecieve($message); 269 270 $message = new xmlrpcmsg('d.start', array(new xmlrpcval($id))); 271 272 $response = $this->SendAndRecieve($message); 273 274 $return = array(); 275 276 $return['resume']=$response==0?true:false; 277 278 return $return; 279 280 } 281 282 public function ResumeAll() 283 { 284 285 $message = new xmlrpcmsg('d.multicall', array( 286 new xmlrpcval('closed'), //Get all ongoing torrents 287 new xmlrpcval('d.open='), //Stop all ongoing torrents 288 )); 289 290 $this->SendAndRecieve($message); 291 292 $message = new xmlrpcmsg('d.multicall', array( 293 new xmlrpcval('stopped'), //Get all ongoing torrents 294 new xmlrpcval('d.start='), //Stop all ongoing torrents 295 )); 296 297 $response = $this->SendAndRecieve($message); 298 299 $return = array(); 300 301 $return['pause']=$response==0?true:false; 302 303 return $return; 304 305 } 306 307 public function SetTorrentPriority($id, $priority) 308 { 309 $message = new xmlrpcmsg('d.set_priority',array( 310 new xmlrpcval($id), //Torrent ID 311 new xmlrpcval($priority, 'int'), //Priority 312 )); 313 314 $response = $this->SendAndRecieve($message); 315 316 $return = array(); 317 318 $return['setpriority']=$response==0?true:false; 319 320 return $return; 321 322 } 323 324 public function SetFilePriority($id, $file_id, $priority) 325 { 326 //We have a range of priorities 327 if(strpos($file_id,':')) 328 { 329 $priorities_ar = array(); 330 $file_id= explode(':', $file_id); 331 for($i=$file_id[0]; $i<=$file_id[1]; ++$i) 332 { 333 $priorities_ar[]= new xmlrpcval(array( 334 'methodName'=>new xmlrpcval('f.set_priority'), 335 'params'=>new xmlrpcval(array( 336 new xmlrpcval($id), 337 new xmlrpcval($i, 'int'), 338 new xmlrpcval($priority, 'int'), 339 ), 'array') 340 ),'struct'); 341 342 } 343 } 344 elseif(strpos($file_id,',')) 345 { 346 $priorities_ar = array(); 347 $file_id= explode(',', $file_id); 348 $count = count($file_id); 349 for($i=0; $i<=$count; ++$i) 350 { 351 $priorities_ar[]= new xmlrpcval(array( 352 'methodName'=>new xmlrpcval('f.set_priority'), 353 'params'=>new xmlrpcval(array( 354 new xmlrpcval($id), 355 new xmlrpcval($file_id[$i], 'int'), 356 new xmlrpcval($priority, 'int'), 357 ), 'array') 358 ),'struct'); 359 360 } 361 } 362 else 363 { 364 $priorities_ar = array(new xmlrpcval(array( 365 'methodName'=>new xmlrpcval('f.set_priority'), 366 'params'=>new xmlrpcval(array( 367 new xmlrpcval($id), 368 new xmlrpcval($file_id, 'int'), 369 new xmlrpcval($priority, 'int'), 370 ), 'array') 371 ),'struct')); 372 } 373 374 $priorities_ar[] = new xmlrpcval(array( 375 'methodName'=>new xmlrpcval('d.update_priorities'), 376 'params'=>new xmlrpcval(array( 377 new xmlrpcval($id) 378 ), 'array'), 379 ), 'struct'); 380 381 $message = new xmlrpcmsg('system.multicall',array( 382 new xmlrpcval($priorities_ar, 'array'))); 383 384 385 $response = $this->SendAndRecieve($message); 386 387 $return = array(); 388 389 $return['setpriority']=true; 390 391 foreach($response as $response_array) 392 { 393 foreach($response_array as $res) 394 { 395 if($res!=0) 396 { 397 $return['setpriority']=false; 398 return $return; 399 } 400 } 401 } 402 403 return $return; 404 405 } 406 407 public function SetRate($type, $rate) 408 { 409 410 if($type!= 'upload' && $type!= 'download') 411 { 412 return json_encode(array('error'=>'Unrecognised command')); 413 } 414 else 415 { 416 $message = new xmlrpcmsg('set_'.$type.'_rate', array(new xmlrpcval($rate.'k'))); 417 418 $response = $this->SendAndRecieve($message); 419 420 $return = array(); 421 422 $return['setrate']=$response==0?true:false; 423 424 return $return; 425 426 } 427 428 } 429 430 public function GetRate($id) 431 { 432 433 if($id!= 'upload' && $id!= 'download') 434 { 435 return json_encode(array('error'=>'Unrecognised command')); 436 } 437 else 438 { 439 $message = new xmlrpcmsg('get_'.$id.'_rate'); 440 } 441 442 return array('getRate'=>$this->SendAndRecieve($message)/1024); 443 444 } 445 446 public function GetTrackers($id) 447 { 448 449 $message = new xmlrpcmsg('t.multicall', array( 450 new xmlrpcval($id), //Torrent ID 451 new xmlrpcval(''), 452 new xmlrpcval('t.get_url='), //Tracker's URL 453 new xmlrpcval('t.get_scrape_incomplete='), //The peers scraped from the tracker 454 new xmlrpcval('t.get_scrape_complete='), //The seeders scraped from the tracker 455 new xmlrpcval('t.is_enabled='), //0 = disabled , 1 = enabled 456 new xmlrpcval('t.get_group='), //0 = disabled , 1 = enabled 457 new xmlrpcval('t.is_open='), //0 = closed , 1 = open 458 ) ); 459 460 $trackers = $this->SendAndRecieve($message); 461 462 if(!is_array($trackers) ) 463 { 464 465 return json_encode(array('error'=>$trackers)); 466 467 } 468 469 $return_array = array(); 470 471 foreach($trackers as $tracker) 472 { 473 474 $return_array[$tracker[0]] = array( 475 476 'peers' => $tracker[1], 477 'seeders' => $tracker[2], 478 'enabled' => $tracker[3]==1?true:false, 479 'id' => $tracker[4], 480 'open' => $tracker[5]==1?true:false, 481 482 ); 483 484 } 485 486 if(array_key_exists('dht://', $return_array)) 487 { 488 $message = new xmlrpcmsg('dht_statistics', array(new xmlrpcval($id))); 489 $dht = $this->SendAndRecieve($message); 490 $return_array['dht://']['peers']=$dht['peers']; 491 $return_array['dht://']['nodes']=$dht['nodes']; 492 unset($return_array['dht://']['seeders']); 493 } 494 495 return $return_array; 496 497 } 498 499 public function SetTracker($id, $tracker, $state) 500 { 501 $state=$state==1?1:0; 502 503 $message = new xmlrpcmsg('t.set_enabled', array( 504 new xmlrpcval($id), //Torrent ID 505 new xmlrpcval($tracker, 'int'), //The tracker ID 506 new xmlrpcval($state, 'int') //Disabled or Enabled. 507 ) ); 508 509 $response = $this->SendAndRecieve($message); 510 511 $return = array(); 512 513 $return['settracker']=$reponse[0]==0?true:false; 514 515 return $return; 516 } 517 518 public function GetPeers($id) 519 { 520 521 $message = new xmlrpcmsg('p.multicall', array( 522 new xmlrpcval($id), //Torrent ID 523 new xmlrpcval(''), 524 new xmlrpcval('p.get_address='), //The peer IP 525 new xmlrpcval('p.get_client_version='), //Peers rtorrent Program 526 new xmlrpcval('p.get_completed_percent='), //Peers % complete 527 new xmlrpcval('p.get_down_rate='), //How fast this peer is seeding to us 528 new xmlrpcval('p.get_down_total='), //How much they've seeded to us 529 new xmlrpcval('p.get_up_rate='), //How fast we're seeding to this peer 530 new xmlrpcval('p.get_up_total='), //How much we've seeded to them 531 new xmlrpcval('p.is_encrypted='), //0 = not encrypted , 1 = encrypted 532 new xmlrpcval('p.is_obfuscated='), //0 = not obf., 1 = obf. 533 new xmlrpcval('p.is_snubbed='), //0 = not snubbed, 1 = snubbed 534 ) ); 535 536 $peers = $this->SendAndRecieve($message); 537 538 if(!is_array($peers) ) 539 { 540 541 return json_encode(array('error'=>$peers)); 542 543 } 544 545 $return_array = array(); 546 547 foreach($peers as $peer) 548 { 549 550 $return_array[$peer[0]] = array( 551 552 'client' => $peer[1], 553 'completed_percent' => $peer[2], 554 'down_rate' => $peer[3], 555 'down_total' => $peer[4], 556 'up_rate' => $peer[5], 557 'up_total' => $peer[6], 558 'is_e' => $peer[7], 559 'is_o' => $peer[8], 560 'is_s' => $peer[9], 561 562 ); 563 564 } 565 566 return $return_array; 567 568 } 569 570 public function GetFiles($id) 571 { 572 573 $message = new xmlrpcmsg('f.multicall', array( 574 new xmlrpcval($id), //Torrent ID 575 new xmlrpcval(''), 576 new xmlrpcval('f.get_path='), //The file path 577 new xmlrpcval('f.get_priority='), //The file priority 578 new xmlrpcval('f.get_size_bytes='), //The file size in bytes 579 new xmlrpcval('f.get_completed_chunks='), //Chunks done of the file 580 new xmlrpcval('f.get_size_chunks='), //The file size in chunks 581 new xmlrpcval('f.get_size_chunks='), //The file size in chunks 582 ) ); 583 584 $files = $this->SendAndRecieve($message); 585 586 if(!is_array($files) ) 587 { 588 589 return json_encode(array('error'=>$files)); 590 591 } 592 593 $return_array = array(); 594 595 foreach($files as $file) 596 { 597 598 $return_array[] = array( 599 600 'path' => $file[0], 601 'priority' => $file[1], 602 'size_bytes' => $file[2]<0?2147483648:$file[2], 603 'chunks_complete' => $file[3], 604 'chunks' => $file[4] 605 606 ); 607 608 } 609 610 return $return_array; 611 612 } 613 614 public function SetName($id, $name) 615 { 616 $message = new xmlrpcmsg('d.set_custom5', 617 array(new xmlrpcval($id), new xmlrpcval($name) ) ); 618 619 $reponse = $this->SendAndRecieve($message); 620 621 $return = array(); 622 623 $return['rename']=$reponse[0]==0?true:false; 624 625 return $return; 626 } 627 628 public function SetLabel($id, $label) 629 { 630 631 $message = new xmlrpcmsg('d.set_custom4', 632 array(new xmlrpcmsg($id), new xmlrpcmsg($label) ) ); 633 634 $reponse = $this->SendAndRecieve($message); 635 636 $return = array(); 637 638 $return['setLabel']=$reponse[0]==0?true:false; 639 640 return $return; 641 642 } 643 644 public function GetListOfCommands() 645 { 646 647 $message = new xmlrpcmsg('system.listMethods'); 648 return $this->SendAndRecieve($message); 649 650 } 651 652 public function Debug() 653 { 654 655 $message = new xmlrpcmsg('system.listMethods'); 656 $this->server->setDebug(2); 657 print_r($this); 658 return $this->SendAndRecieve($message); 659 660 } 661 662}