This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | IXR - The Inutio XML-RPC Library - (c) Incutio Ltd 2002 |
||
5 | Version 1.62WP - Simon Willison, 11th July 2003 (htmlentities -> htmlspecialchars) |
||
6 | ^^^^^^ (We've made some changes) |
||
7 | Site: http://scripts.incutio.com/xmlrpc/ |
||
8 | Manual: http://scripts.incutio.com/xmlrpc/manual.php |
||
9 | Made available under the BSD License: http://www.opensource.org/licenses/bsd-license.php |
||
10 | */ |
||
11 | |||
12 | /** |
||
13 | * Class IXR_Value |
||
14 | */ |
||
15 | class IXR_Value |
||
16 | { |
||
17 | public $data; |
||
18 | public $type; |
||
19 | |||
20 | /** |
||
21 | * IXR_Value constructor. |
||
22 | * @param $data |
||
23 | * @param bool $type |
||
24 | */ |
||
25 | public function __construct($data, $type = false) |
||
26 | { |
||
27 | $this->data = $data; |
||
28 | if (!$type) { |
||
29 | $type = $this->calculateType(); |
||
30 | } |
||
31 | $this->type = $type; |
||
32 | if ('struct' === $type) { |
||
33 | /* Turn all the values in the array in to new IXR_Value objects */ |
||
34 | foreach ($this->data as $key => $value) { |
||
35 | $this->data[$key] = new IXR_Value($value); |
||
36 | } |
||
37 | } |
||
38 | if ('array' === $type) { |
||
39 | for ($i = 0, $j = count($this->data); $i < $j; ++$i) { |
||
40 | $this->data[$i] = new IXR_Value($this->data[$i]); |
||
41 | } |
||
42 | } |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * @return string |
||
47 | */ |
||
48 | public function calculateType() |
||
49 | { |
||
50 | if (true === $this->data || false === $this->data) { |
||
51 | return 'boolean'; |
||
52 | } |
||
53 | if (is_int($this->data)) { |
||
54 | return 'int'; |
||
55 | } |
||
56 | if (is_float($this->data)) { |
||
57 | return 'double'; |
||
58 | } |
||
59 | // Deal with IXR object types base64 and date |
||
60 | if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { |
||
61 | return 'date'; |
||
62 | } |
||
63 | if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { |
||
64 | return 'base64'; |
||
65 | } |
||
66 | // If it is a normal PHP object convert it in to a struct |
||
67 | if (is_object($this->data)) { |
||
68 | $this->data = get_object_vars($this->data); |
||
69 | |||
70 | return 'struct'; |
||
71 | } |
||
72 | if (!is_array($this->data)) { |
||
73 | return 'string'; |
||
74 | } |
||
75 | /* We have an array - is it an array or a struct ? */ |
||
76 | if ($this->isStruct($this->data)) { |
||
77 | return 'struct'; |
||
78 | } else { |
||
79 | return 'array'; |
||
80 | } |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * @return bool|string |
||
85 | */ |
||
86 | public function getXml() |
||
87 | { |
||
88 | /* Return XML for this value */ |
||
89 | switch ($this->type) { |
||
90 | case 'boolean': |
||
91 | return '<boolean>' . ($this->data ? '1' : '0') . '</boolean>'; |
||
92 | break; |
||
93 | case 'int': |
||
94 | return '<int>' . $this->data . '</int>'; |
||
95 | break; |
||
96 | case 'double': |
||
97 | return '<double>' . $this->data . '</double>'; |
||
98 | break; |
||
99 | case 'string': |
||
100 | return '<string>' . htmlspecialchars($this->data, ENT_QUOTES | ENT_HTML5) . '</string>'; |
||
101 | break; |
||
102 | case 'array': |
||
103 | $return = '<array><data>' . "\n"; |
||
104 | foreach ($this->data as $item) { |
||
105 | $return .= ' <value>' . $item->getXml() . "</value>\n"; |
||
106 | } |
||
107 | $return .= '</data></array>'; |
||
108 | |||
109 | return $return; |
||
110 | break; |
||
111 | case 'struct': |
||
112 | $return = '<struct>' . "\n"; |
||
113 | foreach ($this->data as $name => $value) { |
||
114 | $return .= " <member><name>$name</name><value>"; |
||
115 | $return .= $value->getXml() . "</value></member>\n"; |
||
116 | } |
||
117 | $return .= '</struct>'; |
||
118 | |||
119 | return $return; |
||
120 | break; |
||
121 | case 'date': |
||
122 | case 'base64': |
||
123 | return $this->data->getXml(); |
||
124 | break; |
||
125 | } |
||
126 | |||
127 | return false; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * @param $array |
||
132 | * @return bool |
||
133 | */ |
||
134 | public function isStruct($array) |
||
135 | { |
||
136 | /* Nasty function to check if an array is a struct or not */ |
||
137 | $expected = 0; |
||
138 | foreach ($array as $key => $value) { |
||
139 | if ((string)$key != (string)$expected) { |
||
140 | return true; |
||
141 | } |
||
142 | ++$expected; |
||
143 | } |
||
144 | |||
145 | return false; |
||
146 | } |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Class IXR_Message |
||
151 | */ |
||
152 | class IXR_Message |
||
153 | { |
||
154 | public $message; |
||
155 | public $messageType; // methodCall / methodResponse / fault |
||
156 | public $faultCode; |
||
157 | public $faultString; |
||
158 | public $methodName; |
||
159 | public $params; |
||
160 | // Current variable stacks |
||
161 | public $_arraystructs = []; // The stack used to keep track of the current array/struct |
||
162 | public $_arraystructstypes = []; // Stack keeping track of if things are structs or array |
||
163 | public $_currentStructName = []; // A stack as well |
||
164 | public $_param; |
||
165 | public $_value; |
||
166 | public $_currentTag; |
||
167 | public $_currentTagContents; |
||
168 | // The XML parser |
||
169 | public $_parser; |
||
170 | |||
171 | /** |
||
172 | * IXR_Message constructor. |
||
173 | * @param $message |
||
174 | */ |
||
175 | public function __construct($message) |
||
176 | { |
||
177 | $this->message = $message; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @return bool |
||
182 | */ |
||
183 | public function parse() |
||
184 | { |
||
185 | // first remove the XML declaration |
||
186 | $this->message = preg_replace('/<\?xml(.*)?\?' . '>/', '', $this->message); |
||
187 | if ('' == trim($this->message)) { |
||
188 | return false; |
||
189 | } |
||
190 | $this->_parser = xml_parser_create(); |
||
191 | // Set XML parser to take the case of tags in to account |
||
192 | xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); |
||
193 | // Set XML parser callback functions |
||
194 | xml_set_object($this->_parser, $this); |
||
195 | xml_set_elementHandler($this->_parser, 'tag_open', 'tag_close'); |
||
196 | xml_set_character_dataHandler($this->_parser, 'cdata'); |
||
197 | if (!xml_parse($this->_parser, $this->message)) { |
||
198 | /* die(sprintf('XML error: %s at line %d', |
||
199 | xml_error_string(xml_get_error_code($this->_parser)), |
||
200 | xml_get_current_line_number($this->_parser))); */ |
||
201 | |||
202 | return false; |
||
203 | } |
||
204 | xml_parser_free($this->_parser); |
||
205 | // Grab the error messages, if any |
||
206 | if ('fault' === $this->messageType) { |
||
207 | $this->faultCode = $this->params[0]['faultCode']; |
||
208 | $this->faultString = $this->params[0]['faultString']; |
||
209 | } |
||
210 | |||
211 | return true; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * @param $parser |
||
216 | * @param $tag |
||
217 | * @param $attr |
||
218 | */ |
||
219 | public function tag_open($parser, $tag, $attr) |
||
220 | { |
||
221 | $this->currentTag = $tag; |
||
222 | switch ($tag) { |
||
223 | case 'methodCall': |
||
224 | case 'methodResponse': |
||
225 | case 'fault': |
||
226 | $this->messageType = $tag; |
||
227 | break; |
||
228 | /* Deal with stacks of arrays and structs */ |
||
229 | case 'data': // data is to all intents and puposes more interesting than array |
||
230 | $this->_arraystructstypes[] = 'array'; |
||
231 | $this->_arraystructs[] = []; |
||
232 | break; |
||
233 | case 'struct': |
||
234 | $this->_arraystructstypes[] = 'struct'; |
||
235 | $this->_arraystructs[] = []; |
||
236 | break; |
||
237 | } |
||
238 | } |
||
239 | |||
240 | /** |
||
241 | * @param $parser |
||
242 | * @param $cdata |
||
243 | */ |
||
244 | public function cdata($parser, $cdata) |
||
245 | { |
||
246 | $this->_currentTagContents .= $cdata; |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * @param $parser |
||
251 | * @param $tag |
||
252 | */ |
||
253 | public function tag_close($parser, $tag) |
||
254 | { |
||
255 | $valueFlag = false; |
||
256 | switch ($tag) { |
||
257 | case 'int': |
||
258 | case 'i4': |
||
259 | $value = (int)trim($this->_currentTagContents); |
||
260 | $this->_currentTagContents = ''; |
||
261 | $valueFlag = true; |
||
262 | break; |
||
263 | case 'double': |
||
264 | $value = (double)trim($this->_currentTagContents); |
||
265 | $this->_currentTagContents = ''; |
||
266 | $valueFlag = true; |
||
267 | break; |
||
268 | case 'string': |
||
269 | $value = trim($this->_currentTagContents); |
||
270 | $this->_currentTagContents = ''; |
||
271 | $valueFlag = true; |
||
272 | break; |
||
273 | case 'dateTime.iso8601': |
||
274 | $value = new IXR_Date(trim($this->_currentTagContents)); |
||
275 | // $value = $iso->getTimestamp(); |
||
276 | $this->_currentTagContents = ''; |
||
277 | $valueFlag = true; |
||
278 | break; |
||
279 | View Code Duplication | case 'value': |
|
0 ignored issues
–
show
|
|||
280 | // "If no type is indicated, the type is string." |
||
281 | if ('' != trim($this->_currentTagContents)) { |
||
282 | $value = (string)$this->_currentTagContents; |
||
283 | $this->_currentTagContents = ''; |
||
284 | $valueFlag = true; |
||
285 | } |
||
286 | break; |
||
287 | View Code Duplication | case 'boolean': |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
288 | $value = (boolean)trim($this->_currentTagContents); |
||
289 | $this->_currentTagContents = ''; |
||
290 | $valueFlag = true; |
||
291 | break; |
||
292 | case 'base64': |
||
293 | $value = base64_decode(trim($this->_currentTagContents)); |
||
294 | $this->_currentTagContents = ''; |
||
295 | $valueFlag = true; |
||
296 | break; |
||
297 | /* Deal with stacks of arrays and structs */ |
||
298 | case 'data': |
||
299 | case 'struct': |
||
300 | $value = array_pop($this->_arraystructs); |
||
301 | array_pop($this->_arraystructstypes); |
||
302 | $valueFlag = true; |
||
303 | break; |
||
304 | case 'member': |
||
305 | array_pop($this->_currentStructName); |
||
306 | break; |
||
307 | case 'name': |
||
308 | $this->_currentStructName[] = trim($this->_currentTagContents); |
||
309 | $this->_currentTagContents = ''; |
||
310 | break; |
||
311 | case 'methodName': |
||
312 | $this->methodName = trim($this->_currentTagContents); |
||
313 | $this->_currentTagContents = ''; |
||
314 | break; |
||
315 | } |
||
316 | if ($valueFlag) { |
||
317 | /* |
||
318 | if (!is_array($value) && !is_object($value)) { |
||
319 | $value = trim($value); |
||
320 | } |
||
321 | */ |
||
322 | if (count($this->_arraystructs) > 0) { |
||
323 | // Add value to struct or array |
||
324 | if ('struct' === $this->_arraystructstypes[count($this->_arraystructstypes) - 1]) { |
||
325 | // Add to struct |
||
326 | $this->_arraystructs[count($this->_arraystructs) - 1][$this->_currentStructName[count($this->_currentStructName) - 1]] = $value; |
||
327 | } else { |
||
328 | // Add to array |
||
329 | $this->_arraystructs[count($this->_arraystructs) - 1][] = $value; |
||
330 | } |
||
331 | } else { |
||
332 | // Just add as a paramater |
||
333 | $this->params[] = $value; |
||
334 | } |
||
335 | } |
||
336 | } |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Class IXR_Server |
||
341 | */ |
||
342 | class IXR_Server |
||
343 | { |
||
344 | public $data; |
||
345 | public $callbacks = []; |
||
346 | public $message; |
||
347 | public $capabilities; |
||
348 | |||
349 | /** |
||
350 | * IXR_Server constructor. |
||
351 | * @param bool $callbacks |
||
352 | * @param bool $data |
||
353 | */ |
||
354 | public function __construct($callbacks = false, $data = false) |
||
355 | { |
||
356 | $this->setCapabilities(); |
||
357 | if ($callbacks) { |
||
358 | $this->callbacks = $callbacks; |
||
359 | } |
||
360 | $this->setCallbacks(); |
||
361 | $this->serve($data); |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * @param bool $data |
||
366 | */ |
||
367 | public function serve($data = false) |
||
368 | { |
||
369 | if (!$data) { |
||
370 | $http_raw_post_data = file_get_contents('php://input'); |
||
371 | if (!$http_raw_post_data) { |
||
372 | die('XML-RPC server accepts POST requests only.'); |
||
373 | } |
||
374 | $data = $http_raw_post_data; |
||
375 | } |
||
376 | $this->message = new IXR_Message($data); |
||
377 | if (!$this->message->parse()) { |
||
378 | $this->error(-32700, 'parse error. not well formed'); |
||
379 | } |
||
380 | if ('methodCall' !== $this->message->messageType) { |
||
381 | $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); |
||
382 | } |
||
383 | $result = $this->call($this->message->methodName, $this->message->params); |
||
384 | // Is the result an error? |
||
385 | if (is_a($result, 'IXR_Error')) { |
||
386 | $this->error($result); |
||
387 | } |
||
388 | // Encode the result |
||
389 | $r = new IXR_Value($result); |
||
390 | $resultxml = $r->getXml(); |
||
391 | // Create the XML |
||
392 | $xml = <<<EOD |
||
393 | <methodResponse> |
||
394 | <params> |
||
395 | <param> |
||
396 | <value> |
||
397 | $resultxml |
||
398 | </value> |
||
399 | </param> |
||
400 | </params> |
||
401 | </methodResponse> |
||
402 | |||
403 | EOD; |
||
404 | // Send it |
||
405 | $this->output($xml); |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * @param $methodname |
||
410 | * @param $args |
||
411 | * @return IXR_Error|mixed |
||
412 | */ |
||
413 | public function call($methodname, $args) |
||
414 | { |
||
415 | if (!$this->hasMethod($methodname)) { |
||
416 | return new IXR_Error(-32601, 'server error. requested method ' . $methodname . ' does not exist.'); |
||
417 | } |
||
418 | $method = $this->callbacks[$methodname]; |
||
419 | // Perform the callback and send the response |
||
420 | if (1 == count($args)) { |
||
421 | // If only one paramater just send that instead of the whole array |
||
422 | $args = $args[0]; |
||
423 | } |
||
424 | // Are we dealing with a function or a method? |
||
425 | if ('this:' === substr($method, 0, 5)) { |
||
426 | // It's a class method - check it exists |
||
427 | $method = substr($method, 5); |
||
428 | if (!method_exists($this, $method)) { |
||
429 | return new IXR_Error(-32601, 'server error. requested class method "' . $method . '" does not exist.'); |
||
430 | } |
||
431 | // Call the method |
||
432 | $result = $this->$method($args); |
||
433 | } else { |
||
434 | // It's a function - does it exist? |
||
435 | if (is_array($method)) { |
||
436 | if (!method_exists($method[0], $method[1])) { |
||
437 | return new IXR_Error(-32601, 'server error. requested object method "' . $method[1] . '" does not exist.'); |
||
438 | } |
||
439 | } elseif (!function_exists($method)) { |
||
440 | return new IXR_Error(-32601, 'server error. requested function "' . $method . '" does not exist.'); |
||
441 | } |
||
442 | // Call the function |
||
443 | $result = call_user_func($method, $args); |
||
444 | } |
||
445 | |||
446 | return $result; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * @param $error |
||
451 | * @param bool $message |
||
452 | */ |
||
453 | public function error($error, $message = false) |
||
454 | { |
||
455 | // Accepts either an error object or an error code and message |
||
456 | if ($message && !is_object($error)) { |
||
457 | $error = new IXR_Error($error, $message); |
||
458 | } |
||
459 | $this->output($error->getXml()); |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * @param $xml |
||
464 | */ |
||
465 | public function output($xml) |
||
466 | { |
||
467 | $xml = '<?xml version="1.0"?>' . "\n" . $xml; |
||
468 | $length = strlen($xml); |
||
469 | header('Connection: close'); |
||
470 | header('Content-Length: ' . $length); |
||
471 | header('Content-Type: text/xml'); |
||
472 | header('Date: ' . date('r')); |
||
473 | echo $xml; |
||
474 | exit; |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * @param $method |
||
479 | * @return bool |
||
480 | */ |
||
481 | public function hasMethod($method) |
||
482 | { |
||
483 | return in_array($method, array_keys($this->callbacks)); |
||
484 | } |
||
485 | |||
486 | public function setCapabilities() |
||
487 | { |
||
488 | // Initialises capabilities array |
||
489 | $this->capabilities = [ |
||
490 | 'xmlrpc' => [ |
||
491 | 'specUrl' => 'http://www.xmlrpc.com/spec', |
||
492 | 'specVersion' => 1 |
||
493 | ], |
||
494 | 'faults_interop' => [ |
||
495 | 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', |
||
496 | 'specVersion' => 20010516 |
||
497 | ], |
||
498 | 'system.multicall' => [ |
||
499 | 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', |
||
500 | 'specVersion' => 1 |
||
501 | ] |
||
502 | ]; |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * @param $args |
||
507 | * @return mixed |
||
508 | */ |
||
509 | public function getCapabilities($args) |
||
510 | { |
||
511 | return $this->capabilities; |
||
512 | } |
||
513 | |||
514 | public function setCallbacks() |
||
515 | { |
||
516 | $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; |
||
517 | $this->callbacks['system.listMethods'] = 'this:listMethods'; |
||
518 | $this->callbacks['system.multicall'] = 'this:multiCall'; |
||
519 | } |
||
520 | |||
521 | /** |
||
522 | * @param $args |
||
523 | * @return array |
||
524 | */ |
||
525 | public function listMethods($args) |
||
526 | { |
||
527 | // Returns a list of methods - uses array_reverse to ensure user defined |
||
528 | // methods are listed before server defined methods |
||
529 | return array_reverse(array_keys($this->callbacks)); |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * @param $methodcalls |
||
534 | * @return array |
||
535 | */ |
||
536 | public function multiCall($methodcalls) |
||
537 | { |
||
538 | // See http://www.xmlrpc.com/discuss/msgReader$1208 |
||
539 | $return = []; |
||
540 | foreach ($methodcalls as $call) { |
||
541 | $method = $call['methodName']; |
||
542 | $params = $call['params']; |
||
543 | if ('system.multicall' === $method) { |
||
544 | $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); |
||
545 | } else { |
||
546 | $result = $this->call($method, $params); |
||
547 | } |
||
548 | if (is_a($result, 'IXR_Error')) { |
||
549 | $return[] = [ |
||
550 | 'faultCode' => $result->code, |
||
551 | 'faultString' => $result->message |
||
552 | ]; |
||
553 | } else { |
||
554 | $return[] = [$result]; |
||
555 | } |
||
556 | } |
||
557 | |||
558 | return $return; |
||
559 | } |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * Class IXR_Request |
||
564 | */ |
||
565 | class IXR_Request |
||
566 | { |
||
567 | public $method; |
||
568 | public $args; |
||
569 | public $xml; |
||
570 | |||
571 | /** |
||
572 | * IXR_Request constructor. |
||
573 | * @param $method |
||
574 | * @param $args |
||
575 | */ |
||
576 | public function __construct($method, $args) |
||
577 | { |
||
578 | $this->method = $method; |
||
579 | $this->args = $args; |
||
580 | $this->xml = <<<EOD |
||
581 | <?xml version="1.0"?> |
||
582 | <methodCall> |
||
583 | <methodName>{$this->method}</methodName> |
||
584 | <params> |
||
585 | |||
586 | EOD; |
||
587 | foreach ($this->args as $arg) { |
||
588 | $this->xml .= '<param><value>'; |
||
589 | $v = new IXR_Value($arg); |
||
590 | $this->xml .= $v->getXml(); |
||
591 | $this->xml .= "</value></param>\n"; |
||
592 | } |
||
593 | $this->xml .= '</params></methodCall>'; |
||
594 | } |
||
595 | |||
596 | /** |
||
597 | * @return int |
||
598 | */ |
||
599 | public function getLength() |
||
600 | { |
||
601 | return strlen($this->xml); |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * @return string |
||
606 | */ |
||
607 | public function getXml() |
||
608 | { |
||
609 | return $this->xml; |
||
610 | } |
||
611 | } |
||
612 | |||
613 | /** |
||
614 | * Class IXR_Client |
||
615 | */ |
||
616 | class IXR_Client |
||
617 | { |
||
618 | public $server; |
||
619 | public $port; |
||
620 | public $path; |
||
621 | public $useragent; |
||
622 | public $response; |
||
623 | public $timeout; |
||
624 | public $vendor = ''; |
||
625 | public $message = false; |
||
626 | public $debug = false; |
||
627 | // Storage place for an error message |
||
628 | public $error = false; |
||
629 | |||
630 | /** |
||
631 | * IXR_Client constructor. |
||
632 | * @param $server |
||
633 | * @param bool $path |
||
634 | * @param int $port |
||
635 | * @param int $timeout |
||
636 | * @param string $vendor |
||
637 | */ |
||
638 | public function __construct($server, $path = false, $port = 80, $timeout = 30, $vendor = '') |
||
639 | { |
||
640 | if (!$path) { |
||
641 | // Assume we have been given a URL instead |
||
642 | $bits = parse_url($server); |
||
643 | $this->server = $bits['host']; |
||
644 | $this->port = isset($bits['port']) ? $bits['port'] : 80; |
||
645 | $this->path = isset($bits['path']) ? $bits['path'] : '/'; |
||
646 | // Make absolutely sure we have a path |
||
647 | if (!$this->path) { |
||
648 | $this->path = '/'; |
||
649 | } |
||
650 | } else { |
||
651 | $this->server = $server; |
||
652 | $this->path = $path; |
||
653 | $this->port = $port; |
||
654 | $this->timeout = $timeout; |
||
655 | } |
||
656 | $this->useragent = 'The Incutio XML-RPC PHP Library'; |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * @return bool |
||
661 | */ |
||
662 | public function query() |
||
663 | { |
||
664 | $args = func_get_args(); |
||
665 | $method = array_shift($args); |
||
666 | $request = new IXR_Request($method, $args); |
||
667 | $length = $request->getLength(); |
||
668 | $xml = $request->getXml(); |
||
669 | $r = "\r\n"; |
||
670 | $request = "POST {$this->path} HTTP/1.0$r"; |
||
671 | $request .= "Host: {$this->server}$r"; |
||
672 | $request .= "Content-Type: text/xml$r"; |
||
673 | $request .= "User-Agent: {$this->useragent}$r"; |
||
674 | $request .= "Content-length: {$length}$r$r"; |
||
675 | $request .= $xml; |
||
676 | // Now send the request |
||
677 | View Code Duplication | if ($this->debug) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
678 | echo '<pre>' . htmlspecialchars($request, ENT_QUOTES | ENT_HTML5) . "\n</pre>\n\n"; |
||
679 | } |
||
680 | $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); |
||
681 | if (!$fp) { |
||
682 | $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); |
||
683 | |||
684 | return false; |
||
685 | } |
||
686 | fwrite($fp, $request); |
||
687 | $contents = ''; |
||
688 | $gotFirstLine = false; |
||
689 | $gettingHeaders = true; |
||
690 | while (!feof($fp)) { |
||
691 | $line = fgets($fp, 4096); |
||
692 | if (!$gotFirstLine) { |
||
693 | // Check line for '200' |
||
694 | if (false === strpos($line, '200')) { |
||
695 | $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); |
||
696 | |||
697 | return false; |
||
698 | } |
||
699 | $gotFirstLine = true; |
||
700 | } |
||
701 | if ('' == trim($line)) { |
||
702 | $gettingHeaders = false; |
||
703 | } |
||
704 | if (!$gettingHeaders) { |
||
705 | $contents .= trim($line) . "\n"; |
||
706 | } |
||
707 | } |
||
708 | View Code Duplication | if ($this->debug) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
709 | echo '<pre>' . htmlspecialchars($contents, ENT_QUOTES | ENT_HTML5) . "\n</pre>\n\n"; |
||
710 | } |
||
711 | // Now parse what we've got back |
||
712 | $this->message = new IXR_Message($contents); |
||
713 | if (!$this->message->parse()) { |
||
714 | // XML error |
||
715 | $this->error = new IXR_Error(-32700, 'parse error. not well formed'); |
||
716 | |||
717 | return false; |
||
718 | } |
||
719 | // Is the message a fault? |
||
720 | if ('fault' === $this->message->messageType) { |
||
721 | $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); |
||
722 | |||
723 | return false; |
||
724 | } |
||
725 | |||
726 | // Message must be OK |
||
727 | return true; |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * @return mixed |
||
732 | */ |
||
733 | public function getResponse() |
||
734 | { |
||
735 | // methodResponses can only have one param - return that |
||
736 | return $this->message->params[0]; |
||
737 | } |
||
738 | |||
739 | /** |
||
740 | * @return bool |
||
741 | */ |
||
742 | public function isError() |
||
743 | { |
||
744 | return is_object($this->error); |
||
745 | } |
||
746 | |||
747 | /** |
||
748 | * @return mixed |
||
749 | */ |
||
750 | public function getErrorCode() |
||
751 | { |
||
752 | return $this->error->code; |
||
753 | } |
||
754 | |||
755 | /** |
||
756 | * @return mixed |
||
757 | */ |
||
758 | public function getErrorMessage() |
||
759 | { |
||
760 | return $this->error->message; |
||
761 | } |
||
762 | } |
||
763 | |||
764 | /** |
||
765 | * Class IXR_Error |
||
766 | */ |
||
767 | class IXR_Error |
||
768 | { |
||
769 | public $code; |
||
770 | public $message; |
||
771 | |||
772 | /** |
||
773 | * IXR_Error constructor. |
||
774 | * @param $code |
||
775 | * @param $message |
||
776 | */ |
||
777 | public function __construct($code, $message) |
||
778 | { |
||
779 | $this->code = $code; |
||
780 | $this->message = $message; |
||
781 | } |
||
782 | |||
783 | /** |
||
784 | * @return string |
||
785 | */ |
||
786 | public function getXml() |
||
787 | { |
||
788 | $xml = <<<EOD |
||
789 | <methodResponse> |
||
790 | <fault> |
||
791 | <value> |
||
792 | <struct> |
||
793 | <member> |
||
794 | <name>faultCode</name> |
||
795 | <value><int>{$this->code}</int></value> |
||
796 | </member> |
||
797 | <member> |
||
798 | <name>faultString</name> |
||
799 | <value><string>{$this->message}</string></value> |
||
800 | </member> |
||
801 | </struct> |
||
802 | </value> |
||
803 | </fault> |
||
804 | </methodResponse> |
||
805 | |||
806 | EOD; |
||
807 | |||
808 | return $xml; |
||
809 | } |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * Class IXR_Date |
||
814 | */ |
||
815 | class IXR_Date |
||
816 | { |
||
817 | public $year; |
||
818 | public $month; |
||
819 | public $day; |
||
820 | public $hour; |
||
821 | public $minute; |
||
822 | public $second; |
||
823 | public $timezone; |
||
824 | |||
825 | /** |
||
826 | * IXR_Date constructor. |
||
827 | * @param $time |
||
828 | */ |
||
829 | public function __construct($time) |
||
830 | { |
||
831 | // $time can be a PHP timestamp or an ISO one |
||
832 | if (is_numeric($time)) { |
||
833 | $this->parseTimestamp($time); |
||
834 | } else { |
||
835 | $this->parseIso($time); |
||
836 | } |
||
837 | } |
||
838 | |||
839 | /** |
||
840 | * @param $timestamp |
||
841 | */ |
||
842 | public function parseTimestamp($timestamp) |
||
843 | { |
||
844 | $this->year = date('Y', $timestamp); |
||
845 | $this->month = date('Y', $timestamp); |
||
846 | $this->day = date('Y', $timestamp); |
||
847 | $this->hour = date('H', $timestamp); |
||
848 | $this->minute = date('i', $timestamp); |
||
849 | $this->second = date('s', $timestamp); |
||
850 | } |
||
851 | |||
852 | /** |
||
853 | * @param $iso |
||
854 | */ |
||
855 | public function parseIso($iso) |
||
856 | { |
||
857 | $this->year = substr($iso, 0, 4); |
||
858 | $this->month = substr($iso, 4, 2); |
||
859 | $this->day = substr($iso, 6, 2); |
||
860 | $this->hour = substr($iso, 9, 2); |
||
861 | $this->minute = substr($iso, 12, 2); |
||
862 | $this->second = substr($iso, 15, 2); |
||
863 | $this->timezone = substr($iso, 17); |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * @return string |
||
868 | */ |
||
869 | public function getIso() |
||
870 | { |
||
871 | return $this->year . $this->month . $this->day . 'T' . $this->hour . ':' . $this->minute . ':' . $this->second . $this->timezone; |
||
872 | } |
||
873 | |||
874 | /** |
||
875 | * @return string |
||
876 | */ |
||
877 | public function getXml() |
||
878 | { |
||
879 | return '<dateTime.iso8601>' . $this->getIso() . '</dateTime.iso8601>'; |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * @return int |
||
884 | */ |
||
885 | public function getTimestamp() |
||
886 | { |
||
887 | return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); |
||
888 | } |
||
889 | } |
||
890 | |||
891 | /** |
||
892 | * Class IXR_Base64 |
||
893 | */ |
||
894 | class IXR_Base64 |
||
895 | { |
||
896 | public $data; |
||
897 | |||
898 | /** |
||
899 | * IXR_Base64 constructor. |
||
900 | * @param $data |
||
901 | */ |
||
902 | public function __construct($data) |
||
903 | { |
||
904 | $this->data = $data; |
||
905 | } |
||
906 | |||
907 | /** |
||
908 | * @return string |
||
909 | */ |
||
910 | public function getXml() |
||
911 | { |
||
912 | return '<base64>' . base64_encode($this->data) . '</base64>'; |
||
913 | } |
||
914 | } |
||
915 | |||
916 | /** |
||
917 | * Class IXR_IntrospectionServer |
||
918 | */ |
||
919 | class IXR_IntrospectionServer extends IXR_Server |
||
920 | { |
||
921 | public $signatures; |
||
922 | public $help; |
||
923 | |||
924 | /** |
||
925 | * IXR_IntrospectionServer constructor. |
||
926 | */ |
||
927 | public function __construct() |
||
928 | { |
||
929 | $this->setCallbacks(); |
||
930 | $this->setCapabilities(); |
||
931 | $this->capabilities['introspection'] = [ |
||
932 | 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', |
||
933 | 'specVersion' => 1 |
||
934 | ]; |
||
935 | $this->addCallback('system.methodSignature', 'this:methodSignature', ['array', 'string'], 'Returns an array describing the return type and required parameters of a method'); |
||
936 | $this->addCallback('system.getCapabilities', 'this:getCapabilities', ['struct'], 'Returns a struct describing the XML-RPC specifications supported by this server'); |
||
937 | $this->addCallback('system.listMethods', 'this:listMethods', ['array'], 'Returns an array of available methods on this server'); |
||
938 | $this->addCallback('system.methodHelp', 'this:methodHelp', ['string', 'string'], 'Returns a documentation string for the specified method'); |
||
939 | } |
||
940 | |||
941 | /** |
||
942 | * @param $method |
||
943 | * @param $callback |
||
944 | * @param $args |
||
945 | * @param $help |
||
946 | */ |
||
947 | public function addCallback($method, $callback, $args, $help) |
||
948 | { |
||
949 | $this->callbacks[$method] = $callback; |
||
950 | $this->signatures[$method] = $args; |
||
951 | $this->help[$method] = $help; |
||
952 | } |
||
953 | |||
954 | /** |
||
955 | * @param $methodname |
||
956 | * @param $args |
||
957 | * @return IXR_Error|mixed |
||
958 | */ |
||
959 | public function call($methodname, $args) |
||
960 | { |
||
961 | // Make sure it's in an array |
||
962 | if ($args && !is_array($args)) { |
||
963 | $args = [$args]; |
||
964 | } |
||
965 | // Over-rides default call method, adds signature check |
||
966 | if (!$this->hasMethod($methodname)) { |
||
967 | return new IXR_Error(-32601, 'server error. requested method "' . $this->message->methodName . '" not specified.'); |
||
968 | } |
||
969 | $method = $this->callbacks[$methodname]; |
||
970 | $signature = $this->signatures[$methodname]; |
||
971 | $returnType = array_shift($signature); |
||
972 | // Check the number of arguments |
||
973 | if (count($args) != count($signature)) { |
||
974 | // print 'Num of args: '.count($args).' Num in signature: '.count($signature); |
||
975 | return new IXR_Error(-32602, 'server error. wrong number of method parameters'); |
||
976 | } |
||
977 | // Check the argument types |
||
978 | $ok = true; |
||
979 | $argsbackup = $args; |
||
980 | for ($i = 0, $j = count($args); $i < $j; ++$i) { |
||
981 | $arg = array_shift($args); |
||
982 | $type = array_shift($signature); |
||
983 | switch ($type) { |
||
984 | case 'int': |
||
985 | case 'i4': |
||
986 | if (is_array($arg) || !is_int($arg)) { |
||
987 | $ok = false; |
||
988 | } |
||
989 | break; |
||
990 | case 'base64': |
||
991 | case 'string': |
||
992 | if (!is_string($arg)) { |
||
993 | $ok = false; |
||
994 | } |
||
995 | break; |
||
996 | case 'boolean': |
||
997 | if (false !== $arg && true !== $arg) { |
||
998 | $ok = false; |
||
999 | } |
||
1000 | break; |
||
1001 | case 'float': |
||
1002 | case 'double': |
||
1003 | if (!is_float($arg)) { |
||
1004 | $ok = false; |
||
1005 | } |
||
1006 | break; |
||
1007 | case 'date': |
||
1008 | case 'dateTime.iso8601': |
||
1009 | if (!is_a($arg, 'IXR_Date')) { |
||
1010 | $ok = false; |
||
1011 | } |
||
1012 | break; |
||
1013 | } |
||
1014 | if (!$ok) { |
||
1015 | return new IXR_Error(-32602, 'server error. invalid method parameters'); |
||
1016 | } |
||
1017 | } |
||
1018 | |||
1019 | // It passed the test - run the "real" method call |
||
1020 | return parent::call($methodname, $argsbackup); |
||
1021 | } |
||
1022 | |||
1023 | /** |
||
1024 | * @param $method |
||
1025 | * @return array|IXR_Error |
||
1026 | */ |
||
1027 | public function methodSignature($method) |
||
1028 | { |
||
1029 | if (!$this->hasMethod($method)) { |
||
1030 | return new IXR_Error(-32601, 'server error. requested method "' . $method . '" not specified.'); |
||
1031 | } |
||
1032 | // We should be returning an array of types |
||
1033 | $types = $this->signatures[$method]; |
||
1034 | $return = []; |
||
1035 | foreach ($types as $type) { |
||
1036 | switch ($type) { |
||
1037 | case 'string': |
||
1038 | $return[] = 'string'; |
||
1039 | break; |
||
1040 | case 'int': |
||
1041 | case 'i4': |
||
1042 | $return[] = 42; |
||
1043 | break; |
||
1044 | case 'double': |
||
1045 | $return[] = 3.1415; |
||
1046 | break; |
||
1047 | case 'dateTime.iso8601': |
||
1048 | $return[] = new IXR_Date(time()); |
||
1049 | break; |
||
1050 | case 'boolean': |
||
1051 | $return[] = true; |
||
1052 | break; |
||
1053 | case 'base64': |
||
1054 | $return[] = new IXR_Base64('base64'); |
||
1055 | break; |
||
1056 | case 'array': |
||
1057 | $return[] = ['array']; |
||
1058 | break; |
||
1059 | case 'struct': |
||
1060 | $return[] = ['struct' => 'struct']; |
||
1061 | break; |
||
1062 | } |
||
1063 | } |
||
1064 | |||
1065 | return $return; |
||
1066 | } |
||
1067 | |||
1068 | /** |
||
1069 | * @param $method |
||
1070 | * @return mixed |
||
1071 | */ |
||
1072 | public function methodHelp($method) |
||
1073 | { |
||
1074 | return $this->help[$method]; |
||
1075 | } |
||
1076 | } |
||
1077 | |||
1078 | /** |
||
1079 | * Class IXR_ClientMulticall |
||
1080 | */ |
||
1081 | class IXR_ClientMulticall extends IXR_Client |
||
1082 | { |
||
1083 | public $calls = []; |
||
1084 | |||
1085 | /** |
||
1086 | * IXR_ClientMulticall constructor. |
||
1087 | * @param $server |
||
1088 | * @param bool $path |
||
1089 | * @param int $port |
||
1090 | */ |
||
1091 | public function __construct($server, $path = false, $port = 80) |
||
1092 | { |
||
1093 | parent::IXR_Client($server, $path, $port); |
||
1094 | $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; |
||
1095 | } |
||
1096 | |||
1097 | public function addCall() |
||
1098 | { |
||
1099 | $args = func_get_args(); |
||
1100 | $methodName = array_shift($args); |
||
1101 | $struct = [ |
||
1102 | 'methodName' => $methodName, |
||
1103 | 'params' => $args |
||
1104 | ]; |
||
1105 | $this->calls[] = $struct; |
||
1106 | } |
||
1107 | |||
1108 | /** |
||
1109 | * @return bool |
||
1110 | */ |
||
1111 | public function query() |
||
1112 | { |
||
1113 | // Prepare multicall, then call the parent::query() method |
||
1114 | return parent::query('system.multicall', $this->calls); |
||
1115 | } |
||
1116 | } |
||
1117 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.