1 | <?php |
||
2 | |||
3 | namespace PhpXmlRpc; |
||
4 | |||
5 | use PhpXmlRpc\Exception\ValueErrorException; |
||
6 | use PhpXmlRpc\Helper\XMLParser; |
||
7 | use PhpXmlRpc\Traits\CharsetEncoderAware; |
||
8 | use PhpXmlRpc\Traits\DeprecationLogger; |
||
9 | |||
10 | /** |
||
11 | * Used to represent a client of an XML-RPC server. |
||
12 | * |
||
13 | * @property int $errno deprecated - public access left in purely for BC. |
||
14 | * @property string $errstr deprecated - public access left in purely for BC. |
||
15 | * @property string $method deprecated - public access left in purely for BC. Access via getUrl()/__construct() |
||
16 | * @property string $server deprecated - public access left in purely for BC. Access via getUrl()/__construct() |
||
17 | * @property int $port deprecated - public access left in purely for BC. Access via getUrl()/__construct() |
||
18 | * @property string $path deprecated - public access left in purely for BC. Access via getUrl()/__construct() |
||
19 | */ |
||
20 | class Client |
||
21 | { |
||
22 | use DeprecationLogger; |
||
23 | //use CharsetEncoderAware; |
||
24 | |||
25 | const USE_CURL_NEVER = 0; |
||
26 | const USE_CURL_ALWAYS = 1; |
||
27 | const USE_CURL_AUTO = 2; |
||
28 | |||
29 | const OPT_ACCEPTED_CHARSET_ENCODINGS = 'accepted_charset_encodings'; |
||
30 | const OPT_ACCEPTED_COMPRESSION = 'accepted_compression'; |
||
31 | const OPT_AUTH_TYPE = 'authtype'; |
||
32 | const OPT_CA_CERT = 'cacert'; |
||
33 | const OPT_CA_CERT_DIR = 'cacertdir'; |
||
34 | const OPT_CERT = 'cert'; |
||
35 | const OPT_CERT_PASS = 'certpass'; |
||
36 | const OPT_COOKIES = 'cookies'; |
||
37 | const OPT_DEBUG = 'debug'; |
||
38 | const OPT_EXTRA_CURL_OPTS = 'extracurlopts'; |
||
39 | const OPT_EXTRA_SOCKET_OPTS = 'extrasockopts'; |
||
40 | const OPT_KEEPALIVE = 'keepalive'; |
||
41 | const OPT_KEY = 'key'; |
||
42 | const OPT_KEY_PASS = 'keypass'; |
||
43 | const OPT_NO_MULTICALL = 'no_multicall'; |
||
44 | const OPT_PASSWORD = 'password'; |
||
45 | const OPT_PROXY = 'proxy'; |
||
46 | const OPT_PROXY_AUTH_TYPE = 'proxy_authtype'; |
||
47 | const OPT_PROXY_PASS = 'proxy_pass'; |
||
48 | const OPT_PROXY_PORT = 'proxyport'; |
||
49 | const OPT_PROXY_USER = 'proxy_user'; |
||
50 | const OPT_REQUEST_CHARSET_ENCODING = 'request_charset_encoding'; |
||
51 | const OPT_REQUEST_COMPRESSION = 'request_compression'; |
||
52 | const OPT_RETURN_TYPE = 'return_type'; |
||
53 | const OPT_SSL_VERSION = 'sslversion'; |
||
54 | const OPT_TIMEOUT = 'timeout'; |
||
55 | const OPT_USERNAME = 'username'; |
||
56 | const OPT_USER_AGENT = 'user_agent'; |
||
57 | const OPT_USE_CURL = 'use_curl'; |
||
58 | const OPT_VERIFY_HOST = 'verifyhost'; |
||
59 | const OPT_VERIFY_PEER = 'verifypeer'; |
||
60 | const OPT_EXTRA_HEADERS = 'extra_headers'; |
||
61 | |||
62 | /** @var string */ |
||
63 | protected static $requestClass = '\\PhpXmlRpc\\Request'; |
||
64 | /** @var string */ |
||
65 | protected static $responseClass = '\\PhpXmlRpc\\Response'; |
||
66 | |||
67 | /** |
||
68 | * @var int |
||
69 | * @deprecated will be removed in the future |
||
70 | */ |
||
71 | protected $errno; |
||
72 | /** |
||
73 | * @var string |
||
74 | * @deprecated will be removed in the future |
||
75 | */ |
||
76 | protected $errstr; |
||
77 | |||
78 | /// @todo: do all the ones below need to be public? |
||
79 | |||
80 | /** |
||
81 | * @var string |
||
82 | */ |
||
83 | protected $method = 'http'; |
||
84 | /** |
||
85 | * @var string |
||
86 | */ |
||
87 | protected $server; |
||
88 | /** |
||
89 | * @var int |
||
90 | */ |
||
91 | protected $port = 0; |
||
92 | /** |
||
93 | * @var string |
||
94 | */ |
||
95 | protected $path; |
||
96 | |||
97 | /** |
||
98 | * @var int |
||
99 | */ |
||
100 | protected $debug = 0; |
||
101 | /** |
||
102 | * @var string |
||
103 | */ |
||
104 | protected $username = ''; |
||
105 | /** |
||
106 | * @var string |
||
107 | */ |
||
108 | protected $password = ''; |
||
109 | /** |
||
110 | * @var int |
||
111 | */ |
||
112 | protected $authtype = 1; |
||
113 | /** |
||
114 | * @var string |
||
115 | */ |
||
116 | protected $cert = ''; |
||
117 | /** |
||
118 | * @var string |
||
119 | */ |
||
120 | protected $certpass = ''; |
||
121 | /** |
||
122 | 2 | * @var string |
|
123 | */ |
||
124 | 2 | protected $cacert = ''; |
|
125 | 2 | /** |
|
126 | * @var string |
||
127 | 2 | */ |
|
128 | protected $cacertdir = ''; |
||
129 | /** |
||
130 | * @var string |
||
131 | */ |
||
132 | protected $key = ''; |
||
133 | /** |
||
134 | * @var string |
||
135 | */ |
||
136 | protected $keypass = ''; |
||
137 | /** |
||
138 | * @var bool |
||
139 | */ |
||
140 | protected $verifypeer = true; |
||
141 | /** |
||
142 | * @var int |
||
143 | */ |
||
144 | protected $verifyhost = 2; |
||
145 | /** |
||
146 | * @var int Corresponds to CURL_SSLVERSION_DEFAULT. Other CURL_SSLVERSION_ values are supported when in curl mode, |
||
147 | * and in socket mode different values from 0 to 7, with old php versions not supporting all of them |
||
148 | * (php 5.4 and 5.5 not supporting any in fact) |
||
149 | */ |
||
150 | protected $sslversion = 0; // |
||
151 | /** |
||
152 | 718 | * @var string |
|
153 | */ |
||
154 | protected $proxy = ''; |
||
155 | 718 | /** |
|
156 | 7 | * @var int |
|
157 | 7 | */ |
|
158 | 7 | protected $proxyport = 0; |
|
159 | 7 | /** |
|
160 | * @var string |
||
161 | */ |
||
162 | 7 | protected $proxy_user = ''; |
|
163 | /** |
||
164 | * @var string |
||
165 | 7 | */ |
|
166 | protected $proxy_pass = ''; |
||
167 | /** |
||
168 | 7 | * @var int |
|
169 | 7 | */ |
|
170 | protected $proxy_authtype = 1; |
||
171 | 7 | /** |
|
172 | * @var array |
||
173 | */ |
||
174 | 7 | protected $cookies = array(); |
|
175 | /** |
||
176 | * @var array |
||
177 | */ |
||
178 | 718 | protected $extrasockopts = array(); |
|
179 | /** |
||
180 | * @var array |
||
181 | 718 | */ |
|
182 | protected $extracurlopts = array(); |
||
183 | 718 | /** |
|
184 | 718 | * @var int |
|
185 | 3 | */ |
|
186 | protected $timeout = 0; |
||
187 | 718 | /** |
|
188 | 7 | * @var int |
|
189 | */ |
||
190 | protected $use_curl = self::USE_CURL_AUTO; |
||
191 | /** |
||
192 | 718 | * @var bool |
|
193 | * |
||
194 | 711 | * This determines whether the multicall() method will try to take advantage of the system.multicall xml-rpc method |
|
195 | * to dispatch to the server an array of requests in a single http roundtrip or simply execute many consecutive http |
||
196 | * calls. Defaults to FALSE, but it will be enabled automatically on the first failure of execution of |
||
197 | 718 | * system.multicall. |
|
198 | */ |
||
199 | protected $no_multicall = false; |
||
200 | /** |
||
201 | 718 | * @var array |
|
202 | * |
||
203 | * List of http compression methods accepted by the client for responses. |
||
204 | 718 | * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib. |
|
205 | * |
||
206 | * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since in those cases it will be up to CURL to |
||
207 | * decide the compression methods it supports. You might check for the presence of 'zlib' in the output of |
||
208 | * curl_version() to determine whether compression is supported or not |
||
209 | */ |
||
210 | protected $accepted_compression = array(); |
||
211 | /** |
||
212 | * @var string|null |
||
213 | * |
||
214 | * Name of compression scheme to be used for sending requests. |
||
215 | * Either null, 'gzip' or 'deflate'. |
||
216 | */ |
||
217 | 718 | protected $request_compression = ''; |
|
218 | 718 | /** |
|
219 | * @var bool |
||
220 | * |
||
221 | * Whether to use persistent connections for http 1.1 and https. Value set at constructor time. |
||
222 | */ |
||
223 | protected $keepalive = false; |
||
224 | /** |
||
225 | * @var string[] |
||
226 | * |
||
227 | * Charset encodings that can be decoded without problems by the client. Value set at constructor time |
||
228 | */ |
||
229 | protected $accepted_charset_encodings = array(); |
||
230 | /** |
||
231 | * @var string |
||
232 | * |
||
233 | 714 | * The charset encoding that will be used for serializing request sent by the client. |
|
234 | * It defaults to NULL, which means using US-ASCII and encoding all characters outside the ASCII printable range |
||
235 | 714 | * using their xml character entity representation (this has the benefit that line end characters will not be mangled |
|
236 | 714 | * in the transfer, a CR-LF will be preserved as well as a singe LF). |
|
237 | * Valid values are 'US-ASCII', 'UTF-8' and 'ISO-8859-1'. |
||
238 | * For the fastest mode of operation, set your both your app internal encoding and this to UTF-8. |
||
239 | */ |
||
240 | protected $request_charset_encoding = ''; |
||
241 | /** |
||
242 | * @var string |
||
243 | * |
||
244 | * Decides the content of Response objects returned by calls to send() and multicall(). |
||
245 | * Valid values are 'xmlrpcvals', 'phpvals' or 'xml'. |
||
246 | * |
||
247 | * Determines whether the value returned inside a Response object as results of calls to the send() and multicall() |
||
248 | * methods will be a Value object, a plain php value or a raw xml string. |
||
249 | * Allowed values are 'xmlrpcvals' (the default), 'phpvals' and 'xml'. |
||
250 | * To allow the user to differentiate between a correct and a faulty response, fault responses will be returned as |
||
251 | * Response objects in any case. |
||
252 | 66 | * Note that the 'phpvals' setting will yield faster execution times, but some of the information from the original |
|
253 | * response will be lost. It will be e.g. impossible to tell whether a particular php string value was sent by the |
||
254 | 66 | * server as an xml-rpc string or base64 value. |
|
255 | 66 | */ |
|
256 | 66 | protected $return_type = XMLParser::RETURN_XMLRPCVALS; |
|
257 | 66 | /** |
|
258 | * @var string |
||
259 | * |
||
260 | * Sent to servers in http headers. Value set at constructor time. |
||
261 | */ |
||
262 | protected $user_agent; |
||
263 | |||
264 | /** |
||
265 | * Additional headers to be included in the requests. |
||
266 | * |
||
267 | * @var string[] |
||
268 | */ |
||
269 | protected $extra_headers = array(); |
||
270 | |||
271 | /** |
||
272 | * CURL handle: used for keep-alive |
||
273 | * @internal |
||
274 | */ |
||
275 | public $xmlrpc_curl_handle = null; |
||
276 | |||
277 | /** |
||
278 | * @var array |
||
279 | */ |
||
280 | protected static $options = array( |
||
281 | self::OPT_ACCEPTED_CHARSET_ENCODINGS, |
||
282 | self::OPT_ACCEPTED_COMPRESSION, |
||
283 | self::OPT_AUTH_TYPE, |
||
284 | self::OPT_CA_CERT, |
||
285 | self::OPT_CA_CERT_DIR, |
||
286 | self::OPT_CERT, |
||
287 | self::OPT_CERT_PASS, |
||
288 | self::OPT_COOKIES, |
||
289 | self::OPT_DEBUG, |
||
290 | self::OPT_EXTRA_CURL_OPTS, |
||
291 | self::OPT_EXTRA_SOCKET_OPTS, |
||
292 | self::OPT_KEEPALIVE, |
||
293 | self::OPT_KEY, |
||
294 | self::OPT_KEY_PASS, |
||
295 | self::OPT_NO_MULTICALL, |
||
296 | self::OPT_PASSWORD, |
||
297 | self::OPT_PROXY, |
||
298 | self::OPT_PROXY_AUTH_TYPE, |
||
299 | self::OPT_PROXY_PASS, |
||
300 | self::OPT_PROXY_USER, |
||
301 | self::OPT_PROXY_PORT, |
||
302 | self::OPT_REQUEST_CHARSET_ENCODING, |
||
303 | self::OPT_REQUEST_COMPRESSION, |
||
304 | self::OPT_RETURN_TYPE, |
||
305 | self::OPT_SSL_VERSION, |
||
306 | self::OPT_TIMEOUT, |
||
307 | self::OPT_USE_CURL, |
||
308 | self::OPT_USER_AGENT, |
||
309 | self::OPT_USERNAME, |
||
310 | self::OPT_VERIFY_HOST, |
||
311 | self::OPT_VERIFY_PEER, |
||
312 | self::OPT_EXTRA_HEADERS, |
||
313 | ); |
||
314 | |||
315 | /** |
||
316 | 132 | * @param string $path either the PATH part of the xml-rpc server URL, or complete server URL (in which case you |
|
317 | * should use an empty string for all other parameters) |
||
318 | 132 | * e.g. /xmlrpc/server.php |
|
319 | 132 | * e.g. http://phpxmlrpc.sourceforge.net/server.php |
|
320 | * e.g. https://james:[email protected]:444/xmlrpcserver?agent=007 |
||
321 | * e.g. h2://fast-and-secure-services.org/endpoint |
||
322 | * @param string $server the server name / ip address |
||
323 | * @param integer $port the port the server is listening on, when omitted defaults to 80 or 443 depending on |
||
324 | * protocol used |
||
325 | * @param string $method the http protocol variant: defaults to 'http'; 'https', 'http11', 'h2' and 'h2c' can |
||
326 | * be used if CURL is installed. The value set here can be overridden in any call to $this->send(). |
||
327 | * Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c' |
||
328 | 132 | * for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be |
|
329 | * thus incompatible with any server/proxy not supporting http/2. This is because POST |
||
330 | 132 | * request are not compatible with h2c upgrade. |
|
331 | 132 | */ |
|
332 | public function __construct($path, $server = '', $port = '', $method = '') |
||
333 | { |
||
334 | // allow user to specify all params in $path |
||
335 | if ($server == '' && $port == '' && $method == '') { |
||
336 | $parts = parse_url($path); |
||
337 | $server = $parts['host']; |
||
338 | 132 | $path = isset($parts['path']) ? $parts['path'] : ''; |
|
339 | if (isset($parts['query'])) { |
||
340 | 132 | $path .= '?' . $parts['query']; |
|
341 | 132 | } |
|
342 | if (isset($parts['fragment'])) { |
||
343 | $path .= '#' . $parts['fragment']; |
||
344 | } |
||
345 | if (isset($parts['port'])) { |
||
346 | $port = $parts['port']; |
||
347 | } |
||
348 | if (isset($parts['scheme'])) { |
||
349 | $method = $parts['scheme']; |
||
350 | } |
||
351 | if (isset($parts['user'])) { |
||
352 | $this->username = $parts['user']; |
||
353 | } |
||
354 | if (isset($parts['pass'])) { |
||
355 | 99 | $this->password = $parts['pass']; |
|
356 | } |
||
357 | 99 | } |
|
358 | 99 | if ($path == '' || $path[0] != '/') { |
|
359 | 99 | $this->path = '/' . $path; |
|
360 | 99 | } else { |
|
361 | 99 | $this->path = $path; |
|
362 | 99 | } |
|
363 | $this->server = $server; |
||
364 | if ($port != '') { |
||
365 | $this->port = $port; |
||
366 | } |
||
367 | if ($method != '') { |
||
368 | $this->method = $method; |
||
369 | } |
||
370 | |||
371 | // if ZLIB is enabled, let the client by default accept compressed responses |
||
372 | if (function_exists('gzinflate') || ( |
||
373 | function_exists('curl_version') && (($info = curl_version()) && |
||
374 | ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version']))) |
||
375 | ) |
||
376 | ) { |
||
377 | $this->accepted_compression = array('gzip', 'deflate'); |
||
378 | } |
||
379 | |||
380 | // keepalives: enabled by default |
||
381 | $this->keepalive = true; |
||
382 | |||
383 | // by default the xml parser can support these 3 charset encodings |
||
384 | $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); |
||
385 | |||
386 | // NB: this is disabled to avoid making all the requests sent huge... mbstring supports more than 80 charsets! |
||
387 | //$ch = $this->getCharsetEncoder(); |
||
388 | //$this->accepted_charset_encodings = $ch->knownCharsets(); |
||
389 | |||
390 | // initialize user_agent string |
||
391 | $this->user_agent = PhpXmlRpc::$xmlrpcName . ' ' . PhpXmlRpc::$xmlrpcVersion; |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * @param string $name see all the OPT_ constants |
||
396 | * @param mixed $value |
||
397 | * @return $this |
||
398 | * @throws ValueErrorException on unsupported option |
||
399 | */ |
||
400 | public function setOption($name, $value) |
||
401 | { |
||
402 | if (in_array($name, static::$options)) { |
||
403 | $this->$name = $value; |
||
404 | return $this; |
||
405 | } |
||
406 | |||
407 | throw new ValueErrorException("Unsupported option '$name'"); |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * @param string $name see all the OPT_ constants |
||
412 | * @return mixed |
||
413 | * @throws ValueErrorException on unsupported option |
||
414 | */ |
||
415 | public function getOption($name) |
||
416 | { |
||
417 | if (in_array($name, static::$options)) { |
||
418 | 580 | return $this->$name; |
|
419 | } |
||
420 | 580 | ||
421 | 580 | throw new ValueErrorException("Unsupported option '$name'"); |
|
422 | } |
||
423 | |||
424 | /** |
||
425 | * Returns the complete list of Client options, with their value. |
||
426 | * @return array |
||
427 | 580 | */ |
|
428 | public function getOptions() |
||
429 | 580 | { |
|
430 | $values = array(); |
||
431 | foreach (static::$options as $opt) { |
||
432 | $values[$opt] = $this->getOption($opt); |
||
433 | } |
||
434 | return $values; |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * @param array $options key: any valid option (see all the OPT_ constants) |
||
439 | * @return $this |
||
440 | * @throws ValueErrorException on unsupported option |
||
441 | */ |
||
442 | public function setOptions($options) |
||
443 | { |
||
444 | foreach ($options as $name => $value) { |
||
445 | $this->setOption($name, $value); |
||
446 | 66 | } |
|
447 | |||
448 | 66 | return $this; |
|
449 | 66 | } |
|
450 | |||
451 | /** |
||
452 | * Enable/disable the echoing to screen of the xml-rpc responses received. The default is not to output anything. |
||
453 | * |
||
454 | * The debugging information at level 1 includes the raw data returned from the XML-RPC server it was querying |
||
455 | * (including bot HTTP headers and the full XML payload), and the PHP value the client attempts to create to |
||
456 | * represent the value returned by the server. |
||
457 | * At level 2, the complete payload of the xml-rpc request is also printed, before being sent to the server. |
||
458 | * At level -1, the Response objects returned by send() calls will not carry information about the http response's |
||
459 | * cookies, headers and body, which might save some memory |
||
460 | * |
||
461 | * This option can be very useful when debugging servers as it allows you to see exactly what the client sends and |
||
462 | * the server returns. Never leave it enabled for production! |
||
463 | * |
||
464 | * @param integer $level values -1, 0, 1 and 2 are supported |
||
465 | * @return $this |
||
466 | */ |
||
467 | public function setDebug($level) |
||
468 | { |
||
469 | $this->debug = $level; |
||
470 | return $this; |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * Sets the username and password for authorizing the client to the server. |
||
475 | * |
||
476 | * With the default (HTTP) transport, this information is used for HTTP Basic authorization. |
||
477 | * Note that username and password can also be set using the class constructor. |
||
478 | * With HTTP 1.1 and HTTPS transport, NTLM and Digest authentication protocols are also supported. To enable them use |
||
479 | * the constants CURLAUTH_DIGEST and CURLAUTH_NTLM as values for the auth type parameter. |
||
480 | * |
||
481 | * @param string $user username |
||
482 | * @param string $password password |
||
483 | * @param integer $authType auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC |
||
484 | * (basic auth). Note that auth types NTLM and Digest will only work if the Curl php |
||
485 | * extension is enabled. |
||
486 | * @return $this |
||
487 | */ |
||
488 | public function setCredentials($user, $password, $authType = 1) |
||
489 | { |
||
490 | $this->username = $user; |
||
491 | $this->password = $password; |
||
492 | $this->authtype = $authType; |
||
493 | return $this; |
||
494 | } |
||
495 | 697 | ||
496 | /** |
||
497 | * Set the optional certificate and passphrase used in SSL-enabled communication with a remote server. |
||
498 | * |
||
499 | 697 | * Note: to retrieve information about the client certificate on the server side, you will need to look into the |
|
500 | 117 | * environment variables which are set up by the webserver. Different webservers will typically set up different |
|
501 | * variables. |
||
502 | * |
||
503 | 697 | * @param string $cert the name of a file containing a PEM formatted certificate |
|
504 | * @param string $certPass the password required to use it |
||
505 | 66 | * @return $this |
|
506 | */ |
||
507 | 66 | public function setCertificate($cert, $certPass = '') |
|
508 | 697 | { |
|
509 | 28 | $this->cert = $cert; |
|
510 | 28 | $this->certpass = $certPass; |
|
511 | 28 | return $this; |
|
512 | } |
||
513 | |||
514 | /** |
||
515 | 697 | * Add a CA certificate to verify server with in SSL-enabled communication when SetSSLVerifypeer has been set to TRUE. |
|
516 | * |
||
517 | * See the php manual page about CURLOPT_CAINFO for more details. |
||
518 | * |
||
519 | 697 | * @param string $caCert certificate file name (or dir holding certificates) |
|
520 | 697 | * @param bool $isDir set to true to indicate cacert is a dir. defaults to false |
|
521 | * @return $this |
||
522 | 697 | */ |
|
523 | 322 | public function setCaCertificate($caCert, $isDir = false) |
|
524 | 322 | { |
|
525 | 322 | if ($isDir) { |
|
526 | 322 | $this->cacertdir = $caCert; |
|
527 | } else { |
||
528 | 322 | $this->cacert = $caCert; |
|
529 | 322 | } |
|
530 | 322 | return $this; |
|
531 | 322 | } |
|
532 | 322 | ||
533 | 322 | /** |
|
534 | 322 | * Set attributes for SSL communication: private SSL key. |
|
535 | 322 | * |
|
536 | 322 | * NB: does not work in older php/curl installs. |
|
537 | 322 | * Thanks to Daniel Convissor. |
|
538 | 322 | * |
|
539 | 322 | * @param string $key The name of a file containing a private SSL key |
|
540 | * @param string $keyPass The secret password needed to use the private SSL key |
||
541 | 322 | * @return $this |
|
542 | 322 | */ |
|
543 | 322 | public function setKey($key, $keyPass) |
|
544 | 322 | { |
|
545 | 322 | $this->key = $key; |
|
546 | $this->keypass = $keyPass; |
||
547 | return $this; |
||
548 | } |
||
549 | 375 | ||
550 | 375 | /** |
|
551 | 375 | * Set attributes for SSL communication: verify the remote host's SSL certificate, and cause the connection to fail |
|
552 | 375 | * if the cert verification fails. |
|
553 | * |
||
554 | 375 | * By default, verification is enabled. |
|
555 | 375 | * To specify custom SSL certificates to validate the server with, use the setCaCertificate method. |
|
556 | 375 | * |
|
557 | 375 | * @param bool $i enable/disable verification of peer certificate |
|
558 | 375 | * @return $this |
|
559 | 375 | * @deprecated use setOption |
|
560 | 375 | */ |
|
561 | 375 | public function setSSLVerifyPeer($i) |
|
562 | 375 | { |
|
563 | 375 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
|
564 | 375 | ||
565 | 375 | $this->verifypeer = $i; |
|
566 | return $this; |
||
567 | 375 | } |
|
568 | 375 | ||
569 | 375 | /** |
|
570 | * Set attributes for SSL communication: verify the remote host's SSL certificate's common name (CN). |
||
571 | * |
||
572 | * Note that support for value 1 has been removed in cURL 7.28.1 |
||
573 | 697 | * |
|
574 | * @param int $i Set to 1 to only the existence of a CN, not that it matches |
||
575 | * @return $this |
||
576 | * @deprecated use setOption |
||
577 | */ |
||
578 | public function setSSLVerifyHost($i) |
||
579 | { |
||
580 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
581 | |||
582 | $this->verifyhost = $i; |
||
583 | return $this; |
||
584 | } |
||
585 | |||
586 | /** |
||
587 | * Set attributes for SSL communication: SSL version to use. Best left at 0 (default value): let PHP decide. |
||
588 | * |
||
589 | * @param int $i use CURL_SSLVERSION_ constants. When in socket mode, use values 2 (SSLv2) to 7 (TLSv1.3). 0 for auto |
||
590 | * (note that old php versions do not support all TLS versions) |
||
591 | * @return $this |
||
592 | * @deprecated use setOption |
||
593 | */ |
||
594 | public function setSSLVersion($i) |
||
595 | { |
||
596 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
597 | |||
598 | $this->sslversion = $i; |
||
599 | return $this; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Set proxy info. |
||
604 | * |
||
605 | * NB: CURL versions before 7.11.10 cannot use a proxy to communicate with https servers. |
||
606 | * |
||
607 | * @param string $proxyHost |
||
608 | * @param string $proxyPort Defaults to 8080 for HTTP and 443 for HTTPS |
||
609 | * @param string $proxyUsername Leave blank if proxy has public access |
||
610 | * @param string $proxyPassword Leave blank if proxy has public access |
||
611 | * @param int $proxyAuthType defaults to CURLAUTH_BASIC (Basic authentication protocol); set to constant CURLAUTH_NTLM |
||
612 | * to use NTLM auth with proxy (has effect only when the client uses the HTTP 1.1 protocol) |
||
613 | * @return $this |
||
614 | */ |
||
615 | public function setProxy($proxyHost, $proxyPort, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1) |
||
616 | { |
||
617 | $this->proxy = $proxyHost; |
||
618 | $this->proxyport = $proxyPort; |
||
619 | $this->proxy_user = $proxyUsername; |
||
620 | $this->proxy_pass = $proxyPassword; |
||
621 | $this->proxy_authtype = $proxyAuthType; |
||
622 | return $this; |
||
623 | } |
||
624 | |||
625 | /** |
||
626 | * Enables/disables reception of compressed xml-rpc responses. |
||
627 | * |
||
628 | * This requires the "zlib" extension to be enabled in your php install. If it is, by default xmlrpc_client |
||
629 | * instances will enable reception of compressed content. |
||
630 | * Note that enabling reception of compressed responses merely adds some standard http headers to xml-rpc requests. |
||
631 | * It is up to the xml-rpc server to return compressed responses when receiving such requests. |
||
632 | * |
||
633 | * @param string $compMethod either 'gzip', 'deflate', 'any' or '' |
||
634 | * @return $this |
||
635 | */ |
||
636 | public function setAcceptedCompression($compMethod) |
||
637 | { |
||
638 | if ($compMethod == 'any') { |
||
639 | $this->accepted_compression = array('gzip', 'deflate'); |
||
640 | } elseif ($compMethod == false) { |
||
641 | $this->accepted_compression = array(); |
||
642 | } else { |
||
643 | $this->accepted_compression = array($compMethod); |
||
644 | } |
||
645 | return $this; |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * Enables/disables http compression of xml-rpc request. |
||
650 | * |
||
651 | * This requires the "zlib" extension to be enabled in your php install. |
||
652 | * Take care when sending compressed requests: servers might not support them (and automatic fallback to |
||
653 | * uncompressed requests is not yet implemented). |
||
654 | * |
||
655 | * @param string $compMethod either 'gzip', 'deflate' or '' |
||
656 | * @return $this |
||
657 | * @deprecated use setOption |
||
658 | */ |
||
659 | public function setRequestCompression($compMethod) |
||
660 | { |
||
661 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
662 | |||
663 | $this->request_compression = $compMethod; |
||
664 | 375 | return $this; |
|
665 | } |
||
666 | |||
667 | /** |
||
668 | * Adds a cookie to list of cookies that will be sent to server with every further request (useful e.g. for keeping |
||
669 | * session info outside the xml-rpc payload). |
||
670 | * |
||
671 | 375 | * NB: by default all cookies set via this method are sent to the server, regardless of path/domain/port. Taking |
|
672 | 373 | * advantage of those values is left to the single developer. |
|
673 | * |
||
674 | * @param string $name nb: will not be escaped in the request's http headers. Take care not to use CTL chars or |
||
675 | * separators! |
||
676 | 375 | * @param string $value |
|
677 | 346 | * @param string $path |
|
678 | * @param string $domain |
||
679 | * @param int $port do not use! Cookies are not separated by port |
||
680 | 375 | * @return $this |
|
681 | * |
||
682 | 375 | * @todo check correctness of urlencoding cookie value (copied from php way of doing it, but php is generally sending |
|
683 | 375 | * response not requests. We do the opposite...) |
|
684 | 64 | * @todo strip invalid chars from cookie name? As per RFC 6265, we should follow RFC 2616, Section 2.2 |
|
685 | 32 | * @todo drop/rename $port parameter. Cookies are not isolated by port! |
|
686 | 32 | * @todo feature-creep allow storing 'expires', 'secure', 'httponly' and 'samesite' cookie attributes (we could do |
|
687 | 32 | * as php, and allow $path to be an array of attributes...) |
|
688 | 32 | */ |
|
689 | public function setCookie($name, $value = '', $path = '', $domain = '', $port = null) |
||
690 | { |
||
691 | 32 | $this->cookies[$name]['value'] = rawurlencode($value); |
|
692 | 32 | if ($path || $domain || $port) { |
|
693 | 32 | $this->cookies[$name]['path'] = $path; |
|
694 | 32 | $this->cookies[$name]['domain'] = $domain; |
|
695 | $this->cookies[$name]['port'] = $port; |
||
696 | } |
||
697 | return $this; |
||
698 | } |
||
699 | |||
700 | 375 | /** |
|
701 | 375 | * Directly set cURL options, for extra flexibility (when in cURL mode). |
|
702 | 32 | * |
|
703 | 32 | * It allows e.g. to bind client to a specific IP interface / address. |
|
704 | * |
||
705 | * @param array $options |
||
706 | * @return $this |
||
707 | * @deprecated use setOption |
||
708 | 375 | */ |
|
709 | 375 | public function setCurlOptions($options) |
|
710 | 73 | { |
|
711 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
712 | |||
713 | 375 | $this->extracurlopts = $options; |
|
714 | 375 | return $this; |
|
715 | 32 | } |
|
716 | |||
717 | /** |
||
718 | 32 | * @param int $useCurlMode self::USE_CURL_ALWAYS, self::USE_CURL_AUTO or self::USE_CURL_NEVER |
|
719 | 32 | * @return $this |
|
720 | 32 | * @deprecated use setOption |
|
721 | 32 | */ |
|
722 | 32 | public function setUseCurl($useCurlMode) |
|
723 | { |
||
724 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
725 | |||
726 | 32 | $this->use_curl = $useCurlMode; |
|
727 | return $this; |
||
728 | } |
||
729 | 343 | ||
730 | 343 | ||
731 | 343 | /** |
|
732 | 343 | * Set user-agent string that will be used by this client instance in http headers sent to the server. |
|
733 | * |
||
734 | * The default user agent string includes the name of this library and the version number. |
||
735 | * |
||
736 | 375 | * @param string $agentString |
|
737 | 375 | * @return $this |
|
738 | 309 | * @deprecated use setOption |
|
739 | 309 | */ |
|
740 | 309 | public function setUserAgent($agentString) |
|
741 | { |
||
742 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
743 | |||
744 | $this->user_agent = $agentString; |
||
745 | return $this; |
||
746 | } |
||
747 | |||
748 | /** |
||
749 | * @param null|int $component allowed values: PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_PATH |
||
750 | * @return string|int Notes: the path component will include query string and fragment; NULL is a valid value for port |
||
751 | * (in which case the default port for http/https will be used); |
||
752 | * @throws ValueErrorException on unsupported component |
||
753 | 309 | */ |
|
754 | public function getUrl($component = null) |
||
755 | { |
||
756 | 309 | if (is_int($component) || ctype_digit($component)) { |
|
757 | switch ($component) { |
||
758 | case PHP_URL_SCHEME: |
||
759 | return $this->method; |
||
760 | 375 | case PHP_URL_HOST: |
|
761 | 375 | return $this->server; |
|
762 | case PHP_URL_PORT: |
||
763 | return $this->port; |
||
764 | case PHP_URL_PATH: |
||
765 | return $this->path; |
||
766 | 375 | case '': |
|
767 | 375 | ||
768 | 375 | default: |
|
769 | 375 | throw new ValueErrorException("Unsupported component '$component'"); |
|
770 | 375 | } |
|
771 | 375 | } |
|
772 | 375 | ||
773 | 375 | $url = $this->method . '://' . $this->server; |
|
774 | 375 | if ($this->port == 0 || ($this->port == 80 && in_array($this->method, array('http', 'http10', 'http11', 'h2c'))) || |
|
775 | 375 | ($this->port == 443 && in_array($this->method, array('https', 'h2')))) { |
|
776 | 375 | return $url . $this->path; |
|
777 | 375 | } else { |
|
778 | return $url . ':' . $this->port . $this->path; |
||
779 | 375 | } |
|
780 | 2 | } |
|
781 | |||
782 | /** |
||
783 | 375 | * Send an xml-rpc request to the server. |
|
784 | 375 | * |
|
785 | 32 | * @param Request|Request[]|string $req The Request object, or an array of requests for using multicall, or the |
|
786 | * complete xml representation of a request. |
||
787 | * When sending an array of Request objects, the client will try to make use of |
||
788 | * a single 'system.multicall' xml-rpc method call to forward to the server all |
||
789 | * the requests in a single HTTP round trip, unless $this->no_multicall has |
||
790 | * been previously set to TRUE (see the multicall method below), in which case |
||
791 | 32 | * many consecutive xml-rpc requests will be sent. The method will return an |
|
792 | * array of Response objects in both cases. |
||
793 | * The third variant allows to build by hand (or any other means) a complete |
||
794 | 32 | * xml-rpc request message, and send it to the server. $req should be a string |
|
795 | * containing the complete xml representation of the request. It is e.g. useful |
||
796 | * when, for maximal speed of execution, the request is serialized into a |
||
797 | 32 | * string using the native php xml-rpc functions (see http://www.php.net/xmlrpc) |
|
798 | * @param integer $timeout deprecated. Connection timeout, in seconds, If unspecified, the timeout set with setOption |
||
799 | * will be used. If that is 0, a platform specific timeout will apply. |
||
800 | 32 | * This timeout value is passed to fsockopen(). It is also used for detecting server |
|
801 | 32 | * timeouts during communication (i.e. if the server does not send anything to the client |
|
802 | * for $timeout seconds, the connection will be closed). When in CURL mode, this is the |
||
803 | * CURL timeout. |
||
804 | 375 | * NB: in both CURL and Socket modes, some conditions might lead to the client not |
|
805 | * respecting the given timeout. Eg. if the network is not connected |
||
806 | 375 | * @param string $method deprecated. Use the same value in the constructor instead. |
|
807 | 64 | * Valid values are 'http', 'http11', 'https', 'h2' and 'h2c'. If left empty, |
|
808 | * the http protocol chosen during creation of the object will be used. |
||
809 | 311 | * Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c' |
|
810 | * for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be |
||
811 | * thus incompatible with any server/proxy not supporting http/2. This is because POST |
||
812 | 375 | * request are not compatible with h2c upgrade. |
|
813 | 375 | * @return Response|Response[] Note that the client will always return a Response object, even if the call fails |
|
814 | * |
||
815 | 375 | * @todo allow throwing exceptions instead of returning responses in case of failed calls and/or Fault responses |
|
816 | 375 | * @todo refactor: we now support many options besides connection timeout and http version to use. Why only privilege those? |
|
817 | 375 | */ |
|
818 | 374 | public function send($req, $timeout = 0, $method = '') |
|
819 | 367 | { |
|
820 | if ($method !== '' || $timeout !== 0) { |
||
821 | $this->logDeprecation("Using non-default values for arguments 'method' and 'timeout' when calling method " . __METHOD__ . ' is deprecated'); |
||
822 | 1 | } |
|
823 | |||
824 | // if user does not specify http protocol, use native method of this client |
||
825 | // (i.e. method set during call to constructor) |
||
826 | if ($method == '') { |
||
827 | 1 | $method = $this->method; |
|
828 | 1 | } |
|
829 | |||
830 | 1 | if ($timeout == 0) { |
|
831 | $timeout = $this->timeout; |
||
832 | } |
||
833 | 374 | ||
834 | if (is_array($req)) { |
||
835 | // $req is an array of Requests |
||
836 | /// @todo switch to the new syntax for multicall |
||
837 | return $this->multicall($req, $timeout, $method); |
||
838 | } elseif (is_string($req)) { |
||
839 | $n = new static::$requestClass(''); |
||
840 | /// @todo we should somehow allow the caller to declare a custom contenttype too, esp. for the charset declaration |
||
841 | $n->setPayload($req); |
||
842 | $req = $n; |
||
843 | 374 | } |
|
844 | |||
845 | // where req is a Request |
||
846 | $req->setDebug($this->debug); |
||
847 | 374 | ||
848 | 374 | /// @todo we could be smarter about this and not force usage of curl for https if not present as well as use the |
|
849 | 374 | /// presence of curl_extra_opts or socket_extra_opts as a hint |
|
850 | $useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO && ( |
||
851 | 374 | in_array($method, array('https', 'http11', 'h2c', 'h2')) || |
|
852 | ($this->username != '' && $this->authtype != 1) || |
||
853 | 374 | ($this->proxy != '' && $this->proxy_user != '' && $this->proxy_authtype != 1) |
|
854 | )); |
||
855 | |||
856 | // BC - we go through sendPayloadCURL/sendPayloadSocket in case some subclass reimplemented those |
||
857 | if ($useCurl) { |
||
858 | $r = $this->sendPayloadCURL( |
||
859 | $req, |
||
860 | $this->server, |
||
861 | $this->port, |
||
862 | $timeout, |
||
863 | $this->username, |
||
864 | $this->password, |
||
865 | $this->authtype, |
||
866 | $this->cert, |
||
867 | $this->certpass, |
||
868 | $this->cacert, |
||
869 | $this->cacertdir, |
||
870 | $this->proxy, |
||
871 | $this->proxyport, |
||
872 | $this->proxy_user, |
||
873 | $this->proxy_pass, |
||
874 | $this->proxy_authtype, |
||
875 | // BC |
||
876 | $method == 'http11' ? 'http' : $method, |
||
877 | $this->keepalive, |
||
878 | $this->key, |
||
879 | $this->keypass, |
||
880 | $this->sslversion |
||
881 | ); |
||
882 | } else { |
||
883 | $r = $this->sendPayloadSocket( |
||
884 | $req, |
||
885 | $this->server, |
||
886 | 322 | $this->port, |
|
887 | $timeout, |
||
888 | $this->username, |
||
889 | $this->password, |
||
890 | $this->authtype, |
||
891 | 322 | $this->cert, |
|
892 | $this->certpass, |
||
893 | $this->cacert, |
||
894 | $this->cacertdir, |
||
895 | 322 | $this->proxy, |
|
896 | $this->proxyport, |
||
897 | 96 | $this->proxy_user, |
|
898 | 96 | $this->proxy_pass, |
|
899 | $this->proxy_authtype, |
||
900 | $method, |
||
901 | $this->key, |
||
902 | $this->keypass, |
||
903 | $this->sslversion |
||
904 | 322 | ); |
|
905 | 322 | } |
|
906 | |||
907 | return $r; |
||
908 | } |
||
909 | |||
910 | 322 | /** |
|
911 | * @param Request $req |
||
912 | * @param string $method |
||
913 | * @param string $server |
||
914 | * @param int $port |
||
915 | 322 | * @param string $path |
|
916 | * @param array $opts |
||
917 | 322 | * @return Response |
|
918 | */ |
||
919 | protected function sendViaSocket($req, $method, $server, $port, $path, $opts) |
||
920 | { |
||
921 | /// @todo log a warning if passed an unsupported method |
||
922 | |||
923 | // Only create the payload if it was not created previously |
||
924 | /// @todo what if the request's payload was created with a different encoding? |
||
925 | /// Also, if we do not call serialize(), the request will not set its content-type to have the charset declared |
||
926 | $payload = $req->getPayload(); |
||
927 | if (empty($payload)) { |
||
928 | $payload = $req->serialize($opts['request_charset_encoding']); |
||
929 | 322 | } |
|
930 | |||
931 | // Deflate request body and set appropriate request headers |
||
932 | 1 | $encodingHdr = ''; |
|
933 | 1 | if ($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate') { |
|
934 | 1 | if ($opts['request_compression'] == 'gzip' && function_exists('gzencode')) { |
|
935 | 1 | $a = @gzencode($payload); |
|
936 | 1 | if ($a) { |
|
937 | $payload = $a; |
||
938 | $encodingHdr = "Content-Encoding: gzip\r\n"; |
||
939 | 322 | } else { |
|
940 | 160 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzencode failure in compressing request'); |
|
941 | } |
||
942 | 322 | } else if (function_exists('gzcompress')) { |
|
943 | $a = @gzcompress($payload); |
||
944 | 322 | if ($a) { |
|
945 | $payload = $a; |
||
946 | $encodingHdr = "Content-Encoding: deflate\r\n"; |
||
947 | } else { |
||
948 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzcompress failure in compressing request'); |
||
949 | } |
||
950 | 322 | } else { |
|
951 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported by this PHP install'); |
||
952 | } |
||
953 | 323 | } else { |
|
954 | if ($opts['request_compression'] != '') { |
||
955 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported'); |
||
956 | } |
||
957 | } |
||
958 | 323 | ||
959 | 322 | // thanks to Grant Rauscher |
|
960 | 226 | $credentials = ''; |
|
961 | if ($opts['username'] != '') { |
||
962 | 96 | if ($opts['authtype'] != 1) { |
|
963 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0'); |
||
964 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], |
||
965 | PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth is supported with HTTP 1.0'); |
||
966 | } |
||
967 | 323 | $credentials = 'Authorization: Basic ' . base64_encode($opts['username'] . ':' . $opts['password']) . "\r\n"; |
|
968 | 302 | } |
|
969 | |||
970 | $acceptedEncoding = ''; |
||
971 | if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) { |
||
972 | 323 | $acceptedEncoding = 'Accept-Encoding: ' . implode(', ', $opts['accepted_compression']) . "\r\n"; |
|
973 | 323 | } |
|
974 | 64 | ||
975 | 32 | if ($port == 0) { |
|
976 | 32 | $port = ($method === 'https') ? 443 : 80; |
|
977 | 32 | } |
|
978 | 32 | ||
979 | $proxyCredentials = ''; |
||
980 | if ($opts['proxy']) { |
||
981 | 32 | if ($opts['proxyport'] == 0) { |
|
982 | 32 | $opts['proxyport'] = 8080; |
|
983 | 32 | } |
|
984 | 64 | $connectServer = $opts['proxy']; |
|
985 | $connectPort = $opts['proxyport']; |
||
986 | $transport = 'tcp'; |
||
987 | /// @todo check: should we not use https in some cases? |
||
988 | 259 | $uri = 'http://' . $server . ':' . $port . $path; |
|
989 | if ($opts['proxy_user'] != '') { |
||
990 | if ($opts['proxy_authtype'] != 1) { |
||
991 | 323 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0'); |
|
992 | 323 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], |
|
993 | 64 | PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth to proxy is supported with HTTP 1.0'); |
|
994 | } |
||
995 | 259 | $proxyCredentials = 'Proxy-Authorization: Basic ' . base64_encode($opts['proxy_user'] . ':' . |
|
996 | 32 | $opts['proxy_pass']) . "\r\n"; |
|
997 | } |
||
998 | } else { |
||
999 | 227 | $connectServer = $server; |
|
1000 | $connectPort = $port; |
||
1001 | $transport = ($method === 'https') ? 'tls' : 'tcp'; |
||
1002 | 323 | $uri = $path; |
|
1003 | 323 | } |
|
1004 | 322 | ||
1005 | // Cookie generation, as per RFC 6265 |
||
1006 | // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user... |
||
1007 | 56 | $cookieHeader = ''; |
|
1008 | if (count($opts['cookies'])) { |
||
1009 | $version = ''; |
||
1010 | foreach ($opts['cookies'] as $name => $cookie) { |
||
1011 | 323 | /// @todo should we sanitize the cookie value on behalf of the user? See setCookie comments |
|
1012 | $cookieHeader .= ' ' . $name . '=' . $cookie['value'] . ";"; |
||
1013 | 323 | } |
|
1014 | $cookieHeader = 'Cookie:' . $version . substr($cookieHeader, 0, -1) . "\r\n"; |
||
1015 | } |
||
1016 | |||
1017 | 323 | $extraHeaders = ''; |
|
1018 | if (is_array($this->extra_headers) && $this->extra_headers) { |
||
1019 | 323 | $extraHeaders = implode("\r\n", $this->extra_headers) . "\r\n"; |
|
1020 | } |
||
1021 | 323 | ||
1022 | // omit port if default |
||
1023 | if (($port == 80 && in_array($method, array('http', 'http10'))) || ($port == 443 && $method == 'https')) { |
||
1024 | 323 | $port = ''; |
|
1025 | } else { |
||
1026 | $port = ':' . $port; |
||
1027 | } |
||
1028 | 323 | ||
1029 | $op = 'POST ' . $uri . " HTTP/1.0\r\n" . |
||
1030 | 'User-Agent: ' . $opts['user_agent'] . "\r\n" . |
||
1031 | 66 | 'Host: ' . $server . $port . "\r\n" . |
|
1032 | 64 | $credentials . |
|
1033 | $proxyCredentials . |
||
1034 | 2 | $acceptedEncoding . |
|
1035 | $encodingHdr . |
||
1036 | 'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings']) . "\r\n" . |
||
1037 | $cookieHeader . |
||
1038 | 323 | 'Content-Type: ' . $req->getContentType() . "\r\n" . |
|
1039 | $extraHeaders . |
||
1040 | 323 | 'Content-Length: ' . strlen($payload) . "\r\n\r\n" . |
|
1041 | 161 | $payload; |
|
1042 | |||
1043 | if ($opts['debug'] > 1) { |
||
1044 | 323 | $this->getLogger()->debug("---SENDING---\n$op\n---END---"); |
|
1045 | 64 | } |
|
1046 | |||
1047 | $contextOptions = array(); |
||
1048 | if ($method == 'https') { |
||
1049 | if ($opts['cert'] != '') { |
||
1050 | 323 | $contextOptions['ssl']['local_cert'] = $opts['cert']; |
|
1051 | if ($opts['certpass'] != '') { |
||
1052 | 323 | $contextOptions['ssl']['passphrase'] = $opts['certpass']; |
|
1053 | } |
||
1054 | 323 | } |
|
1055 | 272 | if ($opts['cacert'] != '') { |
|
1056 | $contextOptions['ssl']['cafile'] = $opts['cacert']; |
||
1057 | } |
||
1058 | 323 | if ($opts['cacertdir'] != '') { |
|
1059 | 323 | $contextOptions['ssl']['capath'] = $opts['cacertdir']; |
|
1060 | 32 | } |
|
1061 | 32 | if ($opts['key'] != '') { |
|
1062 | 291 | $contextOptions['ssl']['local_pk'] = $opts['key']; |
|
1063 | } |
||
1064 | $contextOptions['ssl']['verify_peer'] = $opts['verifypeer']; |
||
1065 | 291 | $contextOptions['ssl']['verify_peer_name'] = $opts['verifypeer']; |
|
1066 | 32 | ||
1067 | 32 | if ($opts['sslversion'] != 0) { |
|
1068 | 259 | /// @see https://www.php.net/manual/en/function.curl-setopt.php, https://www.php.net/manual/en/migration56.openssl.php |
|
1069 | 32 | switch($opts['sslversion']) { |
|
1070 | 32 | /// @todo what does this map to? 1.0-1.3? |
|
1071 | //case 1: // TLSv1 |
||
1072 | // break; |
||
1073 | 323 | case 2: // SSLv2 |
|
1074 | 32 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; |
|
1075 | 32 | break; |
|
1076 | 32 | case 3: // SSLv3 |
|
1077 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; |
||
1078 | break; |
||
1079 | case 4: // TLSv1.0 - not always available |
||
1080 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT; |
||
1081 | break; |
||
1082 | 323 | case 5: // TLSv1.1 - not always available |
|
1083 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; |
||
1084 | 96 | break; |
|
1085 | case 6: // TLSv1.2 - not always available |
||
1086 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; |
||
1087 | break; |
||
1088 | 96 | case 7: // TLSv1.3 - not always available |
|
1089 | if (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT')) { |
||
1090 | $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT; |
||
1091 | } else { |
||
1092 | 96 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], |
|
1093 | PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': TLS-1.3 only is supported with PHP 7.4 or later'); |
||
1094 | 96 | } |
|
1095 | break; |
||
1096 | default: |
||
1097 | 96 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], |
|
1098 | PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': Unsupported required TLS version'); |
||
1099 | } |
||
1100 | } |
||
1101 | 96 | } |
|
1102 | |||
1103 | foreach ($opts['extrasockopts'] as $proto => $protoOpts) { |
||
1104 | foreach ($protoOpts as $key => $val) { |
||
1105 | 96 | $contextOptions[$proto][$key] = $val; |
|
1106 | } |
||
1107 | } |
||
1108 | |||
1109 | $context = stream_context_create($contextOptions); |
||
1110 | 96 | ||
1111 | if ($opts['timeout'] <= 0) { |
||
1112 | 96 | $connectTimeout = ini_get('default_socket_timeout'); |
|
1113 | } else { |
||
1114 | $connectTimeout = $opts['timeout']; |
||
1115 | } |
||
1116 | 323 | ||
1117 | 64 | $this->errno = 0; |
|
1118 | $this->errstr = ''; |
||
1119 | |||
1120 | 64 | /// @todo using `error_get_last` does not give us very detailed messages for connections errors, eg. for ssl |
|
1121 | 64 | /// problems on php 5.6 we get 'Connect error: stream_socket_client(): unable to connect to tls://localhost:443 (Unknown error) (0)', |
|
1122 | /// versus the more detailed warnings 'PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages: |
||
1123 | /// error:0A0C0103:SSL routines::internal error in /home/docker/workspace/src/Client.php on line 1121 |
||
1124 | /// PHP Warning: stream_socket_client(): Failed to enable crypto in /home/docker/workspace/src/Client.php on line 1121 |
||
1125 | /// PHP Warning: stream_socket_client(): unable to connect to tls://localhost:443 (Unknown error) in /home/docker/workspace/src/Client.php on line 1121' |
||
1126 | /// This could be obviated by removing the `@` and capturing warnings via ob_start and co |
||
1127 | $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $connectTimeout, |
||
1128 | STREAM_CLIENT_CONNECT, $context); |
||
1129 | if ($fp) { |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
1130 | if ($opts['timeout'] > 0) { |
||
1131 | stream_set_timeout($fp, $opts['timeout'], 0); |
||
1132 | } |
||
1133 | 323 | } else { |
|
1134 | 271 | if ($this->errstr == '') { |
|
1135 | 271 | $err = error_get_last(); |
|
1136 | 271 | $this->errstr = $err['message']; |
|
1137 | } |
||
1138 | 271 | ||
1139 | $this->errstr = 'Connect error: ' . $this->errstr; |
||
1140 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')'); |
||
1141 | 323 | } |
|
1142 | |||
1143 | if (!fputs($fp, $op, strlen($op))) { |
||
1144 | fclose($fp); |
||
1145 | 323 | $this->errstr = 'Write error'; |
|
1146 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr); |
||
1147 | } |
||
1148 | |||
1149 | 323 | $info = stream_get_meta_data($fp); |
|
1150 | if ($info['timed_out']) { |
||
1151 | fclose($fp); |
||
1152 | $this->errstr = 'Write timeout'; |
||
1153 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr); |
||
1154 | } |
||
1155 | |||
1156 | // Close socket before parsing. |
||
1157 | // It should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects) |
||
1158 | $ipd = ''; |
||
1159 | do { |
||
1160 | // shall we check for $data === FALSE? |
||
1161 | // as per the manual, it signals an error |
||
1162 | $ipd .= fread($fp, 32768); |
||
1163 | |||
1164 | $info = stream_get_meta_data($fp); |
||
1165 | if ($info['timed_out']) { |
||
1166 | fclose($fp); |
||
1167 | $this->errstr = 'Read timeout'; |
||
1168 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr); |
||
1169 | } |
||
1170 | |||
1171 | } while (!feof($fp)); |
||
1172 | fclose($fp); |
||
1173 | |||
1174 | 66 | return $req->parseResponse($ipd, false, $opts['return_type']); |
|
1175 | } |
||
1176 | 66 | ||
1177 | /** |
||
1178 | * Contributed by Justin Miller |
||
1179 | 66 | * Requires curl to be built into PHP |
|
1180 | 45 | * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers! |
|
1181 | 45 | * |
|
1182 | * @param Request $req |
||
1183 | 45 | * @param string $method |
|
1184 | * @param string $server |
||
1185 | * @param int $port |
||
1186 | * @param string $path |
||
1187 | * @param array $opts the keys/values match self::getOptions |
||
1188 | * @return Response |
||
1189 | * |
||
1190 | * @todo the $path arg atm is ignored. What to do if it is != $this->path? |
||
1191 | */ |
||
1192 | protected function sendViaCURL($req, $method, $server, $port, $path, $opts) |
||
1193 | { |
||
1194 | if (!function_exists('curl_init')) { |
||
1195 | $this->errstr = 'CURL unavailable on this install'; |
||
1196 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']); |
||
1197 | } |
||
1198 | if ($method == 'https' || $method == 'h2') { |
||
1199 | // q: what about installs where we get back a string, but curl is linked to other ssl libs than openssl? |
||
1200 | if (($info = curl_version()) && |
||
1201 | 23 | ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))) |
|
0 ignored issues
–
show
|
|||
1202 | ) { |
||
1203 | $this->errstr = 'SSL unavailable on this install'; |
||
1204 | 23 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']); |
|
1205 | 23 | } |
|
1206 | } |
||
1207 | if (($method == 'h2' && !defined('CURL_HTTP_VERSION_2_0')) || |
||
1208 | ($method == 'h2c' && !defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))) { |
||
1209 | 23 | $this->errstr = 'HTTP/2 unavailable on this install'; |
|
1210 | 23 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']); |
|
1211 | } |
||
1212 | |||
1213 | // BC - we go through prepareCurlHandle in case some subclass reimplemented it |
||
1214 | $curl = $this->prepareCurlHandle($req, $server, $port, $opts['timeout'], $opts['username'], $opts['password'], |
||
1215 | $opts['authtype'], $opts['cert'], $opts['certpass'], $opts['cacert'], $opts['cacertdir'], $opts['proxy'], |
||
1216 | $opts['proxyport'], $opts['proxy_user'], $opts['proxy_pass'], $opts['proxy_authtype'], $method, |
||
1217 | $opts['keepalive'], $opts['key'], $opts['keypass'], $opts['sslversion']); |
||
1218 | |||
1219 | if (!$curl) { |
||
1220 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . |
||
1221 | 23 | ': error during curl initialization. Check php error log for details'); |
|
1222 | } |
||
1223 | |||
1224 | $result = curl_exec($curl); |
||
1225 | |||
1226 | if ($opts['debug'] > 1) { |
||
1227 | $message = "---CURL INFO---\n"; |
||
1228 | foreach (curl_getinfo($curl) as $name => $val) { |
||
1229 | if (is_array($val)) { |
||
1230 | $val = implode("\n", $val); |
||
1231 | } |
||
1232 | $message .= $name . ': ' . $val . "\n"; |
||
1233 | } |
||
1234 | $message .= '---END---'; |
||
1235 | 45 | $this->getLogger()->debug($message); |
|
1236 | } |
||
1237 | |||
1238 | 45 | if (!$result) { |
|
1239 | 45 | /// @todo we should use a better check here - what if we get back '' or '0'? |
|
1240 | 45 | ||
1241 | 45 | $this->errstr = 'no response'; |
|
1242 | 45 | $resp = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . |
|
1243 | 45 | ': ' . curl_error($curl)); |
|
1244 | 45 | curl_close($curl); |
|
1245 | if ($opts['keepalive']) { |
||
1246 | 45 | $this->xmlrpc_curl_handle = null; |
|
1247 | 45 | } |
|
1248 | } else { |
||
1249 | 45 | if (!$opts['keepalive']) { |
|
1250 | 45 | curl_close($curl); |
|
1251 | } |
||
1252 | $resp = $req->parseResponse($result, true, $opts['return_type']); |
||
1253 | 45 | if ($opts['keepalive']) { |
|
1254 | /// @todo if we got back a 302 or 308, we should not reuse the curl handle for later calls |
||
1255 | 45 | if ($resp->faultCode() == PhpXmlRpc::$xmlrpcerr['http_error']) { |
|
1256 | curl_close($curl); |
||
1257 | $this->xmlrpc_curl_handle = null; |
||
1258 | } |
||
1259 | } |
||
1260 | } |
||
1261 | 45 | ||
1262 | return $resp; |
||
1263 | 45 | } |
|
1264 | |||
1265 | 45 | /** |
|
1266 | * @param Request $req |
||
1267 | 22 | * @param string $method |
|
1268 | 22 | * @param string $server |
|
1269 | * @param int $port |
||
1270 | * @param string $path |
||
1271 | 22 | * @param array $opts the keys/values match self::getOptions |
|
1272 | 22 | * @return \CurlHandle|resource|false |
|
1273 | * |
||
1274 | * @todo allow this method to either throw or return a Response, so that we can pass back to caller more info on errors |
||
1275 | */ |
||
1276 | 22 | protected function createCURLHandle($req, $method, $server, $port, $path, $opts) |
|
1277 | 22 | { |
|
1278 | 22 | if ($port == 0) { |
|
1279 | 22 | if (in_array($method, array('http', 'http10', 'http11', 'h2c'))) { |
|
1280 | $port = 80; |
||
1281 | } else { |
||
1282 | 22 | $port = 443; |
|
1283 | 22 | } |
|
1284 | 22 | } |
|
1285 | |||
1286 | // Only create the payload if it was not created previously |
||
1287 | $payload = $req->getPayload(); |
||
1288 | 22 | if (empty($payload)) { |
|
1289 | 22 | $payload = $req->serialize($opts['request_charset_encoding']); |
|
1290 | 22 | } |
|
1291 | |||
1292 | 22 | // Deflate request body and set appropriate request headers |
|
1293 | 22 | $encodingHdr = ''; |
|
1294 | if (($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate')) { |
||
1295 | if ($opts['request_compression'] == 'gzip' && function_exists('gzencode')) { |
||
1296 | 22 | $a = @gzencode($payload); |
|
1297 | 22 | if ($a) { |
|
1298 | $payload = $a; |
||
1299 | $encodingHdr = 'Content-Encoding: gzip'; |
||
1300 | 22 | } else { |
|
1301 | 22 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzencode failure in compressing request'); |
|
1302 | } |
||
1303 | } else if (function_exists('gzcompress')) { |
||
1304 | $a = @gzcompress($payload); |
||
1305 | if ($a) { |
||
1306 | $payload = $a; |
||
1307 | 22 | $encodingHdr = 'Content-Encoding: deflate'; |
|
1308 | } else { |
||
1309 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzcompress failure in compressing request'); |
||
1310 | } |
||
1311 | 24 | } else { |
|
1312 | 24 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported by this PHP install'); |
|
1313 | } |
||
1314 | } else { |
||
1315 | 24 | if ($opts['request_compression'] != '') { |
|
1316 | 24 | $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported'); |
|
1317 | } |
||
1318 | } |
||
1319 | |||
1320 | 24 | if (!$opts['keepalive'] || !$this->xmlrpc_curl_handle) { |
|
1321 | 24 | if ($method == 'http11' || $method == 'http10' || $method == 'h2c') { |
|
1322 | 24 | $protocol = 'http'; |
|
1323 | 24 | } else { |
|
1324 | 24 | if ($method == 'h2') { |
|
1325 | $protocol = 'https'; |
||
1326 | } else { |
||
1327 | // http, https |
||
1328 | 24 | $protocol = $method; |
|
1329 | 24 | if (strpos($protocol, ':') !== false) { |
|
1330 | 22 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ": warning - attempted hacking attempt?. The curl protocol requested for the call is: '$protocol'"); |
|
1331 | 22 | return false; |
|
1332 | } |
||
1333 | 22 | } |
|
1334 | } |
||
1335 | $curl = curl_init($protocol . '://' . $server . ':' . $port . $path); |
||
1336 | 22 | if (!$curl) { |
|
1337 | return false; |
||
1338 | 22 | } |
|
1339 | if ($opts['keepalive']) { |
||
1340 | $this->xmlrpc_curl_handle = $curl; |
||
1341 | 22 | } |
|
1342 | 22 | } else { |
|
1343 | $curl = $this->xmlrpc_curl_handle; |
||
1344 | } |
||
1345 | |||
1346 | // results into variable |
||
1347 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
||
1348 | 24 | ||
1349 | if ($opts['debug'] > 1) { |
||
1350 | curl_setopt($curl, CURLOPT_VERBOSE, true); |
||
1351 | /// @todo redirect curlopt_stderr to some stream which can be piped to the logger |
||
1352 | } |
||
1353 | curl_setopt($curl, CURLOPT_USERAGENT, $opts['user_agent']); |
||
1354 | // required for XMLRPC: post the data |
||
1355 | curl_setopt($curl, CURLOPT_POST, 1); |
||
1356 | // the data |
||
1357 | curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); |
||
1358 | |||
1359 | // return the header too |
||
1360 | curl_setopt($curl, CURLOPT_HEADER, 1); |
||
1361 | |||
1362 | // NB: if we set an empty string, CURL will add http header indicating |
||
1363 | // ALL methods it is supporting. This is possibly a better option than letting the user tell what curl can / cannot do... |
||
1364 | if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) { |
||
1365 | //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $opts['accepted_compression'])); |
||
1366 | // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?) |
||
1367 | if (count($opts['accepted_compression']) == 1) { |
||
1368 | curl_setopt($curl, CURLOPT_ENCODING, $opts['accepted_compression'][0]); |
||
1369 | } else { |
||
1370 | curl_setopt($curl, CURLOPT_ENCODING, ''); |
||
1371 | } |
||
1372 | } |
||
1373 | // extra headers |
||
1374 | $headers = array('Content-Type: ' . $req->getContentType(), 'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings'])); |
||
1375 | // if no keepalive is wanted, let the server know it in advance |
||
1376 | if (!$opts['keepalive']) { |
||
1377 | $headers[] = 'Connection: close'; |
||
1378 | } |
||
1379 | // request compression header |
||
1380 | if ($encodingHdr) { |
||
1381 | $headers[] = $encodingHdr; |
||
1382 | } |
||
1383 | |||
1384 | if (is_array($this->extra_headers) && $this->extra_headers) { |
||
1385 | $headers = array_merge($headers, $this->extra_headers); |
||
1386 | } |
||
1387 | |||
1388 | // Fix the HTTP/1.1 417 Expectation Failed Bug (curl by default adds a 'Expect: 100-continue' header when POST |
||
1389 | // size exceeds 1025 bytes, apparently) |
||
1390 | $headers[] = 'Expect:'; |
||
1391 | |||
1392 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); |
||
1393 | // previous note: "timeout is borked" (on some old php/curl versions? It seems to work on 8.1. Maybe the issue |
||
1394 | // has to do with dns resolution...) |
||
1395 | if ($opts['timeout']) { |
||
1396 | curl_setopt($curl, CURLOPT_TIMEOUT, $opts['timeout']); |
||
1397 | } |
||
1398 | |||
1399 | switch ($method) { |
||
1400 | case 'http10': |
||
1401 | curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); |
||
1402 | break; |
||
1403 | case 'http11': |
||
1404 | curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
||
1405 | break; |
||
1406 | case 'h2c': |
||
1407 | if (defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE')) { |
||
1408 | curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); |
||
1409 | } else { |
||
1410 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. HTTP2 is not supported by the current PHP/curl install'); |
||
1411 | curl_close($curl); |
||
1412 | return false; |
||
1413 | } |
||
1414 | break; |
||
1415 | case 'h2': |
||
1416 | curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); |
||
1417 | break; |
||
1418 | } |
||
1419 | |||
1420 | if ($opts['username'] && $opts['password']) { |
||
1421 | curl_setopt($curl, CURLOPT_USERPWD, $opts['username'] . ':' . $opts['password']); |
||
1422 | if (defined('CURLOPT_HTTPAUTH')) { |
||
1423 | curl_setopt($curl, CURLOPT_HTTPAUTH, $opts['authtype']); |
||
1424 | } elseif ($opts['authtype'] != 1) { |
||
1425 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install'); |
||
1426 | curl_close($curl); |
||
1427 | return false; |
||
1428 | } |
||
1429 | } |
||
1430 | |||
1431 | // note: h2c is http2 without the https. No need to have it in this IF |
||
1432 | if ($method == 'https' || $method == 'h2') { |
||
1433 | // set cert file |
||
1434 | if ($opts['cert']) { |
||
1435 | curl_setopt($curl, CURLOPT_SSLCERT, $opts['cert']); |
||
1436 | } |
||
1437 | // set cert password |
||
1438 | if ($opts['certpass']) { |
||
1439 | curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $opts['certpass']); |
||
1440 | } |
||
1441 | // whether to verify remote host's cert |
||
1442 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $opts['verifypeer']); |
||
1443 | // set ca certificates file/dir |
||
1444 | if ($opts['cacert']) { |
||
1445 | curl_setopt($curl, CURLOPT_CAINFO, $opts['cacert']); |
||
1446 | } |
||
1447 | if ($opts['cacertdir']) { |
||
1448 | curl_setopt($curl, CURLOPT_CAPATH, $opts['cacertdir']); |
||
1449 | } |
||
1450 | // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?) |
||
1451 | if ($opts['key']) { |
||
1452 | curl_setopt($curl, CURLOPT_SSLKEY, $opts['key']); |
||
1453 | } |
||
1454 | // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?) |
||
1455 | if ($opts['keypass']) { |
||
1456 | curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $opts['keypass']); |
||
1457 | } |
||
1458 | // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that |
||
1459 | // it matches the hostname used |
||
1460 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $opts['verifyhost']); |
||
1461 | // allow usage of different SSL versions |
||
1462 | curl_setopt($curl, CURLOPT_SSLVERSION, $opts['sslversion']); |
||
1463 | } |
||
1464 | |||
1465 | // proxy info |
||
1466 | if ($opts['proxy']) { |
||
1467 | if ($opts['proxyport'] == 0) { |
||
1468 | $opts['proxyport'] = 8080; // NB: even for HTTPS, local connection is on port 8080 |
||
1469 | } |
||
1470 | curl_setopt($curl, CURLOPT_PROXY, $opts['proxy'] . ':' . $opts['proxyport']); |
||
1471 | if ($opts['proxy_user']) { |
||
1472 | curl_setopt($curl, CURLOPT_PROXYUSERPWD, $opts['proxy_user'] . ':' . $opts['proxy_pass']); |
||
1473 | if (defined('CURLOPT_PROXYAUTH')) { |
||
1474 | curl_setopt($curl, CURLOPT_PROXYAUTH, $opts['proxy_authtype']); |
||
1475 | } elseif ($opts['proxy_authtype'] != 1) { |
||
1476 | $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install'); |
||
1477 | curl_close($curl); |
||
1478 | return false; |
||
1479 | } |
||
1480 | } |
||
1481 | } |
||
1482 | |||
1483 | // NB: should we build cookie http headers by hand rather than let CURL do it? |
||
1484 | // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user... |
||
1485 | if (count($opts['cookies'])) { |
||
1486 | $cookieHeader = ''; |
||
1487 | foreach ($opts['cookies'] as $name => $cookie) { |
||
1488 | $cookieHeader .= $name . '=' . $cookie['value'] . '; '; |
||
1489 | } |
||
1490 | curl_setopt($curl, CURLOPT_COOKIE, substr($cookieHeader, 0, -2)); |
||
1491 | } |
||
1492 | |||
1493 | foreach ($opts['extracurlopts'] as $opt => $val) { |
||
1494 | curl_setopt($curl, $opt, $val); |
||
1495 | } |
||
1496 | |||
1497 | if ($opts['debug'] > 1) { |
||
1498 | $this->getLogger()->debug("---SENDING---\n$payload\n---END---"); |
||
1499 | } |
||
1500 | |||
1501 | return $curl; |
||
1502 | } |
||
1503 | |||
1504 | /** |
||
1505 | * Send an array of requests and return an array of responses. |
||
1506 | * |
||
1507 | * Unless $this->no_multicall has been set to true, it will try first to use one single xml-rpc call to server method |
||
1508 | * system.multicall, and revert to sending many successive calls in case of failure. |
||
1509 | * This failure is also stored in $this->no_multicall for subsequent calls. |
||
1510 | * Unfortunately, there is no server error code universally used to denote the fact that multicall is unsupported, |
||
1511 | * so there is no way to reliably distinguish between that and a temporary failure. |
||
1512 | * If you are sure that server supports multicall and do not want to fallback to using many single calls, set the |
||
1513 | * 2np parameter to FALSE. |
||
1514 | * |
||
1515 | * NB: trying to shoehorn extra functionality into existing syntax has resulted |
||
1516 | * in pretty much convoluted code... |
||
1517 | * |
||
1518 | * @param Request[] $reqs an array of Request objects |
||
1519 | * @param bool $noFallback When true, upon receiving an error during multicall, multiple single calls will not be |
||
1520 | * attempted. |
||
1521 | * Deprecated alternative, was: int - "connection timeout (in seconds). See the details in the |
||
1522 | * docs for the send() method". Please use setOption instead to set a timeout |
||
1523 | * @param string $method deprecated. Was: "the http protocol variant to be used. See the details in the docs for the send() method." |
||
1524 | * Please use the constructor to set an http protocol variant. |
||
1525 | * @param boolean $fallback deprecated. Was: "when true, upon receiving an error during multicall, multiple single |
||
1526 | * calls will be attempted" |
||
1527 | * @return Response[] |
||
1528 | */ |
||
1529 | public function multicall($reqs, $timeout = 0, $method = '', $fallback = true) |
||
1530 | { |
||
1531 | // BC |
||
1532 | if (is_bool($timeout) && $fallback === true) { |
||
1533 | $fallback = !$timeout; |
||
1534 | $timeout = 0; |
||
1535 | } |
||
1536 | |||
1537 | if ($method == '') { |
||
1538 | $method = $this->method; |
||
1539 | } |
||
1540 | |||
1541 | if (!$this->no_multicall) { |
||
1542 | $results = $this->_try_multicall($reqs, $timeout, $method); |
||
1543 | /// @todo how to handle the case of $this->return_type = xml? |
||
1544 | if (is_array($results)) { |
||
1545 | // System.multicall succeeded |
||
1546 | return $results; |
||
1547 | } else { |
||
1548 | // either system.multicall is unsupported by server, or the call failed for some other reason. |
||
1549 | // Feature creep: is there a way to tell apart unsupported multicall from other faults? |
||
1550 | if ($fallback) { |
||
1551 | // Don't try it next time... |
||
1552 | $this->no_multicall = true; |
||
1553 | } else { |
||
1554 | $result = $results; |
||
1555 | } |
||
1556 | } |
||
1557 | } else { |
||
1558 | // override fallback, in case careless user tries to do two |
||
1559 | // opposite things at the same time |
||
1560 | $fallback = true; |
||
1561 | } |
||
1562 | |||
1563 | $results = array(); |
||
1564 | if ($fallback) { |
||
1565 | // system.multicall is (probably) unsupported by server: emulate multicall via multiple requests |
||
1566 | /// @todo use curl multi_ functions to make this quicker (see the implementation in the parallel.php demo) |
||
1567 | foreach ($reqs as $req) { |
||
1568 | $results[] = $this->send($req, $timeout, $method); |
||
1569 | } |
||
1570 | } else { |
||
1571 | // user does NOT want to fallback on many single calls: since we should always return an array of responses, |
||
1572 | // we return an array with the same error repeated n times |
||
1573 | foreach ($reqs as $req) { |
||
1574 | $results[] = $result; |
||
1575 | } |
||
1576 | } |
||
1577 | |||
1578 | return $results; |
||
1579 | } |
||
1580 | |||
1581 | /** |
||
1582 | * Attempt to boxcar $reqs via system.multicall. |
||
1583 | * |
||
1584 | * @param Request[] $reqs |
||
1585 | * @param int $timeout |
||
1586 | * @param string $method |
||
1587 | * @return Response[]|Response a single Response when the call returned a fault / does not conform to what we expect |
||
1588 | * from a multicall response |
||
1589 | */ |
||
1590 | private function _try_multicall($reqs, $timeout, $method) |
||
1591 | { |
||
1592 | // Construct multicall request |
||
1593 | $calls = array(); |
||
1594 | foreach ($reqs as $req) { |
||
1595 | $call['methodName'] = new Value($req->method(), 'string'); |
||
1596 | $numParams = $req->getNumParams(); |
||
1597 | $params = array(); |
||
1598 | for ($i = 0; $i < $numParams; $i++) { |
||
1599 | $params[$i] = $req->getParam($i); |
||
1600 | } |
||
1601 | $call['params'] = new Value($params, 'array'); |
||
1602 | $calls[] = new Value($call, 'struct'); |
||
1603 | } |
||
1604 | $multiCall = new static::$requestClass('system.multicall'); |
||
1605 | $multiCall->addParam(new Value($calls, 'array')); |
||
1606 | |||
1607 | // Attempt RPC call |
||
1608 | $result = $this->send($multiCall, $timeout, $method); |
||
1609 | |||
1610 | if ($result->faultCode() != 0) { |
||
1611 | // call to system.multicall failed |
||
1612 | return $result; |
||
1613 | } |
||
1614 | |||
1615 | // Unpack responses. |
||
1616 | $rets = $result->value(); |
||
1617 | $response = array(); |
||
1618 | |||
1619 | if ($this->return_type == 'xml') { |
||
1620 | for ($i = 0; $i < count($reqs); $i++) { |
||
1621 | $response[] = new static::$responseClass($rets, 0, '', 'xml', $result->httpResponse()); |
||
1622 | } |
||
1623 | |||
1624 | } elseif ($this->return_type == 'phpvals') { |
||
1625 | if (!is_array($rets)) { |
||
1626 | // bad return type from system.multicall |
||
1627 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1628 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': not an array', 'phpvals', $result->httpResponse()); |
||
1629 | } |
||
1630 | $numRets = count($rets); |
||
1631 | if ($numRets != count($reqs)) { |
||
1632 | // wrong number of return values. |
||
1633 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1634 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'phpvals', |
||
1635 | $result->httpResponse()); |
||
1636 | } |
||
1637 | |||
1638 | for ($i = 0; $i < $numRets; $i++) { |
||
1639 | $val = $rets[$i]; |
||
1640 | if (!is_array($val)) { |
||
1641 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1642 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct", |
||
1643 | 'phpvals', $result->httpResponse()); |
||
1644 | } |
||
1645 | switch (count($val)) { |
||
1646 | case 1: |
||
1647 | if (!isset($val[0])) { |
||
1648 | // Bad value |
||
1649 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1650 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has no value", |
||
1651 | 'phpvals', $result->httpResponse()); |
||
1652 | } |
||
1653 | // Normal return value |
||
1654 | $response[$i] = new static::$responseClass($val[0], 0, '', 'phpvals', $result->httpResponse()); |
||
1655 | break; |
||
1656 | case 2: |
||
1657 | /// @todo remove usage of @: it is apparently quite slow |
||
1658 | $code = @$val['faultCode']; |
||
1659 | if (!is_int($code)) { |
||
1660 | /// @todo should we check that it is != 0? |
||
1661 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1662 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", |
||
1663 | 'phpvals', $result->httpResponse()); |
||
1664 | } |
||
1665 | $str = @$val['faultString']; |
||
1666 | if (!is_string($str)) { |
||
1667 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1668 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no FaultString", |
||
1669 | 'phpvals', $result->httpResponse()); |
||
1670 | } |
||
1671 | $response[$i] = new static::$responseClass(0, $code, $str, 'phpvals', $result->httpResponse()); |
||
1672 | break; |
||
1673 | default: |
||
1674 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1675 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", |
||
1676 | 'phpvals', $result->httpResponse()); |
||
1677 | } |
||
1678 | } |
||
1679 | |||
1680 | } else { |
||
1681 | // return type == 'xmlrpcvals' |
||
1682 | if ($rets->kindOf() != 'array') { |
||
1683 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1684 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array", 'xmlrpcvals', |
||
1685 | $result->httpResponse()); |
||
1686 | } |
||
1687 | $numRets = $rets->count(); |
||
1688 | if ($numRets != count($reqs)) { |
||
1689 | // wrong number of return values. |
||
1690 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1691 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'xmlrpcvals', |
||
1692 | $result->httpResponse()); |
||
1693 | } |
||
1694 | |||
1695 | foreach ($rets as $i => $val) { |
||
1696 | switch ($val->kindOf()) { |
||
1697 | case 'array': |
||
1698 | if ($val->count() != 1) { |
||
1699 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1700 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", |
||
1701 | 'phpvals', $result->httpResponse()); |
||
1702 | } |
||
1703 | // Normal return value |
||
1704 | $response[] = new static::$responseClass($val[0], 0, '', 'xmlrpcvals', $result->httpResponse()); |
||
1705 | break; |
||
1706 | case 'struct': |
||
1707 | if ($val->count() != 2) { |
||
1708 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1709 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", |
||
1710 | 'phpvals', $result->httpResponse()); |
||
1711 | } |
||
1712 | /** @var Value $code */ |
||
1713 | $code = $val['faultCode']; |
||
1714 | if ($code->kindOf() != 'scalar' || $code->scalarTyp() != 'int') { |
||
1715 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1716 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", |
||
1717 | 'xmlrpcvals', $result->httpResponse()); |
||
1718 | } |
||
1719 | /** @var Value $str */ |
||
1720 | $str = $val['faultString']; |
||
1721 | if ($str->kindOf() != 'scalar' || $str->scalarTyp() != 'string') { |
||
1722 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1723 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", |
||
1724 | 'xmlrpcvals', $result->httpResponse()); |
||
1725 | } |
||
1726 | $response[] = new static::$responseClass(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse()); |
||
1727 | break; |
||
1728 | default: |
||
1729 | return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], |
||
1730 | PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct", |
||
1731 | 'xmlrpcvals', $result->httpResponse()); |
||
1732 | } |
||
1733 | } |
||
1734 | } |
||
1735 | |||
1736 | return $response; |
||
1737 | } |
||
1738 | |||
1739 | // *** BC layer *** |
||
1740 | |||
1741 | /** |
||
1742 | * @deprecated |
||
1743 | * |
||
1744 | * @param Request $req |
||
1745 | * @param string $server |
||
1746 | * @param int $port |
||
1747 | * @param int $timeout |
||
1748 | * @param string $username |
||
1749 | * @param string $password |
||
1750 | * @param int $authType |
||
1751 | * @param string $proxyHost |
||
1752 | * @param int $proxyPort |
||
1753 | * @param string $proxyUsername |
||
1754 | * @param string $proxyPassword |
||
1755 | * @param int $proxyAuthType |
||
1756 | * @param string $method |
||
1757 | * @return Response |
||
1758 | */ |
||
1759 | protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0, $username = '', $password = '', |
||
1760 | $authType = 1, $proxyHost = '', $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, |
||
1761 | $method = 'http') |
||
1762 | { |
||
1763 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
1764 | |||
1765 | return $this->sendPayloadSocket($req, $server, $port, $timeout, $username, $password, $authType, null, null, |
||
1766 | null, null, $proxyHost, $proxyPort, $proxyUsername, $proxyPassword, $proxyAuthType, $method); |
||
1767 | } |
||
1768 | |||
1769 | /** |
||
1770 | * @deprecated |
||
1771 | * |
||
1772 | * @param Request $req |
||
1773 | * @param string $server |
||
1774 | * @param int $port |
||
1775 | * @param int $timeout |
||
1776 | * @param string $username |
||
1777 | * @param string $password |
||
1778 | * @param int $authType |
||
1779 | * @param string $cert |
||
1780 | * @param string $certPass |
||
1781 | * @param string $caCert |
||
1782 | * @param string $caCertDir |
||
1783 | * @param string $proxyHost |
||
1784 | * @param int $proxyPort |
||
1785 | * @param string $proxyUsername |
||
1786 | * @param string $proxyPassword |
||
1787 | * @param int $proxyAuthType |
||
1788 | * @param bool $keepAlive |
||
1789 | * @param string $key |
||
1790 | * @param string $keyPass |
||
1791 | * @param int $sslVersion |
||
1792 | * @return Response |
||
1793 | */ |
||
1794 | protected function sendPayloadHTTPS($req, $server, $port, $timeout = 0, $username = '', $password = '', |
||
1795 | $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, |
||
1796 | $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $keepAlive = false, $key = '', $keyPass = '', |
||
1797 | $sslVersion = 0) |
||
1798 | { |
||
1799 | $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); |
||
1800 | |||
1801 | return $this->sendPayloadCURL($req, $server, $port, $timeout, $username, |
||
1802 | $password, $authType, $cert, $certPass, $caCert, $caCertDir, $proxyHost, $proxyPort, |
||
1803 | $proxyUsername, $proxyPassword, $proxyAuthType, 'https', $keepAlive, $key, $keyPass, $sslVersion); |
||
1804 | } |
||
1805 | |||
1806 | /** |
||
1807 | * @deprecated |
||
1808 | * |
||
1809 | * @param Request $req |
||
1810 | * @param string $server |
||
1811 | * @param int $port |
||
1812 | * @param int $timeout |
||
1813 | * @param string $username |
||
1814 | * @param string $password |
||
1815 | * @param int $authType only value supported is 1 |
||
1816 | * @param string $cert |
||
1817 | * @param string $certPass |
||
1818 | * @param string $caCert |
||
1819 | * @param string $caCertDir |
||
1820 | * @param string $proxyHost |
||
1821 | * @param int $proxyPort |
||
1822 | * @param string $proxyUsername |
||
1823 | * @param string $proxyPassword |
||
1824 | * @param int $proxyAuthType only value supported is 1 |
||
1825 | * @param string $method 'http' (synonym for 'http10'), 'http10' or 'https' |
||
1826 | * @param string $key |
||
1827 | * @param string $keyPass @todo not implemented yet. |
||
1828 | * @param int $sslVersion |
||
1829 | * @return Response |
||
1830 | */ |
||
1831 | protected function sendPayloadSocket($req, $server, $port, $timeout = 0, $username = '', $password = '', |
||
1832 | $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, |
||
1833 | $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'http', $key = '', $keyPass = '', |
||
1834 | $sslVersion = 0) |
||
1835 | { |
||
1836 | $this->logDeprecationUnlessCalledBy('send'); |
||
1837 | |||
1838 | return $this->sendViaSocket($req, $method, $server, $port, $this->path, array( |
||
1839 | 'accepted_charset_encodings' => $this->accepted_charset_encodings, |
||
1840 | 'accepted_compression' => $this->accepted_compression, |
||
1841 | 'authtype' => $authType, |
||
1842 | 'cacert' => $caCert, |
||
1843 | 'cacertdir' => $caCertDir, |
||
1844 | 'cert' => $cert, |
||
1845 | 'certpass' => $certPass, |
||
1846 | 'cookies' => $this->cookies, |
||
1847 | 'debug' => $this->debug, |
||
1848 | 'extracurlopts' => $this->extracurlopts, |
||
1849 | 'extrasockopts' => $this->extrasockopts, |
||
1850 | 'keepalive' => $this->keepalive, |
||
1851 | 'key' => $key, |
||
1852 | 'keypass' => $keyPass, |
||
1853 | 'no_multicall' => $this->no_multicall, |
||
1854 | 'password' => $password, |
||
1855 | 'proxy' => $proxyHost, |
||
1856 | 'proxy_authtype' => $proxyAuthType, |
||
1857 | 'proxy_pass' => $proxyPassword, |
||
1858 | 'proxyport' => $proxyPort, |
||
1859 | 'proxy_user' => $proxyUsername, |
||
1860 | 'request_charset_encoding' => $this->request_charset_encoding, |
||
1861 | 'request_compression' => $this->request_compression, |
||
1862 | 'return_type' => $this->return_type, |
||
1863 | 'sslversion' => $sslVersion, |
||
1864 | 'timeout' => $timeout, |
||
1865 | 'username' => $username, |
||
1866 | 'user_agent' => $this->user_agent, |
||
1867 | 'use_curl' => $this->use_curl, |
||
1868 | 'verifyhost' => $this->verifyhost, |
||
1869 | 'verifypeer' => $this->verifypeer, |
||
1870 | )); |
||
1871 | } |
||
1872 | |||
1873 | /** |
||
1874 | * @deprecated |
||
1875 | * |
||
1876 | * @param Request $req |
||
1877 | * @param string $server |
||
1878 | * @param int $port |
||
1879 | * @param int $timeout |
||
1880 | * @param string $username |
||
1881 | * @param string $password |
||
1882 | * @param int $authType |
||
1883 | * @param string $cert |
||
1884 | * @param string $certPass |
||
1885 | * @param string $caCert |
||
1886 | * @param string $caCertDir |
||
1887 | * @param string $proxyHost |
||
1888 | * @param int $proxyPort |
||
1889 | * @param string $proxyUsername |
||
1890 | * @param string $proxyPassword |
||
1891 | * @param int $proxyAuthType |
||
1892 | * @param string $method 'http' (let curl decide), 'http10', 'http11', 'https', 'h2c' or 'h2' |
||
1893 | * @param bool $keepAlive |
||
1894 | * @param string $key |
||
1895 | * @param string $keyPass |
||
1896 | * @param int $sslVersion |
||
1897 | * @return Response |
||
1898 | */ |
||
1899 | protected function sendPayloadCURL($req, $server, $port, $timeout = 0, $username = '', $password = '', |
||
1900 | $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, |
||
1901 | $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '', |
||
1902 | $keyPass = '', $sslVersion = 0) |
||
1903 | { |
||
1904 | $this->logDeprecationUnlessCalledBy('send'); |
||
1905 | |||
1906 | return $this->sendViaCURL($req, $method, $server, $port, $this->path, array( |
||
1907 | 'accepted_charset_encodings' => $this->accepted_charset_encodings, |
||
1908 | 'accepted_compression' => $this->accepted_compression, |
||
1909 | 'authtype' => $authType, |
||
1910 | 'cacert' => $caCert, |
||
1911 | 'cacertdir' => $caCertDir, |
||
1912 | 'cert' => $cert, |
||
1913 | 'certpass' => $certPass, |
||
1914 | 'cookies' => $this->cookies, |
||
1915 | 'debug' => $this->debug, |
||
1916 | 'extracurlopts' => $this->extracurlopts, |
||
1917 | 'extrasockopts' => $this->extrasockopts, |
||
1918 | 'keepalive' => $keepAlive, |
||
1919 | 'key' => $key, |
||
1920 | 'keypass' => $keyPass, |
||
1921 | 'no_multicall' => $this->no_multicall, |
||
1922 | 'password' => $password, |
||
1923 | 'proxy' => $proxyHost, |
||
1924 | 'proxy_authtype' => $proxyAuthType, |
||
1925 | 'proxy_pass' => $proxyPassword, |
||
1926 | 'proxyport' => $proxyPort, |
||
1927 | 'proxy_user' => $proxyUsername, |
||
1928 | 'request_charset_encoding' => $this->request_charset_encoding, |
||
1929 | 'request_compression' => $this->request_compression, |
||
1930 | 'return_type' => $this->return_type, |
||
1931 | 'sslversion' => $sslVersion, |
||
1932 | 'timeout' => $timeout, |
||
1933 | 'username' => $username, |
||
1934 | 'user_agent' => $this->user_agent, |
||
1935 | 'use_curl' => $this->use_curl, |
||
1936 | 'verifyhost' => $this->verifyhost, |
||
1937 | 'verifypeer' => $this->verifypeer, |
||
1938 | )); |
||
1939 | } |
||
1940 | |||
1941 | /** |
||
1942 | * @deprecated |
||
1943 | * |
||
1944 | * @param $req |
||
1945 | * @param $server |
||
1946 | * @param $port |
||
1947 | * @param $timeout |
||
1948 | * @param $username |
||
1949 | * @param $password |
||
1950 | * @param $authType |
||
1951 | * @param $cert |
||
1952 | * @param $certPass |
||
1953 | * @param $caCert |
||
1954 | * @param $caCertDir |
||
1955 | * @param $proxyHost |
||
1956 | * @param $proxyPort |
||
1957 | * @param $proxyUsername |
||
1958 | * @param $proxyPassword |
||
1959 | * @param $proxyAuthType |
||
1960 | * @param $method |
||
1961 | * @param $keepAlive |
||
1962 | * @param $key |
||
1963 | * @param $keyPass |
||
1964 | * @param $sslVersion |
||
1965 | * @return false|\CurlHandle|resource |
||
1966 | */ |
||
1967 | protected function prepareCurlHandle($req, $server, $port, $timeout = 0, $username = '', $password = '', |
||
1968 | $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, |
||
1969 | $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '', |
||
1970 | $keyPass = '', $sslVersion = 0) |
||
1971 | { |
||
1972 | $this->logDeprecationUnlessCalledBy('sendViaCURL'); |
||
1973 | |||
1974 | return $this->createCURLHandle($req, $method, $server, $port, $this->path, array( |
||
1975 | 'accepted_charset_encodings' => $this->accepted_charset_encodings, |
||
1976 | 'accepted_compression' => $this->accepted_compression, |
||
1977 | 'authtype' => $authType, |
||
1978 | 'cacert' => $caCert, |
||
1979 | 'cacertdir' => $caCertDir, |
||
1980 | 'cert' => $cert, |
||
1981 | 'certpass' => $certPass, |
||
1982 | 'cookies' => $this->cookies, |
||
1983 | 'debug' => $this->debug, |
||
1984 | 'extracurlopts' => $this->extracurlopts, |
||
1985 | 'keepalive' => $keepAlive, |
||
1986 | 'key' => $key, |
||
1987 | 'keypass' => $keyPass, |
||
1988 | 'no_multicall' => $this->no_multicall, |
||
1989 | 'password' => $password, |
||
1990 | 'proxy' => $proxyHost, |
||
1991 | 'proxy_authtype' => $proxyAuthType, |
||
1992 | 'proxy_pass' => $proxyPassword, |
||
1993 | 'proxyport' => $proxyPort, |
||
1994 | 'proxy_user' => $proxyUsername, |
||
1995 | 'request_charset_encoding' => $this->request_charset_encoding, |
||
1996 | 'request_compression' => $this->request_compression, |
||
1997 | 'return_type' => $this->return_type, |
||
1998 | 'sslversion' => $sslVersion, |
||
1999 | 'timeout' => $timeout, |
||
2000 | 'username' => $username, |
||
2001 | 'user_agent' => $this->user_agent, |
||
2002 | 'use_curl' => $this->use_curl, |
||
2003 | 'verifyhost' => $this->verifyhost, |
||
2004 | 'verifypeer' => $this->verifypeer, |
||
2005 | )); |
||
2006 | } |
||
2007 | |||
2008 | // we have to make this return by ref in order to allow calls such as `$resp->_cookies['name'] = ['value' => 'something'];` |
||
2009 | public function &__get($name) |
||
2010 | { |
||
2011 | if (in_array($name, static::$options)) { |
||
2012 | $this->logDeprecation('Getting property Client::' . $name . ' is deprecated'); |
||
2013 | return $this->$name; |
||
2014 | } |
||
2015 | |||
2016 | switch ($name) { |
||
2017 | case 'errno': |
||
2018 | case 'errstr': |
||
2019 | case 'method': |
||
2020 | case 'server': |
||
2021 | case 'port': |
||
2022 | case 'path': |
||
2023 | $this->logDeprecation('Getting property Client::' . $name . ' is deprecated'); |
||
2024 | return $this->$name; |
||
2025 | default: |
||
2026 | /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... |
||
2027 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); |
||
2028 | trigger_error('Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); |
||
2029 | $result = null; |
||
2030 | return $result; |
||
2031 | } |
||
2032 | } |
||
2033 | |||
2034 | public function __set($name, $value) |
||
2035 | { |
||
2036 | if (in_array($name, static::$options)) { |
||
2037 | $this->logDeprecation('Setting property Client::' . $name . ' is deprecated'); |
||
2038 | $this->$name = $value; |
||
2039 | return; |
||
2040 | } |
||
2041 | |||
2042 | switch ($name) { |
||
2043 | case 'errno': |
||
2044 | case 'errstr': |
||
2045 | case 'method': |
||
2046 | case 'server': |
||
2047 | case 'port': |
||
2048 | case 'path': |
||
2049 | $this->logDeprecation('Setting property Client::' . $name . ' is deprecated'); |
||
2050 | $this->$name = $value; |
||
2051 | return; |
||
2052 | default: |
||
2053 | /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... |
||
2054 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); |
||
2055 | trigger_error('Undefined property via __set(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); |
||
2056 | } |
||
2057 | } |
||
2058 | |||
2059 | public function __isset($name) |
||
2060 | { |
||
2061 | if (in_array($name, static::$options)) { |
||
2062 | $this->logDeprecation('Checking property Client::' . $name . ' is deprecated'); |
||
2063 | return isset($this->$name); |
||
2064 | } |
||
2065 | |||
2066 | switch ($name) { |
||
2067 | case 'errno': |
||
2068 | case 'errstr': |
||
2069 | case 'method': |
||
2070 | case 'server': |
||
2071 | case 'port': |
||
2072 | case 'path': |
||
2073 | $this->logDeprecation('Checking property Client::' . $name . ' is deprecated'); |
||
2074 | return isset($this->$name); |
||
2075 | default: |
||
2076 | return false; |
||
2077 | } |
||
2078 | } |
||
2079 | |||
2080 | public function __unset($name) |
||
2081 | { |
||
2082 | if (in_array($name, static::$options)) { |
||
2083 | $this->logDeprecation('Unsetting property Client::' . $name . ' is deprecated'); |
||
2084 | unset($this->$name); |
||
2085 | return; |
||
2086 | } |
||
2087 | |||
2088 | switch ($name) { |
||
2089 | case 'errno': |
||
2090 | case 'errstr': |
||
2091 | case 'method': |
||
2092 | case 'server': |
||
2093 | case 'port': |
||
2094 | case 'path': |
||
2095 | $this->logDeprecation('Unsetting property Client::' . $name . ' is deprecated'); |
||
2096 | unset($this->$name); |
||
2097 | return; |
||
2098 | default: |
||
2099 | /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... |
||
2100 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); |
||
2101 | trigger_error('Undefined property via __unset(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); |
||
2102 | } |
||
2103 | } |
||
2104 | } |
||
2105 |