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