|
|
|
@ -29,8 +29,9 @@ |
|
|
|
|
/** |
|
|
|
|
* A friendly little version check... |
|
|
|
|
*/ |
|
|
|
|
if ( version_compare( PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<' ) ) |
|
|
|
|
if (version_compare(PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<')) { |
|
|
|
|
die("The TransmissionRPC class requires PHP version {TransmissionRPC::TRANSMISSIONRPC_MIN_PHPVER} or above." . PHP_EOL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Transmission bittorrent client/daemon RPC communication class |
|
|
|
@ -133,7 +134,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function start($ids) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( "ids" => $ids ); |
|
|
|
|
return $this->request("torrent-start", $request); |
|
|
|
|
} |
|
|
|
@ -145,7 +148,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function stop($ids) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( "ids" => $ids ); |
|
|
|
|
return $this->request("torrent-stop", $request); |
|
|
|
|
} |
|
|
|
@ -157,7 +162,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function reannounce($ids) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( "ids" => $ids ); |
|
|
|
|
return $this->request("torrent-reannounce", $request); |
|
|
|
|
} |
|
|
|
@ -169,7 +176,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function verify($ids) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( "ids" => $ids ); |
|
|
|
|
return $this->request("torrent-verify", $request); |
|
|
|
|
} |
|
|
|
@ -215,8 +224,12 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function get($ids = array(), $fields = array()) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if ( count( $fields ) == 0 ) $fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); // Defaults |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (count($fields) == 0) { |
|
|
|
|
$fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); |
|
|
|
|
} // Defaults |
|
|
|
|
$request = array( |
|
|
|
|
"fields" => $fields, |
|
|
|
|
"ids" => $ids |
|
|
|
@ -250,8 +263,12 @@ class TransmissionRPC |
|
|
|
|
public function set($ids = array(), $arguments = array()) |
|
|
|
|
{ |
|
|
|
|
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if ( !isset( $arguments['ids'] ) ) $arguments['ids'] = $ids; // Any $ids given in $arguments overrides the method parameter |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!isset($arguments['ids'])) { |
|
|
|
|
$arguments['ids'] = $ids; |
|
|
|
|
} // Any $ids given in $arguments overrides the method parameter |
|
|
|
|
return $this->request("torrent-set", $arguments); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -282,7 +299,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function add_file($torrent_location, $save_path = '', $extra_options = array()) |
|
|
|
|
{ |
|
|
|
|
if(!empty($save_path)) $extra_options['download-dir'] = $save_path; |
|
|
|
|
if (!empty($save_path)) { |
|
|
|
|
$extra_options['download-dir'] = $save_path; |
|
|
|
|
} |
|
|
|
|
$extra_options['filename'] = $torrent_location; |
|
|
|
|
|
|
|
|
|
return $this->request("torrent-add", $extra_options); |
|
|
|
@ -321,7 +340,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function remove($ids, $delete_local_data = false) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( |
|
|
|
|
"ids" => $ids, |
|
|
|
|
"delete-local-data" => $delete_local_data |
|
|
|
@ -338,7 +359,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function move($ids, $target_location, $move_existing_data = true) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $ids to an array if only a single id was passed |
|
|
|
|
$request = array( |
|
|
|
|
"ids" => $ids, |
|
|
|
|
"location" => $target_location, |
|
|
|
@ -374,7 +397,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function rename($ids, $path, $name) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $id to an array if only a single id was passed |
|
|
|
|
if (!is_array($ids)) { |
|
|
|
|
$ids = array( $ids ); |
|
|
|
|
} // Convert $id to an array if only a single id was passed |
|
|
|
|
if (count($ids) !== 1) { |
|
|
|
|
throw new TransmissionRPCException('A single id is accepted', TransmissionRPCException::E_INVALIDARG); |
|
|
|
|
} |
|
|
|
@ -427,32 +452,44 @@ class TransmissionRPC |
|
|
|
|
public function getStatusString($intstatus) |
|
|
|
|
{ |
|
|
|
|
if ($this->rpc_version < 14) { |
|
|
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT ) |
|
|
|
|
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT) { |
|
|
|
|
return "Waiting to verify local files"; |
|
|
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK) { |
|
|
|
|
return "Verifying local files"; |
|
|
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD) { |
|
|
|
|
return "Downloading"; |
|
|
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::RPC_LT_14_TR_STATUS_SEED) { |
|
|
|
|
return "Seeding"; |
|
|
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::RPC_LT_14_TR_STATUS_STOPPED) { |
|
|
|
|
return "Stopped"; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if( $intstatus == self::TR_STATUS_CHECK_WAIT ) |
|
|
|
|
if ($intstatus == self::TR_STATUS_CHECK_WAIT) { |
|
|
|
|
return "Waiting to verify local files"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_CHECK ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_CHECK) { |
|
|
|
|
return "Verifying local files"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_DOWNLOAD ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_DOWNLOAD) { |
|
|
|
|
return "Downloading"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_SEED ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_SEED) { |
|
|
|
|
return "Seeding"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_STOPPED ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_STOPPED) { |
|
|
|
|
return "Stopped"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_SEED_WAIT ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_SEED_WAIT) { |
|
|
|
|
return "Queued for seeding"; |
|
|
|
|
if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT ) |
|
|
|
|
} |
|
|
|
|
if ($intstatus == self::TR_STATUS_DOWNLOAD_WAIT) { |
|
|
|
|
return "Queued for download"; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return "Unknown"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -472,19 +509,27 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
protected function cleanRequestData($array) |
|
|
|
|
{ |
|
|
|
|
if ( !is_array( $array ) || count( $array ) == 0 ) return null; // Nothing to clean |
|
|
|
|
if (!is_array($array) || count($array) == 0) { |
|
|
|
|
return null; |
|
|
|
|
} // Nothing to clean |
|
|
|
|
setlocale(LC_NUMERIC, 'en_US.utf8'); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON |
|
|
|
|
foreach ( $array as $index => $value ) |
|
|
|
|
{ |
|
|
|
|
if( is_object( $value ) ) $array[$index] = $value->toArray(); // Convert objects to arrays so they can be JSON encoded |
|
|
|
|
if( is_array( $value ) ) $array[$index] = $this->cleanRequestData( $value ); // Recursion |
|
|
|
|
if( empty( $value ) && $value !== 0 ) // Remove empty members |
|
|
|
|
{ |
|
|
|
|
foreach ($array as $index => $value) { |
|
|
|
|
if (is_object($value)) { |
|
|
|
|
$array[$index] = $value->toArray(); |
|
|
|
|
} // Convert objects to arrays so they can be JSON encoded |
|
|
|
|
if (is_array($value)) { |
|
|
|
|
$array[$index] = $this->cleanRequestData($value); |
|
|
|
|
} // Recursion |
|
|
|
|
if (empty($value) && $value !== 0) { // Remove empty members |
|
|
|
|
unset($array[$index]); |
|
|
|
|
continue; // Skip the rest of the tests - they may re-add the element. |
|
|
|
|
} |
|
|
|
|
if( is_numeric( $value ) ) $array[$index] = $value+0; // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc) |
|
|
|
|
if( is_bool( $value ) ) $array[$index] = ( $value ? 1 : 0); // Store boolean values as 0 or 1 |
|
|
|
|
if (is_numeric($value)) { |
|
|
|
|
$array[$index] = $value+0; |
|
|
|
|
} // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc) |
|
|
|
|
if (is_bool($value)) { |
|
|
|
|
$array[$index] = ($value ? 1 : 0); |
|
|
|
|
} // Store boolean values as 0 or 1 |
|
|
|
|
if (is_string($value)) { |
|
|
|
|
if (mb_detect_encoding($value, "auto") !== 'UTF-8') { |
|
|
|
|
$array[$index] = mb_convert_encoding($value, "UTF-8"); |
|
|
|
@ -507,23 +552,26 @@ class TransmissionRPC |
|
|
|
|
// Prepare and cast object to array |
|
|
|
|
$return_as_array = false; |
|
|
|
|
$array = $object; |
|
|
|
|
if ( !is_array( $array ) ) $array = (array) $array; |
|
|
|
|
foreach ( $array as $index => $value ) |
|
|
|
|
{ |
|
|
|
|
if( is_array( $array[$index] ) || is_object( $array[$index] ) ) |
|
|
|
|
{ |
|
|
|
|
if (!is_array($array)) { |
|
|
|
|
$array = (array) $array; |
|
|
|
|
} |
|
|
|
|
foreach ($array as $index => $value) { |
|
|
|
|
if (is_array($array[$index]) || is_object($array[$index])) { |
|
|
|
|
$array[$index] = $this->cleanResultObject($array[$index]); // Recursion |
|
|
|
|
} |
|
|
|
|
if ( strstr( $index, '-' ) ) |
|
|
|
|
{ |
|
|
|
|
if (strstr($index, '-')) { |
|
|
|
|
$valid_index = str_replace('-', '_', $index); |
|
|
|
|
$array[$valid_index] = $array[$index]; |
|
|
|
|
unset($array[$index]); |
|
|
|
|
$index = $valid_index; |
|
|
|
|
} |
|
|
|
|
// Might be an array, check index for digits, if so, an array should be returned |
|
|
|
|
if ( ctype_digit( (string) $index ) ) { $return_as_array = true; } |
|
|
|
|
if ( empty( $value ) ) unset( $array[$index] ); |
|
|
|
|
if (ctype_digit((string) $index)) { |
|
|
|
|
$return_as_array = true; |
|
|
|
|
} |
|
|
|
|
if (empty($value)) { |
|
|
|
|
unset($array[$index]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Return array cast to object |
|
|
|
|
return $return_as_array ? $array : (object) $array; |
|
|
|
@ -539,17 +587,21 @@ class TransmissionRPC |
|
|
|
|
protected function request($method, $arguments = array()) |
|
|
|
|
{ |
|
|
|
|
// Check the parameters |
|
|
|
|
if ( !is_scalar( $method ) ) |
|
|
|
|
if (!is_scalar($method)) { |
|
|
|
|
throw new TransmissionRPCException('Method name has no scalar value', TransmissionRPCException::E_INVALIDARG); |
|
|
|
|
if ( !is_array( $arguments ) ) |
|
|
|
|
} |
|
|
|
|
if (!is_array($arguments)) { |
|
|
|
|
throw new TransmissionRPCException('Arguments must be given as array', TransmissionRPCException::E_INVALIDARG); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$arguments = $this->cleanRequestData($arguments); // Sanitize input |
|
|
|
|
|
|
|
|
|
// Grab the X-Transmission-Session-Id if we don't have it already |
|
|
|
|
if( !$this->session_id ) |
|
|
|
|
if( !$this->GetSessionID() ) |
|
|
|
|
if (!$this->session_id) { |
|
|
|
|
if (!$this->GetSessionID()) { |
|
|
|
|
throw new TransmissionRPCException('Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$data = array( |
|
|
|
|
'method' => $method, |
|
|
|
@ -576,7 +628,9 @@ class TransmissionRPC |
|
|
|
|
$content = curl_exec($ch); |
|
|
|
|
curl_close($ch); |
|
|
|
|
|
|
|
|
|
if (!$content) $content = json_encode(array('result' => 'failed')); |
|
|
|
|
if (!$content) { |
|
|
|
|
$content = json_encode(array('result' => 'failed')); |
|
|
|
|
} |
|
|
|
|
return $this->return_as_array ? json_decode($content, true) : $this->cleanResultObject(json_decode($content)); // Return the sanitized result |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
@ -587,8 +641,9 @@ class TransmissionRPC |
|
|
|
|
*/ |
|
|
|
|
public function GetSessionID() |
|
|
|
|
{ |
|
|
|
|
if( !$this->url ) |
|
|
|
|
if (!$this->url) { |
|
|
|
|
throw new TransmissionRPCException("Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Setup the context |
|
|
|
|
$contextopts = $this->default_context_opts; // Start with the defaults |
|
|
|
@ -597,34 +652,40 @@ class TransmissionRPC |
|
|
|
|
$this->session_id = null; |
|
|
|
|
|
|
|
|
|
// Setup authentication (if provided) |
|
|
|
|
if ( $this->username && $this->password ) |
|
|
|
|
if ($this->username && $this->password) { |
|
|
|
|
$contextopts['http']['header'] = sprintf("Authorization: Basic %s\r\n", base64_encode($this->username.':'.$this->password)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:". |
|
|
|
|
if ($this->debug) { |
|
|
|
|
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:". |
|
|
|
|
PHP_EOL . print_r($contextopts, true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$context = stream_context_create($contextopts); // Create the context for this request |
|
|
|
|
if ( ! $fp = @fopen( $this->url, 'r', false, $context ) ) // Open a filepointer to the data, and use fgets to get the result |
|
|
|
|
if (! $fp = @fopen($this->url, 'r', false, $context)) { // Open a filepointer to the data, and use fgets to get the result |
|
|
|
|
throw new TransmissionRPCException('Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check the response (headers etc) |
|
|
|
|
$stream_meta = stream_get_meta_data($fp); |
|
|
|
|
fclose($fp); |
|
|
|
|
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ". |
|
|
|
|
if ($this->debug) { |
|
|
|
|
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ". |
|
|
|
|
PHP_EOL . print_r($stream_meta, true); |
|
|
|
|
if( $stream_meta['timed_out'] ) |
|
|
|
|
} |
|
|
|
|
if ($stream_meta['timed_out']) { |
|
|
|
|
throw new TransmissionRPCException("Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION); |
|
|
|
|
if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" ) |
|
|
|
|
} |
|
|
|
|
if (substr($stream_meta['wrapper_data'][0], 9, 3) == "401") { |
|
|
|
|
throw new TransmissionRPCException("Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION); |
|
|
|
|
elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" ) // This is what we're hoping to find |
|
|
|
|
{ |
|
|
|
|
} elseif (substr($stream_meta['wrapper_data'][0], 9, 3) == "409") { // This is what we're hoping to find |
|
|
|
|
// Loop through the returned headers and extract the X-Transmission-Session-Id |
|
|
|
|
foreach( $stream_meta['wrapper_data'] as $header ) |
|
|
|
|
{ |
|
|
|
|
if( strpos( $header, 'X-Transmission-Session-Id: ' ) === 0 ) |
|
|
|
|
{ |
|
|
|
|
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ". |
|
|
|
|
foreach ($stream_meta['wrapper_data'] as $header) { |
|
|
|
|
if (strpos($header, 'X-Transmission-Session-Id: ') === 0) { |
|
|
|
|
if ($this->debug) { |
|
|
|
|
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ". |
|
|
|
|
PHP_EOL . print_r($header, true); |
|
|
|
|
} |
|
|
|
|
$this->session_id = trim(substr($header, 27)); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -698,11 +759,10 @@ class TransmissionRPCException extends Exception |
|
|
|
|
public function __construct($message = null, $code = 0, Exception $previous = null) |
|
|
|
|
{ |
|
|
|
|
// PHP version 5.3.0 and above support Exception linking |
|
|
|
|
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) |
|
|
|
|
if (version_compare(PHP_VERSION, '5.3.0', '>=')) { |
|
|
|
|
parent::__construct($message, $code, $previous); |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
parent::__construct($message, $code); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
?> |
|
|
|
|
} |
|
|
|
|