xref: /dokuwiki/vendor/kissifrot/php-ixr/src/Client/ClientSSL.php (revision 934f970a961272cf366d019f15b7c0685de4fc7e)
17f8f2456SAndreas Gohr<?php
27f8f2456SAndreas Gohrnamespace IXR\Client;
37f8f2456SAndreas Gohr
47f8f2456SAndreas Gohruse IXR\Exception\ClientException;
57f8f2456SAndreas Gohruse IXR\Message\Message;
67f8f2456SAndreas Gohruse IXR\Request\Request;
77f8f2456SAndreas Gohr
87f8f2456SAndreas Gohr/**
97f8f2456SAndreas Gohr * Client for communicating with a XML-RPC Server over HTTPS.
107f8f2456SAndreas Gohr *
117f8f2456SAndreas Gohr * @author Jason Stirk <jstirk@gmm.com.au> (@link http://blog.griffin.homelinux.org/projects/xmlrpc/)
127f8f2456SAndreas Gohr * @version 0.2.0 26May2005 08:34 +0800
137f8f2456SAndreas Gohr * @copyright (c) 2004-2005 Jason Stirk
147f8f2456SAndreas Gohr * @package IXR
157f8f2456SAndreas Gohr */
167f8f2456SAndreas Gohrclass ClientSSL extends Client
177f8f2456SAndreas Gohr{
187f8f2456SAndreas Gohr    /**
197f8f2456SAndreas Gohr     * Filename of the SSL Client Certificate
207f8f2456SAndreas Gohr     * @access private
217f8f2456SAndreas Gohr     * @since 0.1.0
227f8f2456SAndreas Gohr     * @var string
237f8f2456SAndreas Gohr     */
247f8f2456SAndreas Gohr    private $_certFile;
257f8f2456SAndreas Gohr
267f8f2456SAndreas Gohr    /**
277f8f2456SAndreas Gohr     * Filename of the SSL CA Certificate
287f8f2456SAndreas Gohr     * @access private
297f8f2456SAndreas Gohr     * @since 0.1.0
307f8f2456SAndreas Gohr     * @var string
317f8f2456SAndreas Gohr     */
327f8f2456SAndreas Gohr    private $_caFile;
337f8f2456SAndreas Gohr
347f8f2456SAndreas Gohr    /**
357f8f2456SAndreas Gohr     * Filename of the SSL Client Private Key
367f8f2456SAndreas Gohr     * @access private
377f8f2456SAndreas Gohr     * @since 0.1.0
387f8f2456SAndreas Gohr     * @var string
397f8f2456SAndreas Gohr     */
407f8f2456SAndreas Gohr    private $_keyFile;
417f8f2456SAndreas Gohr
427f8f2456SAndreas Gohr    /**
437f8f2456SAndreas Gohr     * Passphrase to unlock the private key
447f8f2456SAndreas Gohr     * @access private
457f8f2456SAndreas Gohr     * @since 0.1.0
467f8f2456SAndreas Gohr     * @var string
477f8f2456SAndreas Gohr     */
487f8f2456SAndreas Gohr    private $_passphrase;
497f8f2456SAndreas Gohr
507f8f2456SAndreas Gohr    /**
517f8f2456SAndreas Gohr     * Constructor
527f8f2456SAndreas Gohr     * @param string $server URL of the Server to connect to
537f8f2456SAndreas Gohr     * @since 0.1.0
547f8f2456SAndreas Gohr     */
557f8f2456SAndreas Gohr    public function __construct($server, $path = false, $port = 443, $timeout = false, $timeout_io = null)
567f8f2456SAndreas Gohr    {
577f8f2456SAndreas Gohr        parent::__construct($server, $path, $port, $timeout, $timeout_io);
587f8f2456SAndreas Gohr        $this->useragent = 'The Incutio XML-RPC PHP Library for SSL';
597f8f2456SAndreas Gohr
607f8f2456SAndreas Gohr        // Set class fields
617f8f2456SAndreas Gohr        $this->_certFile = false;
627f8f2456SAndreas Gohr        $this->_caFile = false;
637f8f2456SAndreas Gohr        $this->_keyFile = false;
647f8f2456SAndreas Gohr        $this->_passphrase = '';
657f8f2456SAndreas Gohr    }
667f8f2456SAndreas Gohr
677f8f2456SAndreas Gohr    /**
687f8f2456SAndreas Gohr     * Set the client side certificates to communicate with the server.
697f8f2456SAndreas Gohr     *
707f8f2456SAndreas Gohr     * @since 0.1.0
717f8f2456SAndreas Gohr     * @param string $certificateFile Filename of the client side certificate to use
727f8f2456SAndreas Gohr     * @param string $keyFile         Filename of the client side certificate's private key
737f8f2456SAndreas Gohr     * @param string $keyPhrase       Passphrase to unlock the private key
747f8f2456SAndreas Gohr     * @throws ClientException
757f8f2456SAndreas Gohr     */
767f8f2456SAndreas Gohr    public function setCertificate($certificateFile, $keyFile, $keyPhrase = '')
777f8f2456SAndreas Gohr    {
787f8f2456SAndreas Gohr        // Check the files all exist
797f8f2456SAndreas Gohr        if (is_file($certificateFile)) {
807f8f2456SAndreas Gohr            $this->_certFile = $certificateFile;
817f8f2456SAndreas Gohr        } else {
827f8f2456SAndreas Gohr            throw new ClientException('Could not open certificate: ' . $certificateFile);
837f8f2456SAndreas Gohr        }
847f8f2456SAndreas Gohr
857f8f2456SAndreas Gohr        if (is_file($keyFile)) {
867f8f2456SAndreas Gohr            $this->_keyFile = $keyFile;
877f8f2456SAndreas Gohr        } else {
887f8f2456SAndreas Gohr            throw new ClientException('Could not open private key: ' . $keyFile);
897f8f2456SAndreas Gohr        }
907f8f2456SAndreas Gohr
917f8f2456SAndreas Gohr        $this->_passphrase = (string)$keyPhrase;
927f8f2456SAndreas Gohr    }
937f8f2456SAndreas Gohr
947f8f2456SAndreas Gohr    public function setCACertificate($caFile)
957f8f2456SAndreas Gohr    {
967f8f2456SAndreas Gohr        if (is_file($caFile)) {
977f8f2456SAndreas Gohr            $this->_caFile = $caFile;
987f8f2456SAndreas Gohr        } else {
997f8f2456SAndreas Gohr            throw new ClientException('Could not open CA certificate: ' . $caFile);
1007f8f2456SAndreas Gohr        }
1017f8f2456SAndreas Gohr    }
1027f8f2456SAndreas Gohr
1037f8f2456SAndreas Gohr    /**
1047f8f2456SAndreas Gohr     * Sets the connection timeout (in seconds)
1057f8f2456SAndreas Gohr     * @param int $newTimeOut Timeout in seconds
1067f8f2456SAndreas Gohr     * @returns void
1077f8f2456SAndreas Gohr     * @since 0.1.2
1087f8f2456SAndreas Gohr     */
1097f8f2456SAndreas Gohr    public function setTimeOut($newTimeOut)
1107f8f2456SAndreas Gohr    {
1117f8f2456SAndreas Gohr        $this->timeout = (int)$newTimeOut;
1127f8f2456SAndreas Gohr    }
1137f8f2456SAndreas Gohr
1147f8f2456SAndreas Gohr    /**
1157f8f2456SAndreas Gohr     * Returns the connection timeout (in seconds)
1167f8f2456SAndreas Gohr     * @returns int
1177f8f2456SAndreas Gohr     * @since 0.1.2
1187f8f2456SAndreas Gohr     */
1197f8f2456SAndreas Gohr    public function getTimeOut()
1207f8f2456SAndreas Gohr    {
1217f8f2456SAndreas Gohr        return $this->timeout;
1227f8f2456SAndreas Gohr    }
1237f8f2456SAndreas Gohr
1247f8f2456SAndreas Gohr    /**
1257f8f2456SAndreas Gohr     * Set the query to send to the XML-RPC Server
1267f8f2456SAndreas Gohr     * @since 0.1.0
1277f8f2456SAndreas Gohr     */
1287f8f2456SAndreas Gohr    public function query()
1297f8f2456SAndreas Gohr    {
1307f8f2456SAndreas Gohr        $args = func_get_args();
1317f8f2456SAndreas Gohr        $method = array_shift($args);
1327f8f2456SAndreas Gohr        $request = new Request($method, $args);
1337f8f2456SAndreas Gohr        $length = $request->getLength();
1347f8f2456SAndreas Gohr        $xml = $request->getXml();
1357f8f2456SAndreas Gohr
1367f8f2456SAndreas Gohr        $this->debugOutput('<pre>' . htmlspecialchars($xml) . PHP_EOL . '</pre>');
1377f8f2456SAndreas Gohr
1387f8f2456SAndreas Gohr        //This is where we deviate from the normal query()
1397f8f2456SAndreas Gohr        //Rather than open a normal sock, we will actually use the cURL
1407f8f2456SAndreas Gohr        //extensions to make the calls, and handle the SSL stuff.
1417f8f2456SAndreas Gohr
1427f8f2456SAndreas Gohr        $curl = curl_init('https://' . $this->server . $this->path);
1437f8f2456SAndreas Gohr
1447f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1457f8f2456SAndreas Gohr
1467f8f2456SAndreas Gohr        //Since 23Jun2004 (0.1.2) - Made timeout a class field
1477f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeout);
1487f8f2456SAndreas Gohr        if (null !== $this->timeout_io) {
1497f8f2456SAndreas Gohr            curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout_io);
1507f8f2456SAndreas Gohr        }
1517f8f2456SAndreas Gohr
1527f8f2456SAndreas Gohr        if ($this->debug) {
1537f8f2456SAndreas Gohr            curl_setopt($curl, CURLOPT_VERBOSE, 1);
1547f8f2456SAndreas Gohr        }
1557f8f2456SAndreas Gohr
1567f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_HEADER, 1);
1577f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_POST, 1);
1587f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
1597f8f2456SAndreas Gohr        if($this->port !== 443) {
1607f8f2456SAndreas Gohr            curl_setopt($curl, CURLOPT_PORT, $this->port);
1617f8f2456SAndreas Gohr        }
1627f8f2456SAndreas Gohr        curl_setopt($curl, CURLOPT_HTTPHEADER, [
1637f8f2456SAndreas Gohr            "Content-Type: text/xml",
1647f8f2456SAndreas Gohr            "Content-length: {$length}"
1657f8f2456SAndreas Gohr        ]);
1667f8f2456SAndreas Gohr
1677f8f2456SAndreas Gohr        // Process the SSL certificates, etc. to use
1687f8f2456SAndreas Gohr        if (!($this->_certFile === false)) {
1697f8f2456SAndreas Gohr            // We have a certificate file set, so add these to the cURL handler
1707f8f2456SAndreas Gohr            curl_setopt($curl, CURLOPT_SSLCERT, $this->_certFile);
1717f8f2456SAndreas Gohr            curl_setopt($curl, CURLOPT_SSLKEY, $this->_keyFile);
1727f8f2456SAndreas Gohr
1737f8f2456SAndreas Gohr            if ($this->debug) {
1747f8f2456SAndreas Gohr                $this->debugOutput('SSL Cert at : ' . $this->_certFile);
1757f8f2456SAndreas Gohr                $this->debugOutput('SSL Key at : ' . $this->_keyFile);
1767f8f2456SAndreas Gohr            }
1777f8f2456SAndreas Gohr
1787f8f2456SAndreas Gohr            // See if we need to give a passphrase
1797f8f2456SAndreas Gohr            if (!($this->_passphrase === '')) {
1807f8f2456SAndreas Gohr                curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $this->_passphrase);
1817f8f2456SAndreas Gohr            }
1827f8f2456SAndreas Gohr
1837f8f2456SAndreas Gohr            if ($this->_caFile === false) {
1847f8f2456SAndreas Gohr                // Don't verify their certificate, as we don't have a CA to verify against
1857f8f2456SAndreas Gohr                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
1867f8f2456SAndreas Gohr            } else {
1877f8f2456SAndreas Gohr                // Verify against a CA
1887f8f2456SAndreas Gohr                curl_setopt($curl, CURLOPT_CAINFO, $this->_caFile);
1897f8f2456SAndreas Gohr            }
1907f8f2456SAndreas Gohr        }
1917f8f2456SAndreas Gohr
1927f8f2456SAndreas Gohr        // Call cURL to do it's stuff and return us the content
1937f8f2456SAndreas Gohr        $contents = curl_exec($curl);
1947f8f2456SAndreas Gohr        curl_close($curl);
1957f8f2456SAndreas Gohr
1967f8f2456SAndreas Gohr        // Check for 200 Code in $contents
197*934f970aSAndreas Gohr        if (!strstr($contents, '200 OK') && !strstr($contents, 'HTTP/2 200')) {
1987f8f2456SAndreas Gohr            //There was no "200 OK" returned - we failed
1997f8f2456SAndreas Gohr            return $this->handleError(-32300, 'transport error - HTTP status code was not 200');
2007f8f2456SAndreas Gohr        }
2017f8f2456SAndreas Gohr
2027f8f2456SAndreas Gohr        if ($this->debug) {
2037f8f2456SAndreas Gohr            $this->debugOutput('<pre>' . htmlspecialchars($contents) . PHP_EOL . '</pre>');
2047f8f2456SAndreas Gohr        }
2057f8f2456SAndreas Gohr        // Now parse what we've got back
2067f8f2456SAndreas Gohr        // Since 20Jun2004 (0.1.1) - We need to remove the headers first
2077f8f2456SAndreas Gohr        // Why I have only just found this, I will never know...
2087f8f2456SAndreas Gohr        // So, remove everything before the first <
2097f8f2456SAndreas Gohr        $contents = substr($contents, strpos($contents, '<'));
2107f8f2456SAndreas Gohr
2117f8f2456SAndreas Gohr        $this->message = new Message($contents);
2127f8f2456SAndreas Gohr        if (!$this->message->parse()) {
2137f8f2456SAndreas Gohr            // XML error
2147f8f2456SAndreas Gohr            return $this->handleError(-32700, 'parse error. not well formed');
2157f8f2456SAndreas Gohr        }
2167f8f2456SAndreas Gohr        // Is the message a fault?
2177f8f2456SAndreas Gohr        if ($this->message->messageType == 'fault') {
2187f8f2456SAndreas Gohr            return $this->handleError($this->message->faultCode, $this->message->faultString);
2197f8f2456SAndreas Gohr        }
2207f8f2456SAndreas Gohr
2217f8f2456SAndreas Gohr        // Message must be OK
2227f8f2456SAndreas Gohr        return true;
2237f8f2456SAndreas Gohr    }
2247f8f2456SAndreas Gohr
2257f8f2456SAndreas Gohr    /**
2267f8f2456SAndreas Gohr     * Debug output, if debug is enabled
2277f8f2456SAndreas Gohr     * @param $message
2287f8f2456SAndreas Gohr     */
2297f8f2456SAndreas Gohr    private function debugOutput($message)
2307f8f2456SAndreas Gohr    {
2317f8f2456SAndreas Gohr        if ($this->debug) {
2327f8f2456SAndreas Gohr            echo $message . PHP_EOL;
2337f8f2456SAndreas Gohr        }
2347f8f2456SAndreas Gohr    }
2357f8f2456SAndreas Gohr}
236