1 <?php |
1 <?php |
2 /** |
2 /** |
3 * IXR - The Inutio XML-RPC Library |
3 * IXR - The Incutio XML-RPC Library |
|
4 * |
|
5 * Copyright (c) 2010, Incutio Ltd. |
|
6 * All rights reserved. |
|
7 * |
|
8 * Redistribution and use in source and binary forms, with or without |
|
9 * modification, are permitted provided that the following conditions are met: |
|
10 * |
|
11 * - Redistributions of source code must retain the above copyright notice, |
|
12 * this list of conditions and the following disclaimer. |
|
13 * - Redistributions in binary form must reproduce the above copyright |
|
14 * notice, this list of conditions and the following disclaimer in the |
|
15 * documentation and/or other materials provided with the distribution. |
|
16 * - Neither the name of Incutio Ltd. nor the names of its contributors |
|
17 * may be used to endorse or promote products derived from this software |
|
18 * without specific prior written permission. |
|
19 * |
|
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
|
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
|
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
|
30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
4 * |
31 * |
5 * @package IXR |
32 * @package IXR |
6 * @since 1.5 |
33 * @since 1.5 |
7 * |
34 * |
8 * @copyright Incutio Ltd 2002-2005 |
35 * @copyright Incutio Ltd 2010 (http://www.incutio.com) |
9 * @version 1.7 (beta) 23rd May 2005 |
36 * @version 1.7.4 7th September 2010 |
10 * @author Simon Willison |
37 * @author Simon Willison |
11 * @link http://scripts.incutio.com/xmlrpc/ Site |
38 * @link http://scripts.incutio.com/xmlrpc/ Site/manual |
12 * @link http://scripts.incutio.com/xmlrpc/manual.php Manual |
39 * @license http://www.opensource.org/licenses/bsd-license.php BSD |
13 * @license BSD License http://www.opensource.org/licenses/bsd-license.php |
|
14 */ |
40 */ |
15 |
41 |
16 /** |
42 /** |
17 * IXR_Value |
43 * IXR_Value |
18 * |
44 * |
129 return false; |
166 return false; |
130 } |
167 } |
131 } |
168 } |
132 |
169 |
133 /** |
170 /** |
134 * IXR_Message |
171 * IXR_MESSAGE |
135 * |
172 * |
136 * @package IXR |
173 * @package IXR |
137 * @since 1.5 |
174 * @since 1.5 |
|
175 * |
138 */ |
176 */ |
139 class IXR_Message { |
177 class IXR_Message |
|
178 { |
140 var $message; |
179 var $message; |
141 var $messageType; // methodCall / methodResponse / fault |
180 var $messageType; // methodCall / methodResponse / fault |
142 var $faultCode; |
181 var $faultCode; |
143 var $faultString; |
182 var $faultString; |
144 var $methodName; |
183 var $methodName; |
145 var $params; |
184 var $params; |
|
185 |
146 // Current variable stacks |
186 // Current variable stacks |
147 var $_arraystructs = array(); // The stack used to keep track of the current array/struct |
187 var $_arraystructs = array(); // The stack used to keep track of the current array/struct |
148 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array |
188 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array |
149 var $_currentStructName = array(); // A stack as well |
189 var $_currentStructName = array(); // A stack as well |
150 var $_param; |
190 var $_param; |
151 var $_value; |
191 var $_value; |
152 var $_currentTag; |
192 var $_currentTag; |
153 var $_currentTagContents; |
193 var $_currentTagContents; |
154 // The XML parser |
194 // The XML parser |
155 var $_parser; |
195 var $_parser; |
156 function IXR_Message (&$message) { |
196 |
157 $this->message = &$message; |
197 function IXR_Message($message) |
158 } |
198 { |
159 function parse() { |
199 $this->message =& $message; |
160 // first remove the XML declaration |
200 } |
161 // this method avoids the RAM usage of preg_replace on very large messages |
201 |
162 $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr( $this->message, 0, 100 ), 1 ); |
202 function parse() |
163 $this->message = substr_replace($this->message, $header, 0, 100); |
203 { |
|
204 // first remove the XML declaration |
|
205 // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages |
|
206 $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1); |
|
207 $this->message = substr_replace($this->message, $header, 0, 100); |
164 if (trim($this->message) == '') { |
208 if (trim($this->message) == '') { |
165 return false; |
209 return false; |
166 } |
210 } |
167 $this->_parser = xml_parser_create(); |
211 $this->_parser = xml_parser_create(); |
168 // Set XML parser to take the case of tags in to account |
212 // Set XML parser to take the case of tags in to account |
169 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); |
213 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); |
170 // Set XML parser callback functions |
214 // Set XML parser callback functions |
171 xml_set_object($this->_parser, $this); |
215 xml_set_object($this->_parser, $this); |
172 xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); |
216 xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); |
173 xml_set_character_data_handler($this->_parser, 'cdata'); |
217 xml_set_character_data_handler($this->_parser, 'cdata'); |
174 $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages |
218 $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages |
175 do { |
219 $final = false; |
176 if ( strlen($this->message) <= $chunk_size ) |
220 do { |
177 $final=true; |
221 if (strlen($this->message) <= $chunk_size) { |
178 $part = substr( $this->message, 0, $chunk_size ); |
222 $final = true; |
179 $this->message = substr( $this->message, $chunk_size ); |
223 } |
180 if ( !xml_parse( $this->_parser, $part, $final ) ) |
224 $part = substr($this->message, 0, $chunk_size); |
181 return false; |
225 $this->message = substr($this->message, $chunk_size); |
182 if ( $final ) |
226 if (!xml_parse($this->_parser, $part, $final)) { |
183 break; |
227 return false; |
184 } while ( true ); |
228 } |
185 xml_parser_free($this->_parser); |
229 if ($final) { |
|
230 break; |
|
231 } |
|
232 } while (true); |
|
233 xml_parser_free($this->_parser); |
|
234 |
186 // Grab the error messages, if any |
235 // Grab the error messages, if any |
187 if ($this->messageType == 'fault') { |
236 if ($this->messageType == 'fault') { |
188 $this->faultCode = $this->params[0]['faultCode']; |
237 $this->faultCode = $this->params[0]['faultCode']; |
189 $this->faultString = $this->params[0]['faultString']; |
238 $this->faultString = $this->params[0]['faultString']; |
190 } |
239 } |
191 return true; |
240 return true; |
192 } |
241 } |
193 function tag_open($parser, $tag, $attr) { |
242 |
|
243 function tag_open($parser, $tag, $attr) |
|
244 { |
194 $this->_currentTagContents = ''; |
245 $this->_currentTagContents = ''; |
195 $this->currentTag = $tag; |
246 $this->currentTag = $tag; |
196 switch($tag) { |
247 switch($tag) { |
197 case 'methodCall': |
248 case 'methodCall': |
198 case 'methodResponse': |
249 case 'methodResponse': |
199 case 'fault': |
250 case 'fault': |
200 $this->messageType = $tag; |
251 $this->messageType = $tag; |
201 break; |
252 break; |
202 /* Deal with stacks of arrays and structs */ |
253 /* Deal with stacks of arrays and structs */ |
203 case 'data': // data is to all intents and puposes more interesting than array |
254 case 'data': // data is to all intents and puposes more interesting than array |
204 $this->_arraystructstypes[] = 'array'; |
255 $this->_arraystructstypes[] = 'array'; |
205 $this->_arraystructs[] = array(); |
256 $this->_arraystructs[] = array(); |
206 break; |
257 break; |
207 case 'struct': |
258 case 'struct': |
208 $this->_arraystructstypes[] = 'struct'; |
259 $this->_arraystructstypes[] = 'struct'; |
209 $this->_arraystructs[] = array(); |
260 $this->_arraystructs[] = array(); |
210 break; |
261 break; |
211 } |
262 } |
212 } |
263 } |
213 function cdata($parser, $cdata) { |
264 |
|
265 function cdata($parser, $cdata) |
|
266 { |
214 $this->_currentTagContents .= $cdata; |
267 $this->_currentTagContents .= $cdata; |
215 } |
268 } |
216 function tag_close($parser, $tag) { |
269 |
|
270 function tag_close($parser, $tag) |
|
271 { |
217 $valueFlag = false; |
272 $valueFlag = false; |
218 switch($tag) { |
273 switch($tag) { |
219 case 'int': |
274 case 'int': |
220 case 'i4': |
275 case 'i4': |
221 $value = (int) trim($this->_currentTagContents); |
276 $value = (int)trim($this->_currentTagContents); |
222 $valueFlag = true; |
277 $valueFlag = true; |
223 break; |
278 break; |
224 case 'double': |
279 case 'double': |
225 $value = (double) trim($this->_currentTagContents); |
280 $value = (double)trim($this->_currentTagContents); |
226 $valueFlag = true; |
281 $valueFlag = true; |
227 break; |
282 break; |
228 case 'string': |
283 case 'string': |
229 $value = $this->_currentTagContents; |
284 $value = (string)trim($this->_currentTagContents); |
230 $valueFlag = true; |
285 $valueFlag = true; |
231 break; |
286 break; |
232 case 'dateTime.iso8601': |
287 case 'dateTime.iso8601': |
233 $value = new IXR_Date(trim($this->_currentTagContents)); |
288 $value = new IXR_Date(trim($this->_currentTagContents)); |
234 // $value = $iso->getTimestamp(); |
|
235 $valueFlag = true; |
289 $valueFlag = true; |
236 break; |
290 break; |
237 case 'value': |
291 case 'value': |
238 // "If no type is indicated, the type is string." |
292 // "If no type is indicated, the type is string." |
239 if (trim($this->_currentTagContents) != '') { |
293 if (trim($this->_currentTagContents) != '') { |
240 $value = (string)$this->_currentTagContents; |
294 $value = (string)$this->_currentTagContents; |
241 $valueFlag = true; |
295 $valueFlag = true; |
242 } |
296 } |
243 break; |
297 break; |
244 case 'boolean': |
298 case 'boolean': |
245 $value = (boolean) trim($this->_currentTagContents); |
299 $value = (boolean)trim($this->_currentTagContents); |
246 $valueFlag = true; |
300 $valueFlag = true; |
247 break; |
301 break; |
248 case 'base64': |
302 case 'base64': |
249 $value = base64_decode( trim( $this->_currentTagContents ) ); |
303 $value = base64_decode($this->_currentTagContents); |
250 $valueFlag = true; |
304 $valueFlag = true; |
251 break; |
305 break; |
252 /* Deal with stacks of arrays and structs */ |
306 /* Deal with stacks of arrays and structs */ |
253 case 'data': |
307 case 'data': |
254 case 'struct': |
308 case 'struct': |
255 $value = array_pop($this->_arraystructs); |
309 $value = array_pop($this->_arraystructs); |
256 array_pop($this->_arraystructstypes); |
310 array_pop($this->_arraystructstypes); |
257 $valueFlag = true; |
311 $valueFlag = true; |
289 * IXR_Server |
344 * IXR_Server |
290 * |
345 * |
291 * @package IXR |
346 * @package IXR |
292 * @since 1.5 |
347 * @since 1.5 |
293 */ |
348 */ |
294 class IXR_Server { |
349 class IXR_Server |
|
350 { |
295 var $data; |
351 var $data; |
296 var $callbacks = array(); |
352 var $callbacks = array(); |
297 var $message; |
353 var $message; |
298 var $capabilities; |
354 var $capabilities; |
299 function IXR_Server($callbacks = false, $data = false) { |
355 |
|
356 function IXR_Server($callbacks = false, $data = false, $wait = false) |
|
357 { |
300 $this->setCapabilities(); |
358 $this->setCapabilities(); |
301 if ($callbacks) { |
359 if ($callbacks) { |
302 $this->callbacks = $callbacks; |
360 $this->callbacks = $callbacks; |
303 } |
361 } |
304 $this->setCallbacks(); |
362 $this->setCallbacks(); |
305 $this->serve($data); |
363 if (!$wait) { |
306 } |
364 $this->serve($data); |
307 function serve($data = false) { |
365 } |
|
366 } |
|
367 |
|
368 function serve($data = false) |
|
369 { |
308 if (!$data) { |
370 if (!$data) { |
|
371 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') { |
|
372 header('Content-Type: text/plain'); // merged from WP #9093 |
|
373 die('XML-RPC server accepts POST requests only.'); |
|
374 } |
|
375 |
309 global $HTTP_RAW_POST_DATA; |
376 global $HTTP_RAW_POST_DATA; |
310 if (!$HTTP_RAW_POST_DATA) { |
377 if (empty($HTTP_RAW_POST_DATA)) { |
311 header( 'Content-Type: text/plain' ); |
378 // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293 |
312 die('XML-RPC server accepts POST requests only.'); |
379 $data = file_get_contents('php://input'); |
313 } |
380 } else { |
314 $data = &$HTTP_RAW_POST_DATA; |
381 $data =& $HTTP_RAW_POST_DATA; |
|
382 } |
315 } |
383 } |
316 $this->message = new IXR_Message($data); |
384 $this->message = new IXR_Message($data); |
317 if (!$this->message->parse()) { |
385 if (!$this->message->parse()) { |
318 $this->error(-32700, 'parse error. not well formed'); |
386 $this->error(-32700, 'parse error. not well formed'); |
319 } |
387 } |
320 if ($this->message->messageType != 'methodCall') { |
388 if ($this->message->messageType != 'methodCall') { |
321 $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); |
389 $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); |
322 } |
390 } |
323 $result = $this->call($this->message->methodName, $this->message->params); |
391 $result = $this->call($this->message->methodName, $this->message->params); |
|
392 |
324 // Is the result an error? |
393 // Is the result an error? |
325 if (is_a($result, 'IXR_Error')) { |
394 if (is_a($result, 'IXR_Error')) { |
326 $this->error($result); |
395 $this->error($result); |
327 } |
396 } |
|
397 |
328 // Encode the result |
398 // Encode the result |
329 $r = new IXR_Value($result); |
399 $r = new IXR_Value($result); |
330 $resultxml = $r->getXml(); |
400 $resultxml = $r->getXml(); |
|
401 |
331 // Create the XML |
402 // Create the XML |
332 $xml = <<<EOD |
403 $xml = <<<EOD |
333 <methodResponse> |
404 <methodResponse> |
334 <params> |
405 <params> |
335 <param> |
406 <param> |
336 <value> |
407 <value> |
337 $resultxml |
408 $resultxml |
338 </value> |
409 </value> |
339 </param> |
410 </param> |
340 </params> |
411 </params> |
341 </methodResponse> |
412 </methodResponse> |
342 |
413 |
343 EOD; |
414 EOD; |
344 // Send it |
415 // Send it |
345 $this->output($xml); |
416 $this->output($xml); |
346 } |
417 } |
347 function call($methodname, $args) { |
418 |
|
419 function call($methodname, $args) |
|
420 { |
348 if (!$this->hasMethod($methodname)) { |
421 if (!$this->hasMethod($methodname)) { |
349 return new IXR_Error(-32601, 'server error. requested method '. |
422 return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); |
350 $methodname.' does not exist.'); |
|
351 } |
423 } |
352 $method = $this->callbacks[$methodname]; |
424 $method = $this->callbacks[$methodname]; |
|
425 |
353 // Perform the callback and send the response |
426 // Perform the callback and send the response |
354 if (count($args) == 1) { |
427 if (count($args) == 1) { |
355 // If only one paramater just send that instead of the whole array |
428 // If only one paramater just send that instead of the whole array |
356 $args = $args[0]; |
429 $args = $args[0]; |
357 } |
430 } |
|
431 |
358 // Are we dealing with a function or a method? |
432 // Are we dealing with a function or a method? |
359 if (substr($method, 0, 5) == 'this:') { |
433 if (is_string($method) && substr($method, 0, 5) == 'this:') { |
360 // It's a class method - check it exists |
434 // It's a class method - check it exists |
361 $method = substr($method, 5); |
435 $method = substr($method, 5); |
362 if (!method_exists($this, $method)) { |
436 if (!method_exists($this, $method)) { |
363 return new IXR_Error(-32601, 'server error. requested class method "'. |
437 return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); |
364 $method.'" does not exist.'); |
438 } |
365 } |
439 |
366 // Call the method |
440 //Call the method |
367 $result = $this->$method($args); |
441 $result = $this->$method($args); |
368 } else { |
442 } else { |
369 // It's a function - does it exist? |
443 // It's a function - does it exist? |
370 if (is_array($method)) { |
444 if (is_array($method)) { |
371 if (!method_exists($method[0], $method[1])) { |
445 if (!is_callable(array($method[0], $method[1]))) { |
372 return new IXR_Error(-32601, 'server error. requested object method "'. |
446 return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.'); |
373 $method[1].'" does not exist.'); |
|
374 } |
447 } |
375 } else if (!function_exists($method)) { |
448 } else if (!function_exists($method)) { |
376 return new IXR_Error(-32601, 'server error. requested function "'. |
449 return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); |
377 $method.'" does not exist.'); |
450 } |
378 } |
451 |
379 // Call the function |
452 // Call the function |
380 $result = call_user_func($method, $args); |
453 $result = call_user_func($method, $args); |
381 } |
454 } |
382 return $result; |
455 return $result; |
383 } |
456 } |
384 |
457 |
385 function error($error, $message = false) { |
458 function error($error, $message = false) |
|
459 { |
386 // Accepts either an error object or an error code and message |
460 // Accepts either an error object or an error code and message |
387 if ($message && !is_object($error)) { |
461 if ($message && !is_object($error)) { |
388 $error = new IXR_Error($error, $message); |
462 $error = new IXR_Error($error, $message); |
389 } |
463 } |
390 $this->output($error->getXml()); |
464 $this->output($error->getXml()); |
391 } |
465 } |
392 function output($xml) { |
466 |
|
467 function output($xml) |
|
468 { |
393 $xml = '<?xml version="1.0"?>'."\n".$xml; |
469 $xml = '<?xml version="1.0"?>'."\n".$xml; |
394 $length = strlen($xml); |
470 $length = strlen($xml); |
395 header('Connection: close'); |
471 header('Connection: close'); |
396 header('Content-Length: '.$length); |
472 header('Content-Length: '.$length); |
397 header('Content-Type: text/xml'); |
473 header('Content-Type: text/xml'); |
398 header('Date: '.date('r')); |
474 header('Date: '.date('r')); |
399 echo $xml; |
475 echo $xml; |
400 exit; |
476 exit; |
401 } |
477 } |
402 function hasMethod($method) { |
478 |
|
479 function hasMethod($method) |
|
480 { |
403 return in_array($method, array_keys($this->callbacks)); |
481 return in_array($method, array_keys($this->callbacks)); |
404 } |
482 } |
405 function setCapabilities() { |
483 |
|
484 function setCapabilities() |
|
485 { |
406 // Initialises capabilities array |
486 // Initialises capabilities array |
407 $this->capabilities = array( |
487 $this->capabilities = array( |
408 'xmlrpc' => array( |
488 'xmlrpc' => array( |
409 'specUrl' => 'http://www.xmlrpc.com/spec', |
489 'specUrl' => 'http://www.xmlrpc.com/spec', |
410 'specVersion' => 1 |
490 'specVersion' => 1 |
411 ), |
491 ), |
412 'faults_interop' => array( |
492 'faults_interop' => array( |
413 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', |
493 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', |
414 'specVersion' => 20010516 |
494 'specVersion' => 20010516 |
415 ), |
495 ), |
416 'system.multicall' => array( |
496 'system.multicall' => array( |
417 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', |
497 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', |
418 'specVersion' => 1 |
498 'specVersion' => 1 |
419 ), |
499 ), |
420 ); |
500 ); |
421 } |
501 } |
422 function getCapabilities($args) { |
502 |
|
503 function getCapabilities($args) |
|
504 { |
423 return $this->capabilities; |
505 return $this->capabilities; |
424 } |
506 } |
425 function setCallbacks() { |
507 |
|
508 function setCallbacks() |
|
509 { |
426 $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; |
510 $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; |
427 $this->callbacks['system.listMethods'] = 'this:listMethods'; |
511 $this->callbacks['system.listMethods'] = 'this:listMethods'; |
428 $this->callbacks['system.multicall'] = 'this:multiCall'; |
512 $this->callbacks['system.multicall'] = 'this:multiCall'; |
429 } |
513 } |
430 function listMethods($args) { |
514 |
|
515 function listMethods($args) |
|
516 { |
431 // Returns a list of methods - uses array_reverse to ensure user defined |
517 // Returns a list of methods - uses array_reverse to ensure user defined |
432 // methods are listed before server defined methods |
518 // methods are listed before server defined methods |
433 return array_reverse(array_keys($this->callbacks)); |
519 return array_reverse(array_keys($this->callbacks)); |
434 } |
520 } |
435 function multiCall($methodcalls) { |
521 |
|
522 function multiCall($methodcalls) |
|
523 { |
436 // See http://www.xmlrpc.com/discuss/msgReader$1208 |
524 // See http://www.xmlrpc.com/discuss/msgReader$1208 |
437 $return = array(); |
525 $return = array(); |
438 foreach ($methodcalls as $call) { |
526 foreach ($methodcalls as $call) { |
439 $method = $call['methodName']; |
527 $method = $call['methodName']; |
440 $params = $call['params']; |
528 $params = $call['params']; |
527 $this->port = $port; |
628 $this->port = $port; |
528 } |
629 } |
529 $this->useragent = 'The Incutio XML-RPC PHP Library'; |
630 $this->useragent = 'The Incutio XML-RPC PHP Library'; |
530 $this->timeout = $timeout; |
631 $this->timeout = $timeout; |
531 } |
632 } |
532 function query() { |
633 |
|
634 function query() |
|
635 { |
533 $args = func_get_args(); |
636 $args = func_get_args(); |
534 $method = array_shift($args); |
637 $method = array_shift($args); |
535 $request = new IXR_Request($method, $args); |
638 $request = new IXR_Request($method, $args); |
536 $length = $request->getLength(); |
639 $length = $request->getLength(); |
537 $xml = $request->getXml(); |
640 $xml = $request->getXml(); |
538 $r = "\r\n"; |
641 $r = "\r\n"; |
539 $request = "POST {$this->path} HTTP/1.0$r"; |
642 $request = "POST {$this->path} HTTP/1.0$r"; |
540 |
643 |
541 $this->headers['Host'] = $this->server; |
644 // Merged from WP #8145 - allow custom headers |
542 $this->headers['Content-Type'] = 'text/xml'; |
645 $this->headers['Host'] = $this->server; |
543 $this->headers['User-Agent'] = $this->useragent; |
646 $this->headers['Content-Type'] = 'text/xml'; |
544 $this->headers['Content-Length']= $length; |
647 $this->headers['User-Agent'] = $this->useragent; |
545 |
648 $this->headers['Content-Length']= $length; |
546 foreach( $this->headers as $header => $value ) { |
649 |
547 $request .= "{$header}: {$value}{$r}"; |
650 foreach( $this->headers as $header => $value ) { |
548 } |
651 $request .= "{$header}: {$value}{$r}"; |
549 $request .= $r; |
652 } |
|
653 $request .= $r; |
550 |
654 |
551 $request .= $xml; |
655 $request .= $xml; |
|
656 |
552 // Now send the request |
657 // Now send the request |
553 if ($this->debug) { |
658 if ($this->debug) { |
554 echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n"; |
659 echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n"; |
555 } |
660 } |
|
661 |
556 if ($this->timeout) { |
662 if ($this->timeout) { |
557 $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); |
663 $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); |
558 } else { |
664 } else { |
559 $fp = @fsockopen($this->server, $this->port, $errno, $errstr); |
665 $fp = @fsockopen($this->server, $this->port, $errno, $errstr); |
560 } |
666 } |
561 if (!$fp) { |
667 if (!$fp) { |
562 $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr"); |
668 $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); |
563 return false; |
669 return false; |
564 } |
670 } |
565 fputs($fp, $request); |
671 fputs($fp, $request); |
566 $contents = ''; |
672 $contents = ''; |
567 $debug_contents = ''; |
673 $debugContents = ''; |
568 $gotFirstLine = false; |
674 $gotFirstLine = false; |
569 $gettingHeaders = true; |
675 $gettingHeaders = true; |
570 while (!feof($fp)) { |
676 while (!feof($fp)) { |
571 $line = fgets($fp, 4096); |
677 $line = fgets($fp, 4096); |
572 if (!$gotFirstLine) { |
678 if (!$gotFirstLine) { |
573 // Check line for '200' |
679 // Check line for '200' |
574 if (strstr($line, '200') === false) { |
680 if (strstr($line, '200') === false) { |
575 $this->error = new IXR_Error(-32301, 'transport error - HTTP status code was not 200'); |
681 $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); |
576 return false; |
682 return false; |
577 } |
683 } |
578 $gotFirstLine = true; |
684 $gotFirstLine = true; |
579 } |
685 } |
580 if (trim($line) == '') { |
686 if (trim($line) == '') { |
581 $gettingHeaders = false; |
687 $gettingHeaders = false; |
582 } |
688 } |
583 if (!$gettingHeaders) { |
689 if (!$gettingHeaders) { |
584 $contents .= trim($line); |
690 // merged from WP #12559 - remove trim |
|
691 $contents .= $line; |
585 } |
692 } |
586 if ($this->debug) { |
693 if ($this->debug) { |
587 $debug_contents .= $line; |
694 $debugContents .= $line; |
588 } |
695 } |
589 } |
696 } |
590 if ($this->debug) { |
697 if ($this->debug) { |
591 echo '<pre class="ixr_response">'.htmlspecialchars($debug_contents)."\n</pre>\n\n"; |
698 echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n"; |
592 } |
699 } |
|
700 |
593 // Now parse what we've got back |
701 // Now parse what we've got back |
594 $this->message = new IXR_Message($contents); |
702 $this->message = new IXR_Message($contents); |
595 if (!$this->message->parse()) { |
703 if (!$this->message->parse()) { |
596 // XML error |
704 // XML error |
597 $this->error = new IXR_Error(-32700, 'parse error. not well formed'); |
705 $this->error = new IXR_Error(-32700, 'parse error. not well formed'); |
598 return false; |
706 return false; |
599 } |
707 } |
|
708 |
600 // Is the message a fault? |
709 // Is the message a fault? |
601 if ($this->message->messageType == 'fault') { |
710 if ($this->message->messageType == 'fault') { |
602 $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); |
711 $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); |
603 return false; |
712 return false; |
604 } |
713 } |
|
714 |
605 // Message must be OK |
715 // Message must be OK |
606 return true; |
716 return true; |
607 } |
717 } |
608 function getResponse() { |
718 |
|
719 function getResponse() |
|
720 { |
609 // methodResponses can only have one param - return that |
721 // methodResponses can only have one param - return that |
610 return $this->message->params[0]; |
722 return $this->message->params[0]; |
611 } |
723 } |
612 function isError() { |
724 |
|
725 function isError() |
|
726 { |
613 return (is_object($this->error)); |
727 return (is_object($this->error)); |
614 } |
728 } |
615 function getErrorCode() { |
729 |
|
730 function getErrorCode() |
|
731 { |
616 return $this->error->code; |
732 return $this->error->code; |
617 } |
733 } |
618 function getErrorMessage() { |
734 |
|
735 function getErrorMessage() |
|
736 { |
619 return $this->error->message; |
737 return $this->error->message; |
620 } |
738 } |
621 } |
739 } |
|
740 |
622 |
741 |
623 /** |
742 /** |
624 * IXR_Error |
743 * IXR_Error |
625 * |
744 * |
626 * @package IXR |
745 * @package IXR |
627 * @since 1.5 |
746 * @since 1.5 |
628 */ |
747 */ |
629 class IXR_Error { |
748 class IXR_Error |
|
749 { |
630 var $code; |
750 var $code; |
631 var $message; |
751 var $message; |
632 function IXR_Error($code, $message) { |
752 |
|
753 function IXR_Error($code, $message) |
|
754 { |
633 $this->code = $code; |
755 $this->code = $code; |
634 // WP adds htmlspecialchars(). See #5666 |
|
635 $this->message = htmlspecialchars($message); |
756 $this->message = htmlspecialchars($message); |
636 } |
757 } |
637 function getXml() { |
758 |
|
759 function getXml() |
|
760 { |
638 $xml = <<<EOD |
761 $xml = <<<EOD |
639 <methodResponse> |
762 <methodResponse> |
640 <fault> |
763 <fault> |
641 <value> |
764 <value> |
642 <struct> |
765 <struct> |
670 var $day; |
793 var $day; |
671 var $hour; |
794 var $hour; |
672 var $minute; |
795 var $minute; |
673 var $second; |
796 var $second; |
674 var $timezone; |
797 var $timezone; |
675 function IXR_Date($time) { |
798 |
|
799 function IXR_Date($time) |
|
800 { |
676 // $time can be a PHP timestamp or an ISO one |
801 // $time can be a PHP timestamp or an ISO one |
677 if (is_numeric($time)) { |
802 if (is_numeric($time)) { |
678 $this->parseTimestamp($time); |
803 $this->parseTimestamp($time); |
679 } else { |
804 } else { |
680 $this->parseIso($time); |
805 $this->parseIso($time); |
681 } |
806 } |
682 } |
807 } |
683 function parseTimestamp($timestamp) { |
808 |
|
809 function parseTimestamp($timestamp) |
|
810 { |
684 $this->year = date('Y', $timestamp); |
811 $this->year = date('Y', $timestamp); |
685 $this->month = date('m', $timestamp); |
812 $this->month = date('m', $timestamp); |
686 $this->day = date('d', $timestamp); |
813 $this->day = date('d', $timestamp); |
687 $this->hour = date('H', $timestamp); |
814 $this->hour = date('H', $timestamp); |
688 $this->minute = date('i', $timestamp); |
815 $this->minute = date('i', $timestamp); |
689 $this->second = date('s', $timestamp); |
816 $this->second = date('s', $timestamp); |
690 // WP adds timezone. See #2036 |
|
691 $this->timezone = ''; |
817 $this->timezone = ''; |
692 } |
818 } |
693 function parseIso($iso) { |
819 |
|
820 function parseIso($iso) |
|
821 { |
694 $this->year = substr($iso, 0, 4); |
822 $this->year = substr($iso, 0, 4); |
695 $this->month = substr($iso, 4, 2); |
823 $this->month = substr($iso, 4, 2); |
696 $this->day = substr($iso, 6, 2); |
824 $this->day = substr($iso, 6, 2); |
697 $this->hour = substr($iso, 9, 2); |
825 $this->hour = substr($iso, 9, 2); |
698 $this->minute = substr($iso, 12, 2); |
826 $this->minute = substr($iso, 12, 2); |
699 $this->second = substr($iso, 15, 2); |
827 $this->second = substr($iso, 15, 2); |
700 // WP adds timezone. See #2036 |
|
701 $this->timezone = substr($iso, 17); |
828 $this->timezone = substr($iso, 17); |
702 } |
829 } |
703 function getIso() { |
830 |
704 // WP adds timezone. See #2036 |
831 function getIso() |
|
832 { |
705 return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; |
833 return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; |
706 } |
834 } |
707 function getXml() { |
835 |
|
836 function getXml() |
|
837 { |
708 return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; |
838 return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; |
709 } |
839 } |
710 function getTimestamp() { |
840 |
|
841 function getTimestamp() |
|
842 { |
711 return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); |
843 return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); |
712 } |
844 } |
713 } |
845 } |
714 |
846 |
715 /** |
847 /** |
716 * IXR_Base64 |
848 * IXR_Base64 |
717 * |
849 * |
718 * @package IXR |
850 * @package IXR |
719 * @since 1.5 |
851 * @since 1.5 |
720 */ |
852 */ |
721 class IXR_Base64 { |
853 class IXR_Base64 |
|
854 { |
722 var $data; |
855 var $data; |
723 function IXR_Base64($data) { |
856 |
|
857 function IXR_Base64($data) |
|
858 { |
724 $this->data = $data; |
859 $this->data = $data; |
725 } |
860 } |
726 function getXml() { |
861 |
|
862 function getXml() |
|
863 { |
727 return '<base64>'.base64_encode($this->data).'</base64>'; |
864 return '<base64>'.base64_encode($this->data).'</base64>'; |
728 } |
865 } |
729 } |
866 } |
730 |
867 |
731 /** |
868 /** |
732 * IXR_IntrospectionServer |
869 * IXR_IntrospectionServer |
733 * |
870 * |
734 * @package IXR |
871 * @package IXR |
735 * @since 1.5 |
872 * @since 1.5 |
736 */ |
873 */ |
737 class IXR_IntrospectionServer extends IXR_Server { |
874 class IXR_IntrospectionServer extends IXR_Server |
|
875 { |
738 var $signatures; |
876 var $signatures; |
739 var $help; |
877 var $help; |
740 function IXR_IntrospectionServer() { |
878 |
|
879 function IXR_IntrospectionServer() |
|
880 { |
741 $this->setCallbacks(); |
881 $this->setCallbacks(); |
742 $this->setCapabilities(); |
882 $this->setCapabilities(); |
743 $this->capabilities['introspection'] = array( |
883 $this->capabilities['introspection'] = array( |
744 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', |
884 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', |
745 'specVersion' => 1 |
885 'specVersion' => 1 |
767 'this:methodHelp', |
907 'this:methodHelp', |
768 array('string', 'string'), |
908 array('string', 'string'), |
769 'Returns a documentation string for the specified method' |
909 'Returns a documentation string for the specified method' |
770 ); |
910 ); |
771 } |
911 } |
772 function addCallback($method, $callback, $args, $help) { |
912 |
|
913 function addCallback($method, $callback, $args, $help) |
|
914 { |
773 $this->callbacks[$method] = $callback; |
915 $this->callbacks[$method] = $callback; |
774 $this->signatures[$method] = $args; |
916 $this->signatures[$method] = $args; |
775 $this->help[$method] = $help; |
917 $this->help[$method] = $help; |
776 } |
918 } |
777 function call($methodname, $args) { |
919 |
|
920 function call($methodname, $args) |
|
921 { |
778 // Make sure it's in an array |
922 // Make sure it's in an array |
779 if ($args && !is_array($args)) { |
923 if ($args && !is_array($args)) { |
780 $args = array($args); |
924 $args = array($args); |
781 } |
925 } |
|
926 |
782 // Over-rides default call method, adds signature check |
927 // Over-rides default call method, adds signature check |
783 if (!$this->hasMethod($methodname)) { |
928 if (!$this->hasMethod($methodname)) { |
784 return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); |
929 return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); |
785 } |
930 } |
786 $method = $this->callbacks[$methodname]; |
931 $method = $this->callbacks[$methodname]; |
787 $signature = $this->signatures[$methodname]; |
932 $signature = $this->signatures[$methodname]; |
788 $returnType = array_shift($signature); |
933 $returnType = array_shift($signature); |
|
934 |
789 // Check the number of arguments |
935 // Check the number of arguments |
790 if (count($args) != count($signature)) { |
936 if (count($args) != count($signature)) { |
791 return new IXR_Error(-32602, 'server error. wrong number of method parameters'); |
937 return new IXR_Error(-32602, 'server error. wrong number of method parameters'); |
792 } |
938 } |
|
939 |
793 // Check the argument types |
940 // Check the argument types |
794 $ok = true; |
941 $ok = true; |
795 $argsbackup = $args; |
942 $argsbackup = $args; |
796 for ($i = 0, $j = count($args); $i < $j; $i++) { |
943 for ($i = 0, $j = count($args); $i < $j; $i++) { |
797 $arg = array_shift($args); |
944 $arg = array_shift($args); |