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