Skip to content

Commit fb239a3

Browse files
authored
1 parent 8e7d9a1 commit fb239a3

File tree

5 files changed

+225
-64
lines changed

5 files changed

+225
-64
lines changed

components/ILIAS/Mail/classes/class.ilMailLuceneSearcher.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ public function search(int $user_id, int $mail_folder_id): void
3535
}
3636

3737
try {
38-
$xml = ilRpcClientFactory::factory('RPCSearchHandler')->searchMail();
38+
$xml = ilRpcClientFactory::factory('RPCSearchHandler')->searchMail(
39+
CLIENT_ID . '_' . $this->settings->get('inst_id', '0'),
40+
$user_id,
41+
$this->query_parser->getQuery(),
42+
$mail_folder_id
43+
);
3944
} catch (Exception $e) {
4045
ilLoggerFactory::getLogger('mail')->critical($e->getMessage());
4146
throw $e;

components/ILIAS/WebServices/RPC/classes/class.ilRpcClient.php

Lines changed: 209 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,12 @@
1919
declare(strict_types=1);
2020

2121
/**
22-
* Class ilRpcClient
23-
*
2422
* @author Fabian Wolf <wolf@leifos.com>
25-
* @ingroup ServicesWebServicesRPC
2623
*
2724
* List of all known RPC methods...
2825
*
2926
* RPCIndexHandler:
30-
* @method void index() index(string $client, bool $bool) Prefix/Package: RPCIndexHandler
27+
* @method bool index() index(string $client, bool $bool) Prefix/Package: RPCIndexHandler
3128
* @method void indexObjects() indexObjects(string $client, array $object_ids) Prefix/Package: RPCIndexHandler
3229
*
3330
* RPCTransformationHandler:
@@ -48,56 +45,38 @@ class ilRpcClient
4845
protected string $url;
4946
protected string $prefix = '';
5047
protected int $timeout = 0;
51-
protected string $encoding = '';
5248

5349
protected ilLogger $logger;
5450

5551
/**
56-
* ilRpcClient constructor.
57-
* @param string $a_url URL to connect to
58-
* @param string $a_prefix Optional prefix for method names
59-
* @param int $a_timeout The maximum number of seconds to allow ilRpcClient to connect.
60-
* @param string $a_encoding Character encoding
52+
* @param string $url URL to connect to
53+
* @param string $prefix Optional prefix for method names
54+
* @param int $timeout The maximum number of seconds to allow ilRpcClient to connect.
6155
* @throws ilRpcClientException
6256
*/
63-
public function __construct(string $a_url, string $a_prefix = '', int $a_timeout = 0, string $a_encoding = 'utf-8')
57+
public function __construct(string $url, string $prefix = '', int $timeout = 0)
6458
{
6559
global $DIC;
6660

6761
$this->logger = $DIC->logger()->wsrv();
68-
69-
if (!extension_loaded('xmlrpc')) {
70-
ilLoggerFactory::getLogger('wsrv')->error('RpcClient Xmlrpc extension not enabled');
71-
throw new ilRpcClientException('Xmlrpc extension not enabled.', 50);
72-
}
73-
74-
$this->url = $a_url;
75-
$this->prefix = $a_prefix;
76-
$this->timeout = $a_timeout;
77-
$this->encoding = $a_encoding;
62+
$this->url = $url;
63+
$this->prefix = $prefix;
64+
$this->timeout = $timeout;
7865
}
7966

8067
/**
81-
* Magic caller to all RPC functions
82-
*
83-
* @param string $a_method Method name
84-
* @param array $a_params Argument array
85-
* @return mixed Returns either an array, or an integer, or a string, or a boolean according to the response returned by the XMLRPC method.
68+
* @param string $method Method name
69+
* @param (string|int|bool|int[])[] $parameters Argument array
70+
* @return string|stdClass Depends on the response returned by the XMLRPC method.
8671
* @throws ilRpcClientException
8772
*/
88-
public function __call(string $a_method, array $a_params)
73+
public function __call(string $method, array $parameters): string|bool|stdClass
8974
{
9075
//prepare xml post data
91-
$method_name = str_replace('_', '.', $this->prefix . $a_method);
92-
$rpc_options = array(
93-
'verbosity' => 'newlines_only',
94-
'escaping' => 'markup'
95-
);
76+
$method_name = str_replace('_', '.', $this->prefix . $method);
77+
78+
$post_data = $this->encodeRequest($method_name, $parameters);
9679

97-
if ($this->encoding) {
98-
$rpc_options['encoding'] = $this->encoding;
99-
}
100-
$post_data = xmlrpc_encode_request($method_name, $a_params, $rpc_options);
10180
//try to connect to the given url
10281
try {
10382
$curl = new ilCurlConnection($this->url);
@@ -111,7 +90,7 @@ public function __call(string $a_method, array $a_params)
11190
$curl->setOpt(CURLOPT_TIMEOUT, $this->timeout);
11291
}
11392
$this->logger->debug('RpcClient request to ' . $this->url . ' / ' . $method_name);
114-
$xml_resp = $curl->exec();
93+
$xml_response = $curl->exec();
11594
} catch (ilCurlConnectionException $e) {
11695
$this->logger->error(
11796
'RpcClient could not connect to ' . $this->url . ' ' .
@@ -120,18 +99,200 @@ public function __call(string $a_method, array $a_params)
12099
throw new ilRpcClientException($e->getMessage(), $e->getCode());
121100
}
122101

123-
//prepare output, throw exception if rpc fault is detected
124-
$resp = xmlrpc_decode($xml_resp, $this->encoding);
102+
//return output, throw exception if rpc fault is detected
103+
return $this->handleResponse($xml_response);
104+
}
125105

126-
//xmlrpc_is_fault can just handle arrays as response
127-
if (is_array($resp) && xmlrpc_is_fault($resp)) {
128-
$this->logger->error('RpcClient recieved error ' . $resp['faultCode'] . ': ' . $resp['faultString']);
129-
throw new ilRpcClientException(
130-
'RPC-Server returned fault message: ' .
131-
$resp['faultString'],
132-
$resp['faultCode']
133-
);
106+
/**
107+
* @param (string|int|bool|int[])[] $parameters
108+
* @throws ilRpcClientException
109+
*/
110+
protected function encodeRequest(string $method, array $parameters): string
111+
{
112+
$xml = new DOMDocument('1.0', 'UTF-8');
113+
$method_call = $xml->createElement('methodCall');
114+
$method_name = $xml->createElement('methodName', $method);
115+
$params = $xml->createElement('params');
116+
117+
foreach ($parameters as $parameter) {
118+
match (true) {
119+
is_string($parameter) => $encoded_parameter = $this->encodeString($parameter),
120+
is_int($parameter) => $encoded_parameter = $this->encodeInteger($parameter),
121+
is_bool($parameter) => $encoded_parameter = $this->encodeBoolean($parameter),
122+
$this->isListOfIntegers($parameter) => $encoded_parameter = $this->encodeListOfIntegers(...$parameter),
123+
default => throw new ilRpcClientException(
124+
'Invalid parameter type, only string, int, bool, and int[] are supported.'
125+
)
126+
};
127+
$params->appendChild($xml->importNode($this->wrapParameter($encoded_parameter)->documentElement, true));
128+
}
129+
130+
$method_call->appendChild($method_name);
131+
$method_call->appendChild($params);
132+
133+
$xml->appendChild($method_call);
134+
return $xml->saveXML();
135+
}
136+
137+
protected function isListOfIntegers(mixed $parameter): bool
138+
{
139+
if (!is_array($parameter)) {
140+
return false;
141+
}
142+
foreach ($parameter as $entries) {
143+
if (!is_int($entries)) {
144+
return false;
145+
}
146+
}
147+
return true;
148+
}
149+
150+
protected function wrapParameter(DOMDocument $encoded_parameter): DOMDocument
151+
{
152+
$xml = new DOMDocument('1.0', 'UTF-8');
153+
$param = $xml->createElement('param');
154+
$value = $xml->createElement('value');
155+
156+
$value->appendChild($xml->importNode($encoded_parameter->documentElement, true));
157+
$param->appendChild($value);
158+
159+
$xml->appendChild($param);
160+
return $xml;
161+
}
162+
163+
protected function encodeString(string $parameter): DOMDocument
164+
{
165+
$xml = new DOMDocument('1.0', 'UTF-8');
166+
$xml->appendChild($xml->createElement('string', $parameter));
167+
return $xml;
168+
}
169+
170+
protected function encodeInteger(int $parameter): DOMDocument
171+
{
172+
$xml = new DOMDocument('1.0', 'UTF-8');
173+
$xml->appendChild($xml->createElement('int', (string) $parameter));
174+
return $xml;
175+
}
176+
177+
protected function encodeBoolean(bool $parameter): DOMDocument
178+
{
179+
$xml = new DOMDocument('1.0', 'UTF-8');
180+
$xml->appendChild($xml->createElement('boolean', $parameter ? '1' : '0'));
181+
return $xml;
182+
}
183+
184+
protected function encodeListOfIntegers(int ...$parameters): DOMDocument
185+
{
186+
$xml = new DOMDocument('1.0', 'UTF-8');
187+
$array = $xml->createElement('array');
188+
$data = $xml->createElement('data');
189+
190+
foreach ($parameters as $parameter) {
191+
$value = $xml->createElement('value');
192+
$value->appendChild($xml->importNode($this->encodeInteger($parameter)->documentElement, true));
193+
$data->appendChild($value);
194+
}
195+
$array->appendChild($data);
196+
197+
$xml->appendChild($array);
198+
return $xml;
199+
}
200+
201+
/**
202+
* Returns decoded response if not faulty, otherwise throws exception.
203+
* @throws ilRpcClientException
204+
*/
205+
public function handleResponse(string $xml): string|bool|stdClass
206+
{
207+
$response = new DOMDocument('1.0', 'UTF-8');
208+
$response->preserveWhiteSpace = false;
209+
$response->loadXML($xml);
210+
211+
if (!$response) {
212+
throw new ilRpcClientException('Invalid XML response');
213+
}
214+
215+
$response_body = $response->documentElement->childNodes->item(0);
216+
217+
if ($response_body === null) {
218+
throw new ilRpcClientException('Empty response');
219+
}
220+
221+
$this->logger->dump($response_body);
222+
223+
return match ($response_body->nodeName) {
224+
'params' => $this->decodeOKResponse($response_body),
225+
'fault' => $this->handleFaultResponse($response_body),
226+
default => throw new ilRpcClientException('Unexpected element in response: ' . get_class($response_body)),
227+
};
228+
}
229+
230+
protected function decodeOKResponse(DOMElement $response_body): string|bool|stdClass
231+
{
232+
$param_child = $response_body->getElementsByTagName('value')->item(0)?->childNodes?->item(0);
233+
234+
if ($param_child === null) {
235+
throw new ilRpcClientException('No value in response');
236+
}
237+
238+
return match ($param_child->nodeName) {
239+
'string' => $this->decodeString($param_child),
240+
'#text' => $this->decodeString($param_child), // org.apache.xmlrpc returns java strings as unwrapped text node
241+
'base64' => $this->decodeBase64($param_child),
242+
'boolean' => $this->decodeBoolean($param_child),
243+
default => throw new ilRpcClientException('Unexpected element in response value: ' . $param_child->nodeName),
244+
};
245+
}
246+
247+
protected function decodeString(DOMNode $string): string
248+
{
249+
return (string) $string->nodeValue;
250+
}
251+
252+
protected function decodeBase64(DOMNode $base64): stdClass
253+
{
254+
return (object) base64_decode((string) $base64->nodeValue);
255+
}
256+
257+
protected function decodeBoolean(DOMNode $boolean): bool
258+
{
259+
return (bool) $boolean->nodeValue;
260+
}
261+
262+
/**
263+
* @throws ilRpcClientException
264+
*/
265+
protected function handleFaultResponse(DOMElement $response_body): string
266+
{
267+
$fault_code = null;
268+
$fault_string = null;
269+
270+
$members = $response_body->getElementsByTagName('member');
271+
foreach ($members as $member) {
272+
$name = $member->getElementsByTagName('name')->item(0)?->nodeValue;
273+
if ($name === 'faultCode') {
274+
if ($fault_code !== null) {
275+
throw new ilRpcClientException('Multiple codes in fault response.');
276+
}
277+
$fault_code = (int) $member->getElementsByTagName('int')->item(0)?->nodeValue;
278+
}
279+
if ($name === 'faultString') {
280+
if ($fault_string !== null) {
281+
throw new ilRpcClientException('Multiple strings in fault response.');
282+
}
283+
$fault_string = $member->getElementsByTagName('string')->item(0)?->nodeValue;
284+
}
285+
}
286+
287+
if ($fault_code === null || $fault_string === null) {
288+
throw new ilRpcClientException('No code or no string in fault respsonse');
134289
}
135-
return $resp;
290+
291+
$this->logger->error('RpcClient recieved error ' . $fault_code . ': ' . $fault_string);
292+
throw new ilRpcClientException(
293+
'RPC-Server returned fault message: ' .
294+
$fault_string,
295+
$fault_code
296+
);
136297
}
137298
}

components/ILIAS/WebServices/RPC/classes/class.ilRpcClientFactory.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,22 @@
1717
*********************************************************************/
1818

1919
declare(strict_types=1);
20+
2021
/**
21-
* @classDescription Factory for ILIAS rpc client
2222
* @author Stefan Meyer <meyer@leifos.com>
2323
*/
2424
class ilRpcClientFactory
2525
{
2626
/**
27-
* Creates an ilRpcClient instance to our ilServer
28-
*
29-
* @param string $a_package Package name
30-
* @param int $a_timeout The maximum number of seconds to allow ilRpcClient to connect.
31-
* @return ilRpcClient
27+
* @param string $package Package name
28+
* @param int $timeout The maximum number of seconds to allow ilRpcClient to connect.
3229
*/
33-
public static function factory(string $a_package, int $a_timeout = 0): ilRpcClient
30+
public static function factory(string $package, int $timeout = 0): ilRpcClient
3431
{
3532
return new ilRpcClient(
3633
ilRPCServerSettings::getInstance()->getServerUrl(),
37-
$a_package . '.',
38-
$a_timeout,
39-
'UTF-8'
34+
$package . '.',
35+
$timeout
4036
);
4137
}
4238
}

components/ILIAS/WebServices/RPC/lib/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ This Java server has been tested with Open JDK Java Runtime 17.
2727
To be able to index and search for non-ASCII characters your system should
2828
support UTF-8 encodings.
2929

30-
PHP curl and xmlrpc are required for using the Java server features.
30+
PHP curl is required for using the Java server features.
3131

3232
On Debian-based systems try:
3333

3434
````shell
35-
> apt-get install php-curl php-xmlrpc openjdk-17-jdk-headless
35+
> apt-get install php-curl openjdk-17-jdk-headless
3636
````
3737
Dependencies and the build process is managed via maven
3838
```shell
@@ -180,4 +180,4 @@ if no installation id is given.
180180

181181
```shell
182182
> java -jar /foo/bar/target/ilServer.jar /foo/bar/ilServer.ini search <CLIENT_INFO> "ilias"
183-
```
183+
```

docs/configuration/install.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ gd, dom, xsl, pdo, pdo_mysql, curl, json, simplexml, libxml, xml, zip, imagick,
118118

119119
**Optional PHP Extensions:**
120120

121-
* `xmlrpc` for the ILIAS RPC server
122121
* `soap` for SOAP user administration
123122
* `ldap` for LDAP user authentication
124123

@@ -128,7 +127,7 @@ Alternatively, it can be obtained directly from [getcomposer.org](https://getcom
128127
Composer may be optional when using the prepacked ILIAS from [Download & Releases](https://docu.ilias.de/go/pg/197851_35), but it is necessary when using plugins to rebuild the PHP autoload classmap.
129128

130129
```shell
131-
apt install apache2 libapache2-mod-php php php-gd php-xsl php-imagick php-curl php-mysql php-xmlrpc php-soap php-ldap composer
130+
apt install apache2 libapache2-mod-php php php-gd php-xsl php-imagick php-curl php-mysql php-soap php-ldap composer
132131
```
133132

134133
Create a directory for the html sources (e.g. `/var/www/ilias`) which is referenced in the apache2 vhost and also a directory outside the web servers docroot (e.g. `/var/www/files`) for files stored by ILIAS.

0 commit comments

Comments
 (0)