Skip to content

Commit f76cf6b

Browse files
committed
update xmlrpc to v4.11.2
1 parent da1975c commit f76cf6b

6 files changed

Lines changed: 121 additions & 35 deletions

File tree

external/xmlrpc/NEWS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
## XML-RPC for PHP version 4.11.2 - 2025/6/20
2+
3+
* fixed: the Client would not honour the timeout when in Socket mode (issue #127, thanks to @dima-bzzz for PR #84)
4+
5+
* fixed: the Client would use a timeout off by 1 second when in CURL mode
6+
7+
* fixed: the Client would not honour options set via `OPT_EXTRA_SOCKET_OPTS`
8+
9+
* fixed: teach the vm.sh script how to properly configure test envs using Ubuntu versions from xenial (16) to noble (24)
10+
11+
* improved: default the local testing container to using PHP 8.1 on Ubuntu Jammy
12+
13+
* improved: make the vm.sh script more amenable to be used in parallel / quickly switching between different test envs
14+
15+
* improved: added script `tests/ci/matrix.sh` to run the testsuite locally against a matrix of environments, kinda
16+
like the test matrix which is run on commit on github
17+
18+
119
## XML-RPC for PHP version 4.11.1 - 2025/1/17
220

321
* fixed: removed one warning emitted by the Server on php 8.4 and later (issue #125, thanks @ziegenberg)

external/xmlrpc/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ Use of this software is subject to the terms in the [license.txt](license.txt) f
6161
[![Latest Stable Version](https://poser.pugx.org/phpxmlrpc/phpxmlrpc/v/stable)](https://packagist.org/packages/phpxmlrpc/phpxmlrpc)
6262
[![Total Downloads](https://poser.pugx.org/phpxmlrpc/phpxmlrpc/downloads)](https://packagist.org/packages/phpxmlrpc/phpxmlrpc)
6363

64-
[![Build Status](https://github.com/gggeek/phpxmlrpc/actions/workflows/ci.yaml/badge.svg)](https://github.com/gggeek/phpxmlrpc/actions/workflows/ci.yml)
64+
[![Build Status](https://github.com/gggeek/phpxmlrpc/actions/workflows/ci.yaml/badge.svg)](https://github.com/gggeek/phpxmlrpc/actions/workflows/ci.yaml)
6565
[![Code Coverage](https://codecov.io/gh/gggeek/phpxmlrpc/branch/master/graph/badge.svg)](https://app.codecov.io/gh/gggeek/phpxmlrpc)

external/xmlrpc/src/Client.php

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,12 @@ class Client
143143
*/
144144
protected $verifyhost = 2;
145145
/**
146-
* @var int
146+
* @var int Corresponds to CURL_SSLVERSION_DEFAULT. Other CURL_SSLVERSION_ values are supported when in curl mode,
147+
* and in socket mode different values from 0 to 7, matching the corresponding curl value. Old php versions
148+
* do not support all values, php 5.4 and 5.5 do not support any in fact.
149+
* NB: please do not use any version lower than TLS 1.3 (value: 7) as they are considered insecure.
147150
*/
148-
protected $sslversion = 0; // corresponds to CURL_SSLVERSION_DEFAULT. Other CURL_SSLVERSION_ values are supported
151+
protected $sslversion = 0;
149152
/**
150153
* @var string
151154
*/
@@ -582,9 +585,12 @@ public function setSSLVerifyHost($i)
582585
}
583586

584587
/**
585-
* Set attributes for SSL communication: SSL version to use. Best left at 0 (default value): let cURL decide
588+
* Set attributes for SSL communication: SSL version to use. Best left at 0 (default value): let PHP decide.
586589
*
587-
* @param int $i see CURL_SSLVERSION_ constants
590+
* @param int $i use CURL_SSLVERSION_ constants. When in socket mode, use the same values: 2 (SSLv2) to 7 (TLSv1.3),
591+
* 0 for auto (note that old php versions do not support all TLS versions).
592+
* Note that, in curl mode, the actual ssl version in use might be higher than requested.
593+
* NB: please do not use any version lower than TLS 1.3 as they are considered insecure.
588594
* @return $this
589595
* @deprecated use setOption
590596
*/
@@ -713,6 +719,7 @@ public function setCurlOptions($options)
713719

714720
/**
715721
* @param int $useCurlMode self::USE_CURL_ALWAYS, self::USE_CURL_AUTO or self::USE_CURL_NEVER
722+
* In 'auto' mode, curl is picked up based on features used, such as fe. NTLM auth, or https
716723
* @return $this
717724
* @deprecated use setOption
718725
*/
@@ -724,7 +731,6 @@ public function setUseCurl($useCurlMode)
724731
return $this;
725732
}
726733

727-
728734
/**
729735
* Set user-agent string that will be used by this client instance in http headers sent to the server.
730736
*
@@ -745,7 +751,8 @@ public function setUserAgent($agentString)
745751
/**
746752
* @param null|int $component allowed values: PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_PATH
747753
* @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);
754+
* (in which case the default port for http/https will be used); the url scheme component will
755+
* reflect the `$method` used in the constructor, so it might not be http or https
749756
* @throws ValueErrorException on unsupported component
750757
*/
751758
public function getUrl($component = null)
@@ -796,7 +803,10 @@ public function getUrl($component = null)
796803
* will be used. If that is 0, a platform specific timeout will apply.
797804
* This timeout value is passed to fsockopen(). It is also used for detecting server
798805
* timeouts during communication (i.e. if the server does not send anything to the client
799-
* for $timeout seconds, the connection will be closed).
806+
* for $timeout seconds, the connection will be closed). When in CURL mode, this is the
807+
* CURL timeout.
808+
* NB: in both CURL and Socket modes, some conditions might lead to the client not
809+
* respecting the given timeout. Eg. if the network is not connected
800810
* @param string $method deprecated. Use the same value in the constructor instead.
801811
* Valid values are 'http', 'http11', 'https', 'h2' and 'h2c'. If left empty,
802812
* the http protocol chosen during creation of the object will be used.
@@ -839,12 +849,17 @@ public function send($req, $timeout = 0, $method = '')
839849
// where req is a Request
840850
$req->setDebug($this->debug);
841851

842-
/// @todo we could be smarter about this and not force usage of curl for https if not present as well as use the
843-
/// presence of curl_extra_opts or socket_extra_opts as a hint
852+
/// @todo we could be smarter about this:
853+
/// - not force usage of curl if it is not present
854+
/// - not force usage of curl for https (minor BC)
855+
/// - use the presence of curl_extra_opts or socket_extra_opts as a hint
844856
$useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO && (
845857
in_array($method, array('https', 'http11', 'h2c', 'h2')) ||
846858
($this->username != '' && $this->authtype != 1) ||
847859
($this->proxy != '' && $this->proxy_user != '' && $this->proxy_authtype != 1)
860+
// uncomment the following if not forcing curl always for 'https'
861+
//|| ($this->sslversion == 7 && PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION == '7.3')
862+
//|| ($this->sslversion != 0 && PHP_MAJOR_VERSION < 6)
848863
));
849864

850865
// BC - we go through sendPayloadCURL/sendPayloadSocket in case some subclass reimplemented those
@@ -978,20 +993,32 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
978993
$connectServer = $opts['proxy'];
979994
$connectPort = $opts['proxyport'];
980995
$transport = 'tcp';
981-
/// @todo check: should we not use https in some cases?
982-
$uri = 'http://' . $server . ':' . $port . $path;
996+
$protocol = $method;
997+
if ($method === 'http10' || $method === 'http11') {
998+
$protocol = 'http';
999+
} elseif ($method === 'h2') {
1000+
$protocol = 'https';
1001+
} else if (strpos($protocol, ':') !== false) {
1002+
$this->getLogger()->error('XML-RPC: ' . __METHOD__ . ": warning - attempted hacking attempt?. The protocol requested for the call is: '$protocol'");
1003+
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], PhpXmlRpc::$xmlrpcerr['unsupported_option'] .
1004+
" attempted hacking attempt?. The protocol requested for the call is: '$protocol'");
1005+
}
1006+
/// @todo this does not work atm (tested at least with an http proxy forwarding to an https server) - we
1007+
/// should implement the CONNECT protocol
1008+
$uri = $protocol . '://' . $server . ':' . $port . $path;
9831009
if ($opts['proxy_user'] != '') {
9841010
if ($opts['proxy_authtype'] != 1) {
9851011
$this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0');
9861012
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'],
987-
PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth to proxy is supported with HTTP 1.0');
1013+
PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth to proxy is supported with socket transport');
9881014
}
9891015
$proxyCredentials = 'Proxy-Authorization: Basic ' . base64_encode($opts['proxy_user'] . ':' .
9901016
$opts['proxy_pass']) . "\r\n";
9911017
}
9921018
} else {
9931019
$connectServer = $server;
9941020
$connectPort = $port;
1021+
/// @todo should we add support for 'h2' method? If so, is it 'tls' or 'tcp' ?
9951022
$transport = ($method === 'https') ? 'tls' : 'tcp';
9961023
$uri = $path;
9971024
}
@@ -1014,7 +1041,8 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
10141041
}
10151042

10161043
// omit port if default
1017-
if (($port == 80 && in_array($method, array('http', 'http10'))) || ($port == 443 && $method == 'https')) {
1044+
/// @todo add handling of http2, h2c in case they start being supported by fosckopen
1045+
if (($port == 80 && in_array($method, array('http', 'http10', 'http11'))) || ($port == 443 && $method == 'https')) {
10181046
$port = '';
10191047
} else {
10201048
$port = ':' . $port;
@@ -1059,27 +1087,35 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
10591087
$contextOptions['ssl']['verify_peer_name'] = $opts['verifypeer'];
10601088

10611089
if ($opts['sslversion'] != 0) {
1062-
/// @see https://www.php.net/manual/en/function.curl-setopt.php, https://www.php.net/manual/en/migration56.openssl.php
1090+
/// @see https://www.php.net/manual/en/curl.constants.php,
1091+
/// https://www.php.net/manual/en/function.stream-socket-enable-crypto.php
1092+
/// https://www.php.net/manual/en/migration56.openssl.php,
1093+
/// https://wiki.php.net/rfc/improved-tls-constants
10631094
switch($opts['sslversion']) {
1064-
/// @todo what does this map to? 1.0-1.3?
1065-
//case 1: // TLSv1
1066-
// break;
1095+
case 1: // TLSv1x
1096+
if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
1097+
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLS_CLIENT;
1098+
} else {
1099+
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'],
1100+
PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': TLS-any only is supported with PHP 7.2 or later');
1101+
}
1102+
break;
10671103
case 2: // SSLv2
10681104
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
10691105
break;
10701106
case 3: // SSLv3
10711107
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
10721108
break;
1073-
case 4: // TLSv1.0
1109+
case 4: // TLSv1.0 - not always available?
10741110
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
10751111
break;
1076-
case 5: // TLSv1.1
1112+
case 5: // TLSv1.1 - not always available?
10771113
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
10781114
break;
1079-
case 6: // TLSv1.2
1115+
case 6: // TLSv1.2 - not always available?
10801116
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
10811117
break;
1082-
case 7: // TLSv1.3
1118+
case 7: // TLSv1.3 - not always available
10831119
if (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT')) {
10841120
$contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
10851121
} else {
@@ -1094,7 +1130,7 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
10941130
}
10951131
}
10961132

1097-
foreach ($opts['extracurlopts'] as $proto => $protoOpts) {
1133+
foreach ($opts['extrasockopts'] as $proto => $protoOpts) {
10981134
foreach ($protoOpts as $key => $val) {
10991135
$contextOptions[$proto][$key] = $val;
11001136
}
@@ -1111,6 +1147,13 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
11111147
$this->errno = 0;
11121148
$this->errstr = '';
11131149

1150+
/// @todo using `error_get_last` does not give us very detailed messages for connections errors, eg. for ssl
1151+
/// problems on php 5.6 we get 'Connect error: stream_socket_client(): unable to connect to tls://localhost:443 (Unknown error) (0)',
1152+
/// versus the more detailed warnings 'PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
1153+
/// error:0A0C0103:SSL routines::internal error in /home/docker/workspace/src/Client.php on line 1121
1154+
/// PHP Warning: stream_socket_client(): Failed to enable crypto in /home/docker/workspace/src/Client.php on line 1121
1155+
/// PHP Warning: stream_socket_client(): unable to connect to tls://localhost:443 (Unknown error) in /home/docker/workspace/src/Client.php on line 1121'
1156+
/// This could be obviated by removing the `@` and capturing warnings via ob_start and co
11141157
$fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $connectTimeout,
11151158
STREAM_CLIENT_CONNECT, $context);
11161159
if ($fp) {
@@ -1124,24 +1167,40 @@ protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
11241167
}
11251168

11261169
$this->errstr = 'Connect error: ' . $this->errstr;
1127-
$r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')');
1128-
1129-
return $r;
1170+
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')');
11301171
}
11311172

1173+
/// @todo from here onwards, we can inject the results of stream_get_meta_data in the response. We could
1174+
/// do that f.e. only in new debug level 3, or starting at v1
1175+
11321176
if (!fputs($fp, $op, strlen($op))) {
11331177
fclose($fp);
11341178
$this->errstr = 'Write error';
11351179
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
11361180
}
11371181

1182+
$info = stream_get_meta_data($fp);
1183+
if ($info['timed_out']) {
1184+
fclose($fp);
1185+
$this->errstr = 'Write timeout';
1186+
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
1187+
}
1188+
11381189
// Close socket before parsing.
11391190
// It should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
11401191
$ipd = '';
11411192
do {
11421193
// shall we check for $data === FALSE?
11431194
// as per the manual, it signals an error
11441195
$ipd .= fread($fp, 32768);
1196+
1197+
$info = stream_get_meta_data($fp);
1198+
if ($info['timed_out']) {
1199+
fclose($fp);
1200+
$this->errstr = 'Read timeout';
1201+
return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
1202+
}
1203+
11451204
} while (!feof($fp));
11461205
fclose($fp);
11471206

@@ -1364,11 +1423,13 @@ protected function createCURLHandle($req, $method, $server, $port, $path, $opts)
13641423
$headers[] = 'Expect:';
13651424

13661425
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1367-
// timeout is borked
1426+
// previous note: "timeout is borked" (on some old php/curl versions? It seems to work on 8.1. Maybe the issue
1427+
// has to do with dns resolution...)
13681428
if ($opts['timeout']) {
1369-
curl_setopt($curl, CURLOPT_TIMEOUT, $opts['timeout'] == 1 ? 1 : $opts['timeout'] - 1);
1429+
curl_setopt($curl, CURLOPT_TIMEOUT, $opts['timeout']);
13701430
}
13711431

1432+
// nb: for 'https' we leave it up to curl to decide
13721433
switch ($method) {
13731434
case 'http10':
13741435
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
@@ -1495,7 +1556,7 @@ protected function createCURLHandle($req, $method, $server, $port, $path, $opts)
14951556
* docs for the send() method". Please use setOption instead to set a timeout
14961557
* @param string $method deprecated. Was: "the http protocol variant to be used. See the details in the docs for the send() method."
14971558
* Please use the constructor to set an http protocol variant.
1498-
* @param boolean $fallback deprecated. Was: "w"hen true, upon receiving an error during multicall, multiple single
1559+
* @param boolean $fallback deprecated. Was: "when true, upon receiving an error during multicall, multiple single
14991560
* calls will be attempted"
15001561
* @return Response[]
15011562
*/
@@ -1712,6 +1773,8 @@ private function _try_multicall($reqs, $timeout, $method)
17121773
// *** BC layer ***
17131774

17141775
/**
1776+
* NB: always goes via socket, never curl
1777+
*
17151778
* @deprecated
17161779
*
17171780
* @param Request $req
@@ -1740,6 +1803,8 @@ protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0, $userna
17401803
}
17411804

17421805
/**
1806+
* NB: always goes via curl, never socket
1807+
*
17431808
* @deprecated
17441809
*
17451810
* @param Request $req
@@ -1798,7 +1863,7 @@ protected function sendPayloadHTTPS($req, $server, $port, $timeout = 0, $usernam
17981863
* @param string $method 'http' (synonym for 'http10'), 'http10' or 'https'
17991864
* @param string $key
18001865
* @param string $keyPass @todo not implemented yet.
1801-
* @param int $sslVersion @todo not implemented yet. See http://php.net/manual/en/migration56.openssl.php
1866+
* @param int $sslVersion
18021867
* @return Response
18031868
*/
18041869
protected function sendPayloadSocket($req, $server, $port, $timeout = 0, $username = '', $password = '',

external/xmlrpc/src/Helper/XMLParser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ public function parse($data, $returnType = self::RETURN_XMLRPCVALS, $accept = 3,
279279
} catch (\Error $e) {
280280
xml_parser_free($parser);
281281
$this->current_parsing_options = array();
282-
//$this->accept = $prevAccept;
283-
/// @todo should we set $this->_xh['isf'] and $this->_xh['isf_reason'] ?
282+
//$this->accept = $prevAccept;
283+
/// @todo should we set $this->_xh['isf'] and $this->_xh['isf_reason'] ?
284284
throw $e;
285285
}
286286

external/xmlrpc/src/PhpXmlRpc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class PhpXmlRpc
119119
/**
120120
* @var string
121121
*/
122-
public static $xmlrpcVersion = "4.11.1";
122+
public static $xmlrpcVersion = "4.11.2";
123123

124124
/**
125125
* @var int

0 commit comments

Comments
 (0)