708 lines
25 KiB
708 lines
25 KiB
<?php
|
|
/**
|
|
* Transmission bittorrent client/daemon RPC communication class
|
|
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
|
|
* Bryce Chidester <bryce@cobryce.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* PHP version specific information
|
|
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
|
|
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
|
|
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
|
|
* PHP Class support (PHP 5) (PHP 4 might work, untested)
|
|
*/
|
|
|
|
/**
|
|
* A friendly little version check...
|
|
*/
|
|
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
|
|
*
|
|
* Usage example:
|
|
* <code>
|
|
* $rpc = new TransmissionRPC($rpc_url);
|
|
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
|
|
* </code>
|
|
*
|
|
*/
|
|
class TransmissionRPC
|
|
{
|
|
/**
|
|
* User agent used in all http communication
|
|
*/
|
|
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
|
|
|
|
/**
|
|
* Minimum PHP version required
|
|
* 5.2.10 implemented the required http stream ignore_errors option
|
|
*/
|
|
const MIN_PHPVER = '5.2.10';
|
|
|
|
/**
|
|
* The URL to the bittorent client you want to communicate with
|
|
* the port (default: 9091) can be set in you Transmission preferences
|
|
* @var string
|
|
*/
|
|
public $url = '';
|
|
|
|
/**
|
|
* If your Transmission RPC requires authentication, supply username here
|
|
* @var string
|
|
*/
|
|
public $username = '';
|
|
|
|
/**
|
|
* If your Transmission RPC requires authentication, supply password here
|
|
* @var string
|
|
*/
|
|
public $password = '';
|
|
|
|
/**
|
|
* Return results as an array, or an object (default)
|
|
* @var bool
|
|
*/
|
|
public $return_as_array = false;
|
|
|
|
/**
|
|
* Print debugging information, default is off
|
|
* @var bool
|
|
*/
|
|
public $debug = false;
|
|
|
|
/**
|
|
* Transmission RPC version
|
|
* @var int
|
|
*/
|
|
protected $rpc_version = 0;
|
|
|
|
/**
|
|
* Transmission uses a session id to prevent CSRF attacks
|
|
* @var string
|
|
*/
|
|
protected $session_id = '';
|
|
|
|
/**
|
|
* Default values for stream context
|
|
* @var array
|
|
*/
|
|
private $default_context_opts = array( 'http' => array(
|
|
'user_agent' => self::HTTP_UA,
|
|
'timeout' => '60', // Don't want to be too slow
|
|
'ignore_errors' => true, // Leave the error parsing/handling to the code
|
|
)
|
|
);
|
|
|
|
/**
|
|
* Constants for torrent status
|
|
*/
|
|
const TR_STATUS_STOPPED = 0;
|
|
const TR_STATUS_CHECK_WAIT = 1;
|
|
const TR_STATUS_CHECK = 2;
|
|
const TR_STATUS_DOWNLOAD_WAIT = 3;
|
|
const TR_STATUS_DOWNLOAD = 4;
|
|
const TR_STATUS_SEED_WAIT = 5;
|
|
const TR_STATUS_SEED = 6;
|
|
|
|
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
|
|
const RPC_LT_14_TR_STATUS_CHECK = 2;
|
|
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
|
|
const RPC_LT_14_TR_STATUS_SEED = 8;
|
|
const RPC_LT_14_TR_STATUS_STOPPED = 16;
|
|
|
|
/**
|
|
* Start one or more torrents
|
|
*
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
public function start ( $ids )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* Stop one or more torrents
|
|
*
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
public function stop ( $ids )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* Reannounce one or more torrents
|
|
*
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
public function reannounce ( $ids )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* Verify one or more torrents
|
|
*
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
public function verify ( $ids )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* Get information on torrents in transmission, if the ids parameter is
|
|
* empty all torrents will be returned. The fields array can be used to return certain
|
|
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
|
|
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
|
|
*
|
|
* @param array fields An array of return fields
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*
|
|
Request:
|
|
{
|
|
"arguments": {
|
|
"fields": [ "id", "name", "totalSize" ],
|
|
"ids": [ 7, 10 ]
|
|
},
|
|
"method": "torrent-get",
|
|
"tag": 39693
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"arguments": {
|
|
"torrents": [
|
|
{
|
|
"id": 10,
|
|
"name": "Fedora x86_64 DVD",
|
|
"totalSize": 34983493932,
|
|
},
|
|
{
|
|
"id": 7,
|
|
"name": "Ubuntu x86_64 DVD",
|
|
"totalSize", 9923890123,
|
|
}
|
|
]
|
|
},
|
|
"result": "success",
|
|
"tag": 39693
|
|
}
|
|
*/
|
|
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
|
|
$request = array(
|
|
"fields" => $fields,
|
|
"ids" => $ids
|
|
);
|
|
return $this->request( "torrent-get", $request );
|
|
}
|
|
|
|
/**
|
|
* Set properties on one or more torrents, available fields are:
|
|
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
|
|
* "downloadLimit" | number maximum download speed (in K/s)
|
|
* "downloadLimited" | boolean true if "downloadLimit" is honored
|
|
* "files-wanted" | array indices of file(s) to download
|
|
* "files-unwanted" | array indices of file(s) to not download
|
|
* "honorsSessionLimits" | boolean true if session upload limits are honored
|
|
* "ids" | array torrent list, as described in 3.1
|
|
* "location" | string new location of the torrent's content
|
|
* "peer-limit" | number maximum number of peers
|
|
* "priority-high" | array indices of high-priority file(s)
|
|
* "priority-low" | array indices of low-priority file(s)
|
|
* "priority-normal" | array indices of normal-priority file(s)
|
|
* "seedRatioLimit" | double session seeding ratio
|
|
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
|
|
* "uploadLimit" | number maximum upload speed (in K/s)
|
|
* "uploadLimited" | boolean true if "uploadLimit" is honored
|
|
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
|
|
*
|
|
* @param array arguments An associative array of arguments to set
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
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
|
|
return $this->request( "torrent-set", $arguments );
|
|
}
|
|
|
|
/**
|
|
* Add a new torrent
|
|
*
|
|
* Available extra options:
|
|
* key | value type & description
|
|
* ---------------------+-------------------------------------------------
|
|
* "download-dir" | string path to download the torrent to
|
|
* "filename" | string filename or URL of the .torrent file
|
|
* "metainfo" | string base64-encoded .torrent content
|
|
* "paused" | boolean if true, don't start the torrent
|
|
* "peer-limit" | number maximum number of peers
|
|
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
|
|
* "files-wanted" | array indices of file(s) to download
|
|
* "files-unwanted" | array indices of file(s) to not download
|
|
* "priority-high" | array indices of high-priority file(s)
|
|
* "priority-low" | array indices of low-priority file(s)
|
|
* "priority-normal" | array indices of normal-priority file(s)
|
|
*
|
|
* Either "filename" OR "metainfo" MUST be included.
|
|
* All other arguments are optional.
|
|
*
|
|
* @param torrent_location The URL or path to the torrent file
|
|
* @param save_path Folder to save torrent in
|
|
* @param extra options Optional extra torrent options
|
|
*/
|
|
public function add_file ( $torrent_location, $save_path = '', $extra_options = array() )
|
|
{
|
|
if(!empty($save_path)) $extra_options['download-dir'] = $save_path;
|
|
$extra_options['filename'] = $torrent_location;
|
|
|
|
return $this->request( "torrent-add", $extra_options );
|
|
}
|
|
|
|
/**
|
|
* Add a torrent using the raw torrent data
|
|
*
|
|
* @param torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
|
|
* @param save_path Folder to save torrent in
|
|
* @param extra options Optional extra torrent options
|
|
*/
|
|
public function add_metainfo ( $torrent_metainfo, $save_path = '', $extra_options = array() )
|
|
{
|
|
$extra_options['download-dir'] = $save_path;
|
|
$extra_options['metainfo'] = base64_encode( $torrent_metainfo );
|
|
|
|
return $this->request( "torrent-add", $extra_options );
|
|
}
|
|
|
|
/* Add a new torrent using a file path or a URL (For backwards compatibility)
|
|
* @param torrent_location The URL or path to the torrent file
|
|
* @param save_path Folder to save torrent in
|
|
* @param extra options Optional extra torrent options
|
|
*/
|
|
public function add ( $torrent_location, $save_path = '', $extra_options = array() )
|
|
{
|
|
return $this->add_file( $torrent_location, $save_path, $extra_options );
|
|
}
|
|
|
|
/**
|
|
* Remove torrent from transmission
|
|
*
|
|
* @param bool delete_local_data Also remove local data?
|
|
* @param int|array ids A list of transmission torrent ids
|
|
*/
|
|
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
|
|
$request = array(
|
|
"ids" => $ids,
|
|
"delete-local-data" => $delete_local_data
|
|
);
|
|
return $this->request( "torrent-remove", $request );
|
|
}
|
|
|
|
/**
|
|
* Move local storage location
|
|
*
|
|
* @param int|array ids A list of transmission torrent ids
|
|
* @param string target_location The new storage location
|
|
* @param string move_existing_data Move existing data or scan new location for available data
|
|
*/
|
|
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
|
|
$request = array(
|
|
"ids" => $ids,
|
|
"location" => $target_location,
|
|
"move" => $move_existing_data
|
|
);
|
|
return $this->request( "torrent-set-location", $request );
|
|
}
|
|
|
|
/**
|
|
* 3.7. Renaming a Torrent's Path
|
|
*
|
|
* Method name: "torrent-rename-path"
|
|
*
|
|
* For more information on the use of this function, see the transmission.h
|
|
* documentation of tr_torrentRenamePath(). In particular, note that if this
|
|
* call succeeds you'll want to update the torrent's "files" and "name" field
|
|
* with torrent-get.
|
|
*
|
|
* Request arguments:
|
|
*
|
|
* string | value type & description
|
|
* ---------------------------------+-------------------------------------------------
|
|
* "ids" | array the torrent torrent list, as described in 3.1
|
|
* | (must only be 1 torrent)
|
|
* "path" | string the path to the file or folder that will be renamed
|
|
* "name" | string the file or folder's new name
|
|
|
|
* Response arguments: "path", "name", and "id", holding the torrent ID integer
|
|
*
|
|
* @param int|array ids A 1-element list of transmission torrent ids
|
|
* @param string path The path to the file or folder that will be renamed
|
|
* @param string name The file or folder's new name
|
|
*/
|
|
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 ( count( $ids ) !== 1 ) {
|
|
throw new TransmissionRPCException( 'A single id is accepted', TransmissionRPCException::E_INVALIDARG );
|
|
}
|
|
|
|
$request = array(
|
|
"ids" => $ids,
|
|
"path" => $path,
|
|
"name" => $name
|
|
);
|
|
return $this->request( "torrent-rename-path", $request );
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve session statistics
|
|
*
|
|
* @returns array of statistics
|
|
*/
|
|
public function sstats ( )
|
|
{
|
|
return $this->request( "session-stats", array() );
|
|
}
|
|
|
|
/**
|
|
* Retrieve all session variables
|
|
*
|
|
* @returns array of session information
|
|
*/
|
|
public function sget ( )
|
|
{
|
|
return $this->request( "session-get", array() );
|
|
}
|
|
|
|
/**
|
|
* Set session variable(s)
|
|
*
|
|
* @param array of session variables to set
|
|
*/
|
|
public function sset ( $arguments )
|
|
{
|
|
return $this->request( "session-set", $arguments );
|
|
}
|
|
|
|
/**
|
|
* Return the interpretation of the torrent status
|
|
*
|
|
* @param int The integer "torrent status"
|
|
* @returns string The translated meaning
|
|
*/
|
|
public function getStatusString ( $intstatus )
|
|
{
|
|
if($this->rpc_version < 14){
|
|
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 )
|
|
return "Verifying local files";
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD )
|
|
return "Downloading";
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED )
|
|
return "Seeding";
|
|
if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED )
|
|
return "Stopped";
|
|
}else{
|
|
if( $intstatus == self::TR_STATUS_CHECK_WAIT )
|
|
return "Waiting to verify local files";
|
|
if( $intstatus == self::TR_STATUS_CHECK )
|
|
return "Verifying local files";
|
|
if( $intstatus == self::TR_STATUS_DOWNLOAD )
|
|
return "Downloading";
|
|
if( $intstatus == self::TR_STATUS_SEED )
|
|
return "Seeding";
|
|
if( $intstatus == self::TR_STATUS_STOPPED )
|
|
return "Stopped";
|
|
if( $intstatus == self::TR_STATUS_SEED_WAIT )
|
|
return "Queued for seeding";
|
|
if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT )
|
|
return "Queued for download";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Here be dragons (Internal methods)
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
* Clean up the request array. Removes any empty fields from the request
|
|
*
|
|
* @param array array The request associative array to clean
|
|
* @returns array The cleaned array
|
|
*/
|
|
protected function cleanRequestData ( $array )
|
|
{
|
|
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
|
|
{
|
|
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_string( $value ) ) {
|
|
if ( mb_detect_encoding($value,"auto") !== 'UTF-8' ) {
|
|
$array[$index] = mb_convert_encoding($value, "UTF-8");
|
|
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
|
|
}
|
|
}
|
|
}
|
|
return $array;
|
|
}
|
|
|
|
/**
|
|
* Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
|
|
* and converts any object with any all-digit property names to an array.
|
|
*
|
|
* @param object The request result to clean
|
|
* @returns array The cleaned object
|
|
*/
|
|
protected function cleanResultObject ( $object )
|
|
{
|
|
// 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] ) )
|
|
{
|
|
$array[$index] = $this->cleanResultObject( $array[$index] ); // Recursion
|
|
}
|
|
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] );
|
|
}
|
|
// Return array cast to object
|
|
return $return_as_array ? $array : (object) $array;
|
|
}
|
|
|
|
/**
|
|
* 执行 rpc 请求
|
|
*
|
|
* @param $method 请求类型/方法, 详见 $this->allowMethods
|
|
* @param array $arguments 附加参数, 可选
|
|
* @return mixed
|
|
*/
|
|
protected function request($method, $arguments = array())
|
|
{
|
|
// Check the parameters
|
|
if ( !is_scalar( $method ) )
|
|
throw new TransmissionRPCException( 'Method name has no scalar value', TransmissionRPCException::E_INVALIDARG );
|
|
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() )
|
|
throw new TransmissionRPCException( 'Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID );
|
|
|
|
$data = array(
|
|
'method' => $method,
|
|
'arguments' => $arguments
|
|
);
|
|
|
|
$header = array(
|
|
'Content-Type: application/json',
|
|
'Authorization: Basic '.base64_encode(sprintf("%s:%s", $this->username, $this->password)),
|
|
'X-Transmission-Session-Id: '.$this->session_id
|
|
);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->url);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
|
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
|
|
curl_setopt($ch, CURLOPT_HEADER, false);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
|
|
$content = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
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
|
|
}
|
|
/**
|
|
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
|
|
* and store it in $this->session_id
|
|
*
|
|
* @return string
|
|
*/
|
|
public function GetSessionID()
|
|
{
|
|
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
|
|
|
|
// Make sure it's blank/empty (reset)
|
|
$this->session_id = null;
|
|
|
|
// Setup authentication (if provided)
|
|
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:".
|
|
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
|
|
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: ".
|
|
PHP_EOL . print_r( $stream_meta, true );
|
|
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" )
|
|
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
|
|
{
|
|
// 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: ".
|
|
PHP_EOL . print_r( $header, true );
|
|
$this->session_id = trim( substr( $header, 27 ) );
|
|
break;
|
|
}
|
|
}
|
|
if( ! $this->session_id ) { // Didn't find a session_id
|
|
throw new TransmissionRPCException( "Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID );
|
|
}
|
|
} else {
|
|
throw new TransmissionRPCException( "Unexpected response from Transmission RPC: ".$stream_meta['wrapper_data'][0] );
|
|
}
|
|
return $this->session_id;
|
|
}
|
|
|
|
/**
|
|
* Takes the connection parameters
|
|
*
|
|
* TODO: Sanitize username, password, and URL
|
|
*
|
|
* @param string $url
|
|
* @param string $username
|
|
* @param string $password
|
|
*/
|
|
public function __construct( $url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false )
|
|
{
|
|
// server URL
|
|
$this->url = $url;
|
|
|
|
// Username & password
|
|
$this->username = $username;
|
|
$this->password = $password;
|
|
|
|
// Get the Transmission RPC_version
|
|
$this->rpc_version = self::sget()->arguments->rpc_version;
|
|
|
|
// Return As Array
|
|
$this->return_as_array = $return_as_array;
|
|
|
|
// Reset X-Transmission-Session-Id so we (re)fetch one
|
|
$this->session_id = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is the type of exception the TransmissionRPC class will throw
|
|
*/
|
|
class TransmissionRPCException extends Exception
|
|
{
|
|
/**
|
|
* Exception: Invalid arguments
|
|
*/
|
|
const E_INVALIDARG = -1;
|
|
|
|
/**
|
|
* Exception: Invalid Session-Id
|
|
*/
|
|
const E_SESSIONID = -2;
|
|
|
|
/**
|
|
* Exception: Error while connecting
|
|
*/
|
|
const E_CONNECTION = -3;
|
|
|
|
/**
|
|
* Exception: Error 401 returned, unauthorized
|
|
*/
|
|
const E_AUTHENTICATION = -4;
|
|
|
|
/**
|
|
* Exception constructor
|
|
*/
|
|
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', '>=' ) )
|
|
parent::__construct( $message, $code, $previous );
|
|
else
|
|
parent::__construct( $message, $code );
|
|
}
|
|
}
|
|
|
|
?>
|
|
|