aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/vendor/kissifrot/php-ixr/src/Client/ClientSSL.php
blob: 7bbd5a5511037efb39ec240223e05a1d98c8af60 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<?php
namespace IXR\Client;

use IXR\Exception\ClientException;
use IXR\Message\Message;
use IXR\Request\Request;

/**
 * Client for communicating with a XML-RPC Server over HTTPS.
 *
 * @author Jason Stirk <jstirk@gmm.com.au> (@link http://blog.griffin.homelinux.org/projects/xmlrpc/)
 * @version 0.2.0 26May2005 08:34 +0800
 * @copyright (c) 2004-2005 Jason Stirk
 * @package IXR
 */
class ClientSSL extends Client
{
    /**
     * Filename of the SSL Client Certificate
     * @access private
     * @since 0.1.0
     * @var string
     */
    private $_certFile;

    /**
     * Filename of the SSL CA Certificate
     * @access private
     * @since 0.1.0
     * @var string
     */
    private $_caFile;

    /**
     * Filename of the SSL Client Private Key
     * @access private
     * @since 0.1.0
     * @var string
     */
    private $_keyFile;

    /**
     * Passphrase to unlock the private key
     * @access private
     * @since 0.1.0
     * @var string
     */
    private $_passphrase;

    /**
     * Constructor
     * @param string $server URL of the Server to connect to
     * @since 0.1.0
     */
    public function __construct($server, $path = false, $port = 443, $timeout = false, $timeout_io = null)
    {
        parent::__construct($server, $path, $port, $timeout, $timeout_io);
        $this->useragent = 'The Incutio XML-RPC PHP Library for SSL';

        // Set class fields
        $this->_certFile = false;
        $this->_caFile = false;
        $this->_keyFile = false;
        $this->_passphrase = '';
    }

    /**
     * Set the client side certificates to communicate with the server.
     *
     * @since 0.1.0
     * @param string $certificateFile Filename of the client side certificate to use
     * @param string $keyFile         Filename of the client side certificate's private key
     * @param string $keyPhrase       Passphrase to unlock the private key
     * @throws ClientException
     */
    public function setCertificate($certificateFile, $keyFile, $keyPhrase = '')
    {
        // Check the files all exist
        if (is_file($certificateFile)) {
            $this->_certFile = $certificateFile;
        } else {
            throw new ClientException('Could not open certificate: ' . $certificateFile);
        }

        if (is_file($keyFile)) {
            $this->_keyFile = $keyFile;
        } else {
            throw new ClientException('Could not open private key: ' . $keyFile);
        }

        $this->_passphrase = (string)$keyPhrase;
    }

    public function setCACertificate($caFile)
    {
        if (is_file($caFile)) {
            $this->_caFile = $caFile;
        } else {
            throw new ClientException('Could not open CA certificate: ' . $caFile);
        }
    }

    /**
     * Sets the connection timeout (in seconds)
     * @param int $newTimeOut Timeout in seconds
     * @returns void
     * @since 0.1.2
     */
    public function setTimeOut($newTimeOut)
    {
        $this->timeout = (int)$newTimeOut;
    }

    /**
     * Returns the connection timeout (in seconds)
     * @returns int
     * @since 0.1.2
     */
    public function getTimeOut()
    {
        return $this->timeout;
    }

    /**
     * Set the query to send to the XML-RPC Server
     * @since 0.1.0
     */
    public function query()
    {
        $args = func_get_args();
        $method = array_shift($args);
        $request = new Request($method, $args);
        $length = $request->getLength();
        $xml = $request->getXml();

        $this->debugOutput('<pre>' . htmlspecialchars($xml) . PHP_EOL . '</pre>');

        //This is where we deviate from the normal query()
        //Rather than open a normal sock, we will actually use the cURL
        //extensions to make the calls, and handle the SSL stuff.

        $curl = curl_init('https://' . $this->server . $this->path);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

        //Since 23Jun2004 (0.1.2) - Made timeout a class field
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeout);
        if (null !== $this->timeout_io) {
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout_io);
        }

        if ($this->debug) {
            curl_setopt($curl, CURLOPT_VERBOSE, 1);
        }

        curl_setopt($curl, CURLOPT_HEADER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
        if($this->port !== 443) {
            curl_setopt($curl, CURLOPT_PORT, $this->port);
        }
        curl_setopt($curl, CURLOPT_HTTPHEADER, [
            "Content-Type: text/xml",
            "Content-length: {$length}"
        ]);

        // Process the SSL certificates, etc. to use
        if (!($this->_certFile === false)) {
            // We have a certificate file set, so add these to the cURL handler
            curl_setopt($curl, CURLOPT_SSLCERT, $this->_certFile);
            curl_setopt($curl, CURLOPT_SSLKEY, $this->_keyFile);

            if ($this->debug) {
                $this->debugOutput('SSL Cert at : ' . $this->_certFile);
                $this->debugOutput('SSL Key at : ' . $this->_keyFile);
            }

            // See if we need to give a passphrase
            if (!($this->_passphrase === '')) {
                curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $this->_passphrase);
            }

            if ($this->_caFile === false) {
                // Don't verify their certificate, as we don't have a CA to verify against
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
            } else {
                // Verify against a CA
                curl_setopt($curl, CURLOPT_CAINFO, $this->_caFile);
            }
        }

        // Call cURL to do it's stuff and return us the content
        $contents = curl_exec($curl);
        curl_close($curl);

        // Check for 200 Code in $contents
        if (!strstr($contents, '200 OK')) {
            //There was no "200 OK" returned - we failed
            return $this->handleError(-32300, 'transport error - HTTP status code was not 200');
        }

        if ($this->debug) {
            $this->debugOutput('<pre>' . htmlspecialchars($contents) . PHP_EOL . '</pre>');
        }
        // Now parse what we've got back
        // Since 20Jun2004 (0.1.1) - We need to remove the headers first
        // Why I have only just found this, I will never know...
        // So, remove everything before the first <
        $contents = substr($contents, strpos($contents, '<'));

        $this->message = new Message($contents);
        if (!$this->message->parse()) {
            // XML error
            return $this->handleError(-32700, 'parse error. not well formed');
        }
        // Is the message a fault?
        if ($this->message->messageType == 'fault') {
            return $this->handleError($this->message->faultCode, $this->message->faultString);
        }

        // Message must be OK
        return true;
    }

    /**
     * Debug output, if debug is enabled
     * @param $message
     */
    private function debugOutput($message)
    {
        if ($this->debug) {
            echo $message . PHP_EOL;
        }
    }
}