1*7f8f2456SAndreas Gohr<?php 2*7f8f2456SAndreas Gohrnamespace IXR\Client; 3*7f8f2456SAndreas Gohr 4*7f8f2456SAndreas Gohruse IXR\Exception\ClientException; 5*7f8f2456SAndreas Gohruse IXR\Message\Message; 6*7f8f2456SAndreas Gohruse IXR\Request\Request; 7*7f8f2456SAndreas Gohr 8*7f8f2456SAndreas Gohr/** 9*7f8f2456SAndreas Gohr * Client for communicating with a XML-RPC Server over HTTPS. 10*7f8f2456SAndreas Gohr * 11*7f8f2456SAndreas Gohr * @author Jason Stirk <jstirk@gmm.com.au> (@link http://blog.griffin.homelinux.org/projects/xmlrpc/) 12*7f8f2456SAndreas Gohr * @version 0.2.0 26May2005 08:34 +0800 13*7f8f2456SAndreas Gohr * @copyright (c) 2004-2005 Jason Stirk 14*7f8f2456SAndreas Gohr * @package IXR 15*7f8f2456SAndreas Gohr */ 16*7f8f2456SAndreas Gohrclass ClientSSL extends Client 17*7f8f2456SAndreas Gohr{ 18*7f8f2456SAndreas Gohr /** 19*7f8f2456SAndreas Gohr * Filename of the SSL Client Certificate 20*7f8f2456SAndreas Gohr * @access private 21*7f8f2456SAndreas Gohr * @since 0.1.0 22*7f8f2456SAndreas Gohr * @var string 23*7f8f2456SAndreas Gohr */ 24*7f8f2456SAndreas Gohr private $_certFile; 25*7f8f2456SAndreas Gohr 26*7f8f2456SAndreas Gohr /** 27*7f8f2456SAndreas Gohr * Filename of the SSL CA Certificate 28*7f8f2456SAndreas Gohr * @access private 29*7f8f2456SAndreas Gohr * @since 0.1.0 30*7f8f2456SAndreas Gohr * @var string 31*7f8f2456SAndreas Gohr */ 32*7f8f2456SAndreas Gohr private $_caFile; 33*7f8f2456SAndreas Gohr 34*7f8f2456SAndreas Gohr /** 35*7f8f2456SAndreas Gohr * Filename of the SSL Client Private Key 36*7f8f2456SAndreas Gohr * @access private 37*7f8f2456SAndreas Gohr * @since 0.1.0 38*7f8f2456SAndreas Gohr * @var string 39*7f8f2456SAndreas Gohr */ 40*7f8f2456SAndreas Gohr private $_keyFile; 41*7f8f2456SAndreas Gohr 42*7f8f2456SAndreas Gohr /** 43*7f8f2456SAndreas Gohr * Passphrase to unlock the private key 44*7f8f2456SAndreas Gohr * @access private 45*7f8f2456SAndreas Gohr * @since 0.1.0 46*7f8f2456SAndreas Gohr * @var string 47*7f8f2456SAndreas Gohr */ 48*7f8f2456SAndreas Gohr private $_passphrase; 49*7f8f2456SAndreas Gohr 50*7f8f2456SAndreas Gohr /** 51*7f8f2456SAndreas Gohr * Constructor 52*7f8f2456SAndreas Gohr * @param string $server URL of the Server to connect to 53*7f8f2456SAndreas Gohr * @since 0.1.0 54*7f8f2456SAndreas Gohr */ 55*7f8f2456SAndreas Gohr public function __construct($server, $path = false, $port = 443, $timeout = false, $timeout_io = null) 56*7f8f2456SAndreas Gohr { 57*7f8f2456SAndreas Gohr parent::__construct($server, $path, $port, $timeout, $timeout_io); 58*7f8f2456SAndreas Gohr $this->useragent = 'The Incutio XML-RPC PHP Library for SSL'; 59*7f8f2456SAndreas Gohr 60*7f8f2456SAndreas Gohr // Set class fields 61*7f8f2456SAndreas Gohr $this->_certFile = false; 62*7f8f2456SAndreas Gohr $this->_caFile = false; 63*7f8f2456SAndreas Gohr $this->_keyFile = false; 64*7f8f2456SAndreas Gohr $this->_passphrase = ''; 65*7f8f2456SAndreas Gohr } 66*7f8f2456SAndreas Gohr 67*7f8f2456SAndreas Gohr /** 68*7f8f2456SAndreas Gohr * Set the client side certificates to communicate with the server. 69*7f8f2456SAndreas Gohr * 70*7f8f2456SAndreas Gohr * @since 0.1.0 71*7f8f2456SAndreas Gohr * @param string $certificateFile Filename of the client side certificate to use 72*7f8f2456SAndreas Gohr * @param string $keyFile Filename of the client side certificate's private key 73*7f8f2456SAndreas Gohr * @param string $keyPhrase Passphrase to unlock the private key 74*7f8f2456SAndreas Gohr * @throws ClientException 75*7f8f2456SAndreas Gohr */ 76*7f8f2456SAndreas Gohr public function setCertificate($certificateFile, $keyFile, $keyPhrase = '') 77*7f8f2456SAndreas Gohr { 78*7f8f2456SAndreas Gohr // Check the files all exist 79*7f8f2456SAndreas Gohr if (is_file($certificateFile)) { 80*7f8f2456SAndreas Gohr $this->_certFile = $certificateFile; 81*7f8f2456SAndreas Gohr } else { 82*7f8f2456SAndreas Gohr throw new ClientException('Could not open certificate: ' . $certificateFile); 83*7f8f2456SAndreas Gohr } 84*7f8f2456SAndreas Gohr 85*7f8f2456SAndreas Gohr if (is_file($keyFile)) { 86*7f8f2456SAndreas Gohr $this->_keyFile = $keyFile; 87*7f8f2456SAndreas Gohr } else { 88*7f8f2456SAndreas Gohr throw new ClientException('Could not open private key: ' . $keyFile); 89*7f8f2456SAndreas Gohr } 90*7f8f2456SAndreas Gohr 91*7f8f2456SAndreas Gohr $this->_passphrase = (string)$keyPhrase; 92*7f8f2456SAndreas Gohr } 93*7f8f2456SAndreas Gohr 94*7f8f2456SAndreas Gohr public function setCACertificate($caFile) 95*7f8f2456SAndreas Gohr { 96*7f8f2456SAndreas Gohr if (is_file($caFile)) { 97*7f8f2456SAndreas Gohr $this->_caFile = $caFile; 98*7f8f2456SAndreas Gohr } else { 99*7f8f2456SAndreas Gohr throw new ClientException('Could not open CA certificate: ' . $caFile); 100*7f8f2456SAndreas Gohr } 101*7f8f2456SAndreas Gohr } 102*7f8f2456SAndreas Gohr 103*7f8f2456SAndreas Gohr /** 104*7f8f2456SAndreas Gohr * Sets the connection timeout (in seconds) 105*7f8f2456SAndreas Gohr * @param int $newTimeOut Timeout in seconds 106*7f8f2456SAndreas Gohr * @returns void 107*7f8f2456SAndreas Gohr * @since 0.1.2 108*7f8f2456SAndreas Gohr */ 109*7f8f2456SAndreas Gohr public function setTimeOut($newTimeOut) 110*7f8f2456SAndreas Gohr { 111*7f8f2456SAndreas Gohr $this->timeout = (int)$newTimeOut; 112*7f8f2456SAndreas Gohr } 113*7f8f2456SAndreas Gohr 114*7f8f2456SAndreas Gohr /** 115*7f8f2456SAndreas Gohr * Returns the connection timeout (in seconds) 116*7f8f2456SAndreas Gohr * @returns int 117*7f8f2456SAndreas Gohr * @since 0.1.2 118*7f8f2456SAndreas Gohr */ 119*7f8f2456SAndreas Gohr public function getTimeOut() 120*7f8f2456SAndreas Gohr { 121*7f8f2456SAndreas Gohr return $this->timeout; 122*7f8f2456SAndreas Gohr } 123*7f8f2456SAndreas Gohr 124*7f8f2456SAndreas Gohr /** 125*7f8f2456SAndreas Gohr * Set the query to send to the XML-RPC Server 126*7f8f2456SAndreas Gohr * @since 0.1.0 127*7f8f2456SAndreas Gohr */ 128*7f8f2456SAndreas Gohr public function query() 129*7f8f2456SAndreas Gohr { 130*7f8f2456SAndreas Gohr $args = func_get_args(); 131*7f8f2456SAndreas Gohr $method = array_shift($args); 132*7f8f2456SAndreas Gohr $request = new Request($method, $args); 133*7f8f2456SAndreas Gohr $length = $request->getLength(); 134*7f8f2456SAndreas Gohr $xml = $request->getXml(); 135*7f8f2456SAndreas Gohr 136*7f8f2456SAndreas Gohr $this->debugOutput('<pre>' . htmlspecialchars($xml) . PHP_EOL . '</pre>'); 137*7f8f2456SAndreas Gohr 138*7f8f2456SAndreas Gohr //This is where we deviate from the normal query() 139*7f8f2456SAndreas Gohr //Rather than open a normal sock, we will actually use the cURL 140*7f8f2456SAndreas Gohr //extensions to make the calls, and handle the SSL stuff. 141*7f8f2456SAndreas Gohr 142*7f8f2456SAndreas Gohr $curl = curl_init('https://' . $this->server . $this->path); 143*7f8f2456SAndreas Gohr 144*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 145*7f8f2456SAndreas Gohr 146*7f8f2456SAndreas Gohr //Since 23Jun2004 (0.1.2) - Made timeout a class field 147*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeout); 148*7f8f2456SAndreas Gohr if (null !== $this->timeout_io) { 149*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout_io); 150*7f8f2456SAndreas Gohr } 151*7f8f2456SAndreas Gohr 152*7f8f2456SAndreas Gohr if ($this->debug) { 153*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_VERBOSE, 1); 154*7f8f2456SAndreas Gohr } 155*7f8f2456SAndreas Gohr 156*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_HEADER, 1); 157*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_POST, 1); 158*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); 159*7f8f2456SAndreas Gohr if($this->port !== 443) { 160*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_PORT, $this->port); 161*7f8f2456SAndreas Gohr } 162*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_HTTPHEADER, [ 163*7f8f2456SAndreas Gohr "Content-Type: text/xml", 164*7f8f2456SAndreas Gohr "Content-length: {$length}" 165*7f8f2456SAndreas Gohr ]); 166*7f8f2456SAndreas Gohr 167*7f8f2456SAndreas Gohr // Process the SSL certificates, etc. to use 168*7f8f2456SAndreas Gohr if (!($this->_certFile === false)) { 169*7f8f2456SAndreas Gohr // We have a certificate file set, so add these to the cURL handler 170*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_SSLCERT, $this->_certFile); 171*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_SSLKEY, $this->_keyFile); 172*7f8f2456SAndreas Gohr 173*7f8f2456SAndreas Gohr if ($this->debug) { 174*7f8f2456SAndreas Gohr $this->debugOutput('SSL Cert at : ' . $this->_certFile); 175*7f8f2456SAndreas Gohr $this->debugOutput('SSL Key at : ' . $this->_keyFile); 176*7f8f2456SAndreas Gohr } 177*7f8f2456SAndreas Gohr 178*7f8f2456SAndreas Gohr // See if we need to give a passphrase 179*7f8f2456SAndreas Gohr if (!($this->_passphrase === '')) { 180*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $this->_passphrase); 181*7f8f2456SAndreas Gohr } 182*7f8f2456SAndreas Gohr 183*7f8f2456SAndreas Gohr if ($this->_caFile === false) { 184*7f8f2456SAndreas Gohr // Don't verify their certificate, as we don't have a CA to verify against 185*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); 186*7f8f2456SAndreas Gohr } else { 187*7f8f2456SAndreas Gohr // Verify against a CA 188*7f8f2456SAndreas Gohr curl_setopt($curl, CURLOPT_CAINFO, $this->_caFile); 189*7f8f2456SAndreas Gohr } 190*7f8f2456SAndreas Gohr } 191*7f8f2456SAndreas Gohr 192*7f8f2456SAndreas Gohr // Call cURL to do it's stuff and return us the content 193*7f8f2456SAndreas Gohr $contents = curl_exec($curl); 194*7f8f2456SAndreas Gohr curl_close($curl); 195*7f8f2456SAndreas Gohr 196*7f8f2456SAndreas Gohr // Check for 200 Code in $contents 197*7f8f2456SAndreas Gohr if (!strstr($contents, '200 OK')) { 198*7f8f2456SAndreas Gohr //There was no "200 OK" returned - we failed 199*7f8f2456SAndreas Gohr return $this->handleError(-32300, 'transport error - HTTP status code was not 200'); 200*7f8f2456SAndreas Gohr } 201*7f8f2456SAndreas Gohr 202*7f8f2456SAndreas Gohr if ($this->debug) { 203*7f8f2456SAndreas Gohr $this->debugOutput('<pre>' . htmlspecialchars($contents) . PHP_EOL . '</pre>'); 204*7f8f2456SAndreas Gohr } 205*7f8f2456SAndreas Gohr // Now parse what we've got back 206*7f8f2456SAndreas Gohr // Since 20Jun2004 (0.1.1) - We need to remove the headers first 207*7f8f2456SAndreas Gohr // Why I have only just found this, I will never know... 208*7f8f2456SAndreas Gohr // So, remove everything before the first < 209*7f8f2456SAndreas Gohr $contents = substr($contents, strpos($contents, '<')); 210*7f8f2456SAndreas Gohr 211*7f8f2456SAndreas Gohr $this->message = new Message($contents); 212*7f8f2456SAndreas Gohr if (!$this->message->parse()) { 213*7f8f2456SAndreas Gohr // XML error 214*7f8f2456SAndreas Gohr return $this->handleError(-32700, 'parse error. not well formed'); 215*7f8f2456SAndreas Gohr } 216*7f8f2456SAndreas Gohr // Is the message a fault? 217*7f8f2456SAndreas Gohr if ($this->message->messageType == 'fault') { 218*7f8f2456SAndreas Gohr return $this->handleError($this->message->faultCode, $this->message->faultString); 219*7f8f2456SAndreas Gohr } 220*7f8f2456SAndreas Gohr 221*7f8f2456SAndreas Gohr // Message must be OK 222*7f8f2456SAndreas Gohr return true; 223*7f8f2456SAndreas Gohr } 224*7f8f2456SAndreas Gohr 225*7f8f2456SAndreas Gohr /** 226*7f8f2456SAndreas Gohr * Debug output, if debug is enabled 227*7f8f2456SAndreas Gohr * @param $message 228*7f8f2456SAndreas Gohr */ 229*7f8f2456SAndreas Gohr private function debugOutput($message) 230*7f8f2456SAndreas Gohr { 231*7f8f2456SAndreas Gohr if ($this->debug) { 232*7f8f2456SAndreas Gohr echo $message . PHP_EOL; 233*7f8f2456SAndreas Gohr } 234*7f8f2456SAndreas Gohr } 235*7f8f2456SAndreas Gohr} 236