xref: /dokuwiki/vendor/kissifrot/php-ixr/src/Client/ClientSSL.php (revision 7f8f24562b596c56d79e46eba9f82780df5725cb)
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