├── IXR_Library.php ├── README.md └── ping.php /IXR_Library.php: -------------------------------------------------------------------------------- 1 | data = $data; 50 | if (!$type) { 51 | $type = $this->calculateType(); 52 | } 53 | $this->type = $type; 54 | if ($type == 'struct') { 55 | // Turn all the values in the array in to new IXR_Value objects 56 | foreach ($this->data as $key => $value) { 57 | $this->data[$key] = new IXR_Value($value); 58 | } 59 | } 60 | if ($type == 'array') { 61 | for ($i = 0, $j = count($this->data); $i < $j; $i++) { 62 | $this->data[$i] = new IXR_Value($this->data[$i]); 63 | } 64 | } 65 | } 66 | 67 | function calculateType() 68 | { 69 | if ($this->data === true || $this->data === false) { 70 | return 'boolean'; 71 | } 72 | if (is_integer($this->data)) { 73 | return 'int'; 74 | } 75 | if (is_double($this->data)) { 76 | return 'double'; 77 | } 78 | 79 | // Deal with IXR object types base64 and date 80 | if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { 81 | return 'date'; 82 | } 83 | if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { 84 | return 'base64'; 85 | } 86 | 87 | // If it is a normal PHP object convert it in to a struct 88 | if (is_object($this->data)) { 89 | $this->data = get_object_vars($this->data); 90 | return 'struct'; 91 | } 92 | if (!is_array($this->data)) { 93 | return 'string'; 94 | } 95 | 96 | // We have an array - is it an array or a struct? 97 | if ($this->isStruct($this->data)) { 98 | return 'struct'; 99 | } else { 100 | return 'array'; 101 | } 102 | } 103 | 104 | function getXml() 105 | { 106 | // Return XML for this value 107 | switch ($this->type) { 108 | case 'boolean': 109 | return ''.(($this->data) ? '1' : '0').''; 110 | break; 111 | case 'int': 112 | return ''.$this->data.''; 113 | break; 114 | case 'double': 115 | return ''.$this->data.''; 116 | break; 117 | case 'string': 118 | return ''.htmlspecialchars($this->data).''; 119 | break; 120 | case 'array': 121 | $return = ''."\n"; 122 | foreach ($this->data as $item) { 123 | $return .= ' '.$item->getXml()."\n"; 124 | } 125 | $return .= ''; 126 | return $return; 127 | break; 128 | case 'struct': 129 | $return = ''."\n"; 130 | foreach ($this->data as $name => $value) { 131 | $return .= " $name"; 132 | $return .= $value->getXml()."\n"; 133 | } 134 | $return .= ''; 135 | return $return; 136 | break; 137 | case 'date': 138 | case 'base64': 139 | return $this->data->getXml(); 140 | break; 141 | } 142 | return false; 143 | } 144 | 145 | /** 146 | * Checks whether or not the supplied array is a struct or not 147 | * 148 | * @param unknown_type $array 149 | * @return boolean 150 | */ 151 | function isStruct($array) 152 | { 153 | $expected = 0; 154 | foreach ($array as $key => $value) { 155 | if ((string)$key != (string)$expected) { 156 | return true; 157 | } 158 | $expected++; 159 | } 160 | return false; 161 | } 162 | } 163 | 164 | /** 165 | * IXR_MESSAGE 166 | * 167 | * @package IXR 168 | * @since 1.5 169 | * 170 | */ 171 | class IXR_Message 172 | { 173 | var $message; 174 | var $messageType; // methodCall / methodResponse / fault 175 | var $faultCode; 176 | var $faultString; 177 | var $methodName; 178 | var $params; 179 | 180 | // Current variable stacks 181 | var $_arraystructs = array(); // The stack used to keep track of the current array/struct 182 | var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array 183 | var $_currentStructName = array(); // A stack as well 184 | var $_param; 185 | var $_value; 186 | var $_currentTag; 187 | var $_currentTagContents; 188 | // The XML parser 189 | var $_parser; 190 | 191 | function IXR_Message($message) 192 | { 193 | $this->message =& $message; 194 | } 195 | 196 | function parse() 197 | { 198 | // first remove the XML declaration 199 | // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages 200 | $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1); 201 | $this->message = substr_replace($this->message, $header, 0, 100); 202 | if (trim($this->message) == '') { 203 | return false; 204 | } 205 | $this->_parser = xml_parser_create(); 206 | // Set XML parser to take the case of tags in to account 207 | xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); 208 | // Set XML parser callback functions 209 | xml_set_object($this->_parser, $this); 210 | xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); 211 | xml_set_character_data_handler($this->_parser, 'cdata'); 212 | $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages 213 | do { 214 | if (strlen($this->message) <= $chunk_size) { 215 | $final = true; 216 | } 217 | $part = substr($this->message, 0, $chunk_size); 218 | $this->message = substr($this->message, $chunk_size); 219 | if (!xml_parse($this->_parser, $part, $final)) { 220 | return false; 221 | } 222 | if ($final) { 223 | break; 224 | } 225 | } while (true); 226 | xml_parser_free($this->_parser); 227 | 228 | // Grab the error messages, if any 229 | if ($this->messageType == 'fault') { 230 | $this->faultCode = $this->params[0]['faultCode']; 231 | $this->faultString = $this->params[0]['faultString']; 232 | } 233 | return true; 234 | } 235 | 236 | function tag_open($parser, $tag, $attr) 237 | { 238 | $this->_currentTagContents = ''; 239 | $this->currentTag = $tag; 240 | switch($tag) { 241 | case 'methodCall': 242 | case 'methodResponse': 243 | case 'fault': 244 | $this->messageType = $tag; 245 | break; 246 | /* Deal with stacks of arrays and structs */ 247 | case 'data': // data is to all intents and puposes more interesting than array 248 | $this->_arraystructstypes[] = 'array'; 249 | $this->_arraystructs[] = array(); 250 | break; 251 | case 'struct': 252 | $this->_arraystructstypes[] = 'struct'; 253 | $this->_arraystructs[] = array(); 254 | break; 255 | } 256 | } 257 | 258 | function cdata($parser, $cdata) 259 | { 260 | $this->_currentTagContents .= $cdata; 261 | } 262 | 263 | function tag_close($parser, $tag) 264 | { 265 | $valueFlag = false; 266 | switch($tag) { 267 | case 'int': 268 | case 'i4': 269 | $value = (int)trim($this->_currentTagContents); 270 | $valueFlag = true; 271 | break; 272 | case 'double': 273 | $value = (double)trim($this->_currentTagContents); 274 | $valueFlag = true; 275 | break; 276 | case 'string': 277 | $value = (string)trim($this->_currentTagContents); 278 | $valueFlag = true; 279 | break; 280 | case 'dateTime.iso8601': 281 | $value = new IXR_Date(trim($this->_currentTagContents)); 282 | $valueFlag = true; 283 | break; 284 | case 'value': 285 | // "If no type is indicated, the type is string." 286 | if (trim($this->_currentTagContents) != '') { 287 | $value = (string)$this->_currentTagContents; 288 | $valueFlag = true; 289 | } 290 | break; 291 | case 'boolean': 292 | $value = (boolean)trim($this->_currentTagContents); 293 | $valueFlag = true; 294 | break; 295 | case 'base64': 296 | $value = base64_decode($this->_currentTagContents); 297 | $valueFlag = true; 298 | break; 299 | /* Deal with stacks of arrays and structs */ 300 | case 'data': 301 | case 'struct': 302 | $value = array_pop($this->_arraystructs); 303 | array_pop($this->_arraystructstypes); 304 | $valueFlag = true; 305 | break; 306 | case 'member': 307 | array_pop($this->_currentStructName); 308 | break; 309 | case 'name': 310 | $this->_currentStructName[] = trim($this->_currentTagContents); 311 | break; 312 | case 'methodName': 313 | $this->methodName = trim($this->_currentTagContents); 314 | break; 315 | } 316 | 317 | if ($valueFlag) { 318 | if (count($this->_arraystructs) > 0) { 319 | // Add value to struct or array 320 | if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { 321 | // Add to struct 322 | $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; 323 | } else { 324 | // Add to array 325 | $this->_arraystructs[count($this->_arraystructs)-1][] = $value; 326 | } 327 | } else { 328 | // Just add as a paramater 329 | $this->params[] = $value; 330 | } 331 | } 332 | $this->_currentTagContents = ''; 333 | } 334 | } 335 | 336 | /** 337 | * IXR_Server 338 | * 339 | * @package IXR 340 | * @since 1.5 341 | */ 342 | class IXR_Server 343 | { 344 | var $data; 345 | var $callbacks = array(); 346 | var $message; 347 | var $capabilities; 348 | 349 | function IXR_Server($callbacks = false, $data = false, $wait = false) 350 | { 351 | $this->setCapabilities(); 352 | if ($callbacks) { 353 | $this->callbacks = $callbacks; 354 | } 355 | $this->setCallbacks(); 356 | if (!$wait) { 357 | $this->serve($data); 358 | } 359 | } 360 | 361 | function serve($data = false) 362 | { 363 | if (!$data) { 364 | if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') { 365 | header('Content-Type: text/plain'); // merged from WP #9093 366 | die('XML-RPC server accepts POST requests only.'); 367 | } 368 | 369 | global $HTTP_RAW_POST_DATA; 370 | if (empty($HTTP_RAW_POST_DATA)) { 371 | // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293 372 | $data = file_get_contents('php://input'); 373 | } else { 374 | $data =& $HTTP_RAW_POST_DATA; 375 | } 376 | } 377 | $this->message = new IXR_Message($data); 378 | if (!$this->message->parse()) { 379 | $this->error(-32700, 'parse error. not well formed'); 380 | } 381 | if ($this->message->messageType != 'methodCall') { 382 | $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); 383 | } 384 | $result = $this->call($this->message->methodName, $this->message->params); 385 | 386 | // Is the result an error? 387 | if (is_a($result, 'IXR_Error')) { 388 | $this->error($result); 389 | } 390 | 391 | // Encode the result 392 | $r = new IXR_Value($result); 393 | $resultxml = $r->getXml(); 394 | 395 | // Create the XML 396 | $xml = << 398 | 399 | 400 | 401 | $resultxml 402 | 403 | 404 | 405 | 406 | 407 | EOD; 408 | // Send it 409 | $this->output($xml); 410 | } 411 | 412 | function call($methodname, $args) 413 | { 414 | if (!$this->hasMethod($methodname)) { 415 | return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); 416 | } 417 | $method = $this->callbacks[$methodname]; 418 | 419 | // Perform the callback and send the response 420 | if (count($args) == 1) { 421 | // If only one paramater just send that instead of the whole array 422 | $args = $args[0]; 423 | } 424 | 425 | // Are we dealing with a function or a method? 426 | if (is_string($method) && substr($method, 0, 5) == 'this:') { 427 | // It's a class method - check it exists 428 | $method = substr($method, 5); 429 | if (!method_exists($this, $method)) { 430 | return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); 431 | } 432 | 433 | //Call the method 434 | $result = $this->$method($args); 435 | } else { 436 | // It's a function - does it exist? 437 | if (is_array($method)) { 438 | if (!method_exists($method[0], $method[1])) { 439 | return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.'); 440 | } 441 | } else if (!function_exists($method)) { 442 | return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); 443 | } 444 | 445 | // Call the function 446 | $result = call_user_func($method, $args); 447 | } 448 | return $result; 449 | } 450 | 451 | function error($error, $message = false) 452 | { 453 | // Accepts either an error object or an error code and message 454 | if ($message && !is_object($error)) { 455 | $error = new IXR_Error($error, $message); 456 | } 457 | $this->output($error->getXml()); 458 | } 459 | 460 | function output($xml) 461 | { 462 | $xml = ''."\n".$xml; 463 | $length = strlen($xml); 464 | header('Connection: close'); 465 | header('Content-Length: '.$length); 466 | header('Content-Type: text/xml'); 467 | header('Date: '.date('r')); 468 | echo $xml; 469 | exit; 470 | } 471 | 472 | function hasMethod($method) 473 | { 474 | return in_array($method, array_keys($this->callbacks)); 475 | } 476 | 477 | function setCapabilities() 478 | { 479 | // Initialises capabilities array 480 | $this->capabilities = array( 481 | 'xmlrpc' => array( 482 | 'specUrl' => 'http://www.xmlrpc.com/spec', 483 | 'specVersion' => 1 484 | ), 485 | 'faults_interop' => array( 486 | 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 487 | 'specVersion' => 20010516 488 | ), 489 | 'system.multicall' => array( 490 | 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 491 | 'specVersion' => 1 492 | ), 493 | ); 494 | } 495 | 496 | function getCapabilities($args) 497 | { 498 | return $this->capabilities; 499 | } 500 | 501 | function setCallbacks() 502 | { 503 | $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; 504 | $this->callbacks['system.listMethods'] = 'this:listMethods'; 505 | $this->callbacks['system.multicall'] = 'this:multiCall'; 506 | } 507 | 508 | function listMethods($args) 509 | { 510 | // Returns a list of methods - uses array_reverse to ensure user defined 511 | // methods are listed before server defined methods 512 | return array_reverse(array_keys($this->callbacks)); 513 | } 514 | 515 | function multiCall($methodcalls) 516 | { 517 | // See http://www.xmlrpc.com/discuss/msgReader$1208 518 | $return = array(); 519 | foreach ($methodcalls as $call) { 520 | $method = $call['methodName']; 521 | $params = $call['params']; 522 | if ($method == 'system.multicall') { 523 | $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); 524 | } else { 525 | $result = $this->call($method, $params); 526 | } 527 | if (is_a($result, 'IXR_Error')) { 528 | $return[] = array( 529 | 'faultCode' => $result->code, 530 | 'faultString' => $result->message 531 | ); 532 | } else { 533 | $return[] = array($result); 534 | } 535 | } 536 | return $return; 537 | } 538 | } 539 | 540 | /** 541 | * IXR_Request 542 | * 543 | * @package IXR 544 | * @since 1.5 545 | */ 546 | class IXR_Request 547 | { 548 | var $method; 549 | var $args; 550 | var $xml; 551 | 552 | function IXR_Request($method, $args) 553 | { 554 | $this->method = $method; 555 | $this->args = $args; 556 | $this->xml = << 558 | 559 | {$this->method} 560 | 561 | 562 | EOD; 563 | foreach ($this->args as $arg) { 564 | $this->xml .= ''; 565 | $v = new IXR_Value($arg); 566 | $this->xml .= $v->getXml(); 567 | $this->xml .= "\n"; 568 | } 569 | $this->xml .= ''; 570 | } 571 | 572 | function getLength() 573 | { 574 | return strlen($this->xml); 575 | } 576 | 577 | function getXml() 578 | { 579 | return $this->xml; 580 | } 581 | } 582 | 583 | /** 584 | * IXR_Client 585 | * 586 | * @package IXR 587 | * @since 1.5 588 | * 589 | */ 590 | class IXR_Client 591 | { 592 | var $server; 593 | var $port; 594 | var $path; 595 | var $useragent; 596 | var $response; 597 | var $message = false; 598 | var $debug = false; 599 | var $timeout; 600 | 601 | // Storage place for an error message 602 | var $error = false; 603 | 604 | function IXR_Client($server, $path = false, $port = 80, $timeout = 15) 605 | { 606 | if (!$path) { 607 | // Assume we have been given a URL instead 608 | $bits = parse_url($server); 609 | $this->server = $bits['host']; 610 | $this->port = isset($bits['port']) ? $bits['port'] : 80; 611 | $this->path = isset($bits['path']) ? $bits['path'] : '/'; 612 | 613 | // Make absolutely sure we have a path 614 | if (!$this->path) { 615 | $this->path = '/'; 616 | } 617 | } else { 618 | $this->server = $server; 619 | $this->path = $path; 620 | $this->port = $port; 621 | } 622 | $this->useragent = 'The Incutio XML-RPC PHP Library'; 623 | $this->timeout = $timeout; 624 | } 625 | 626 | function query() 627 | { 628 | $args = func_get_args(); 629 | $method = array_shift($args); 630 | $request = new IXR_Request($method, $args); 631 | $length = $request->getLength(); 632 | $xml = $request->getXml(); 633 | $r = "\r\n"; 634 | $request = "POST {$this->path} HTTP/1.0$r"; 635 | 636 | // Merged from WP #8145 - allow custom headers 637 | $this->headers['Host'] = $this->server; 638 | $this->headers['Content-Type'] = 'text/xml'; 639 | $this->headers['User-Agent'] = $this->useragent; 640 | $this->headers['Content-Length']= $length; 641 | 642 | foreach( $this->headers as $header => $value ) { 643 | $request .= "{$header}: {$value}{$r}"; 644 | } 645 | $request .= $r; 646 | 647 | $request .= $xml; 648 | 649 | // Now send the request 650 | if ($this->debug) { 651 | echo '
'.htmlspecialchars($request)."\n
\n\n"; 652 | } 653 | 654 | if ($this->timeout) { 655 | $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); 656 | } else { 657 | $fp = @fsockopen($this->server, $this->port, $errno, $errstr); 658 | } 659 | if (!$fp) { 660 | $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); 661 | return false; 662 | } 663 | fputs($fp, $request); 664 | $contents = ''; 665 | $debugContents = ''; 666 | $gotFirstLine = false; 667 | $gettingHeaders = true; 668 | while (!feof($fp)) { 669 | $line = fgets($fp, 4096); 670 | if (!$gotFirstLine) { 671 | // Check line for '200' 672 | if (strstr($line, '200') === false) { 673 | $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); 674 | return false; 675 | } 676 | $gotFirstLine = true; 677 | } 678 | if (trim($line) == '') { 679 | $gettingHeaders = false; 680 | } 681 | if (!$gettingHeaders) { 682 | // merged from WP #12559 - remove trim 683 | $contents .= $line; 684 | } 685 | if ($this->debug) { 686 | $debugContents .= $line; 687 | } 688 | } 689 | if ($this->debug) { 690 | echo '
'.htmlspecialchars($debugContents)."\n
\n\n"; 691 | } 692 | 693 | // Now parse what we've got back 694 | $this->message = new IXR_Message($contents); 695 | if (!$this->message->parse()) { 696 | // XML error 697 | $this->error = new IXR_Error(-32700, 'parse error. not well formed'); 698 | return false; 699 | } 700 | 701 | // Is the message a fault? 702 | if ($this->message->messageType == 'fault') { 703 | $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); 704 | return false; 705 | } 706 | 707 | // Message must be OK 708 | return true; 709 | } 710 | 711 | function getResponse() 712 | { 713 | // methodResponses can only have one param - return that 714 | return $this->message->params[0]; 715 | } 716 | 717 | function isError() 718 | { 719 | return (is_object($this->error)); 720 | } 721 | 722 | function getErrorCode() 723 | { 724 | return $this->error->code; 725 | } 726 | 727 | function getErrorMessage() 728 | { 729 | return $this->error->message; 730 | } 731 | } 732 | 733 | 734 | /** 735 | * IXR_Error 736 | * 737 | * @package IXR 738 | * @since 1.5 739 | */ 740 | class IXR_Error 741 | { 742 | var $code; 743 | var $message; 744 | 745 | function IXR_Error($code, $message) 746 | { 747 | $this->code = $code; 748 | $this->message = htmlspecialchars($message); 749 | } 750 | 751 | function getXml() 752 | { 753 | $xml = << 755 | 756 | 757 | 758 | 759 | faultCode 760 | {$this->code} 761 | 762 | 763 | faultString 764 | {$this->message} 765 | 766 | 767 | 768 | 769 | 770 | 771 | EOD; 772 | return $xml; 773 | } 774 | } 775 | 776 | /** 777 | * IXR_Date 778 | * 779 | * @package IXR 780 | * @since 1.5 781 | */ 782 | class IXR_Date { 783 | var $year; 784 | var $month; 785 | var $day; 786 | var $hour; 787 | var $minute; 788 | var $second; 789 | var $timezone; 790 | 791 | function IXR_Date($time) 792 | { 793 | // $time can be a PHP timestamp or an ISO one 794 | if (is_numeric($time)) { 795 | $this->parseTimestamp($time); 796 | } else { 797 | $this->parseIso($time); 798 | } 799 | } 800 | 801 | function parseTimestamp($timestamp) 802 | { 803 | $this->year = date('Y', $timestamp); 804 | $this->month = date('m', $timestamp); 805 | $this->day = date('d', $timestamp); 806 | $this->hour = date('H', $timestamp); 807 | $this->minute = date('i', $timestamp); 808 | $this->second = date('s', $timestamp); 809 | $this->timezone = ''; 810 | } 811 | 812 | function parseIso($iso) 813 | { 814 | $this->year = substr($iso, 0, 4); 815 | $this->month = substr($iso, 4, 2); 816 | $this->day = substr($iso, 6, 2); 817 | $this->hour = substr($iso, 9, 2); 818 | $this->minute = substr($iso, 12, 2); 819 | $this->second = substr($iso, 15, 2); 820 | $this->timezone = substr($iso, 17); 821 | } 822 | 823 | function getIso() 824 | { 825 | return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; 826 | } 827 | 828 | function getXml() 829 | { 830 | return ''.$this->getIso().''; 831 | } 832 | 833 | function getTimestamp() 834 | { 835 | return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); 836 | } 837 | } 838 | 839 | /** 840 | * IXR_Base64 841 | * 842 | * @package IXR 843 | * @since 1.5 844 | */ 845 | class IXR_Base64 846 | { 847 | var $data; 848 | 849 | function IXR_Base64($data) 850 | { 851 | $this->data = $data; 852 | } 853 | 854 | function getXml() 855 | { 856 | return ''.base64_encode($this->data).''; 857 | } 858 | } 859 | 860 | /** 861 | * IXR_IntrospectionServer 862 | * 863 | * @package IXR 864 | * @since 1.5 865 | */ 866 | class IXR_IntrospectionServer extends IXR_Server 867 | { 868 | var $signatures; 869 | var $help; 870 | 871 | function IXR_IntrospectionServer() 872 | { 873 | $this->setCallbacks(); 874 | $this->setCapabilities(); 875 | $this->capabilities['introspection'] = array( 876 | 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', 877 | 'specVersion' => 1 878 | ); 879 | $this->addCallback( 880 | 'system.methodSignature', 881 | 'this:methodSignature', 882 | array('array', 'string'), 883 | 'Returns an array describing the return type and required parameters of a method' 884 | ); 885 | $this->addCallback( 886 | 'system.getCapabilities', 887 | 'this:getCapabilities', 888 | array('struct'), 889 | 'Returns a struct describing the XML-RPC specifications supported by this server' 890 | ); 891 | $this->addCallback( 892 | 'system.listMethods', 893 | 'this:listMethods', 894 | array('array'), 895 | 'Returns an array of available methods on this server' 896 | ); 897 | $this->addCallback( 898 | 'system.methodHelp', 899 | 'this:methodHelp', 900 | array('string', 'string'), 901 | 'Returns a documentation string for the specified method' 902 | ); 903 | } 904 | 905 | function addCallback($method, $callback, $args, $help) 906 | { 907 | $this->callbacks[$method] = $callback; 908 | $this->signatures[$method] = $args; 909 | $this->help[$method] = $help; 910 | } 911 | 912 | function call($methodname, $args) 913 | { 914 | // Make sure it's in an array 915 | if ($args && !is_array($args)) { 916 | $args = array($args); 917 | } 918 | 919 | // Over-rides default call method, adds signature check 920 | if (!$this->hasMethod($methodname)) { 921 | return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); 922 | } 923 | $method = $this->callbacks[$methodname]; 924 | $signature = $this->signatures[$methodname]; 925 | $returnType = array_shift($signature); 926 | 927 | // Check the number of arguments 928 | if (count($args) != count($signature)) { 929 | return new IXR_Error(-32602, 'server error. wrong number of method parameters'); 930 | } 931 | 932 | // Check the argument types 933 | $ok = true; 934 | $argsbackup = $args; 935 | for ($i = 0, $j = count($args); $i < $j; $i++) { 936 | $arg = array_shift($args); 937 | $type = array_shift($signature); 938 | switch ($type) { 939 | case 'int': 940 | case 'i4': 941 | if (is_array($arg) || !is_int($arg)) { 942 | $ok = false; 943 | } 944 | break; 945 | case 'base64': 946 | case 'string': 947 | if (!is_string($arg)) { 948 | $ok = false; 949 | } 950 | break; 951 | case 'boolean': 952 | if ($arg !== false && $arg !== true) { 953 | $ok = false; 954 | } 955 | break; 956 | case 'float': 957 | case 'double': 958 | if (!is_float($arg)) { 959 | $ok = false; 960 | } 961 | break; 962 | case 'date': 963 | case 'dateTime.iso8601': 964 | if (!is_a($arg, 'IXR_Date')) { 965 | $ok = false; 966 | } 967 | break; 968 | } 969 | if (!$ok) { 970 | return new IXR_Error(-32602, 'server error. invalid method parameters'); 971 | } 972 | } 973 | // It passed the test - run the "real" method call 974 | return parent::call($methodname, $argsbackup); 975 | } 976 | 977 | function methodSignature($method) 978 | { 979 | if (!$this->hasMethod($method)) { 980 | return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); 981 | } 982 | // We should be returning an array of types 983 | $types = $this->signatures[$method]; 984 | $return = array(); 985 | foreach ($types as $type) { 986 | switch ($type) { 987 | case 'string': 988 | $return[] = 'string'; 989 | break; 990 | case 'int': 991 | case 'i4': 992 | $return[] = 42; 993 | break; 994 | case 'double': 995 | $return[] = 3.1415; 996 | break; 997 | case 'dateTime.iso8601': 998 | $return[] = new IXR_Date(time()); 999 | break; 1000 | case 'boolean': 1001 | $return[] = true; 1002 | break; 1003 | case 'base64': 1004 | $return[] = new IXR_Base64('base64'); 1005 | break; 1006 | case 'array': 1007 | $return[] = array('array'); 1008 | break; 1009 | case 'struct': 1010 | $return[] = array('struct' => 'struct'); 1011 | break; 1012 | } 1013 | } 1014 | return $return; 1015 | } 1016 | 1017 | function methodHelp($method) 1018 | { 1019 | return $this->help[$method]; 1020 | } 1021 | } 1022 | 1023 | /** 1024 | * IXR_ClientMulticall 1025 | * 1026 | * @package IXR 1027 | * @since 1.5 1028 | */ 1029 | class IXR_ClientMulticall extends IXR_Client 1030 | { 1031 | var $calls = array(); 1032 | 1033 | function IXR_ClientMulticall($server, $path = false, $port = 80) 1034 | { 1035 | parent::IXR_Client($server, $path, $port); 1036 | $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; 1037 | } 1038 | 1039 | function addCall() 1040 | { 1041 | $args = func_get_args(); 1042 | $methodName = array_shift($args); 1043 | $struct = array( 1044 | 'methodName' => $methodName, 1045 | 'params' => $args 1046 | ); 1047 | $this->calls[] = $struct; 1048 | } 1049 | 1050 | function query() 1051 | { 1052 | // Prepare multicall, then call the parent::query() method 1053 | return parent::query('system.multicall', $this->calls); 1054 | } 1055 | } 1056 | 1057 | /** 1058 | * Client for communicating with a XML-RPC Server over HTTPS. 1059 | * 1060 | * @author Jason Stirk (@link http://blog.griffin.homelinux.org/projects/xmlrpc/) 1061 | * @version 0.2.0 26May2005 08:34 +0800 1062 | * @copyright (c) 2004-2005 Jason Stirk 1063 | * @package IXR 1064 | */ 1065 | class IXR_ClientSSL extends IXR_Client 1066 | { 1067 | /** 1068 | * Filename of the SSL Client Certificate 1069 | * @access private 1070 | * @since 0.1.0 1071 | * @var string 1072 | */ 1073 | var $_certFile; 1074 | 1075 | /** 1076 | * Filename of the SSL CA Certificate 1077 | * @access private 1078 | * @since 0.1.0 1079 | * @var string 1080 | */ 1081 | var $_caFile; 1082 | 1083 | /** 1084 | * Filename of the SSL Client Private Key 1085 | * @access private 1086 | * @since 0.1.0 1087 | * @var string 1088 | */ 1089 | var $_keyFile; 1090 | 1091 | /** 1092 | * Passphrase to unlock the private key 1093 | * @access private 1094 | * @since 0.1.0 1095 | * @var string 1096 | */ 1097 | var $_passphrase; 1098 | 1099 | /** 1100 | * Constructor 1101 | * @param string $server URL of the Server to connect to 1102 | * @since 0.1.0 1103 | */ 1104 | function IXR_ClientSSL($server, $path = false, $port = 443, $timeout = false) 1105 | { 1106 | parent::IXR_Client($server, $path, $port, $timeout); 1107 | $this->useragent = 'The Incutio XML-RPC PHP Library for SSL'; 1108 | 1109 | // Set class fields 1110 | $this->_certFile=false; 1111 | $this->_caFile=false; 1112 | $this->_keyFile=false; 1113 | $this->_passphrase=''; 1114 | } 1115 | 1116 | /** 1117 | * Set the client side certificates to communicate with the server. 1118 | * 1119 | * @since 0.1.0 1120 | * @param string $certificateFile Filename of the client side certificate to use 1121 | * @param string $keyFile Filename of the client side certificate's private key 1122 | * @param string $keyPhrase Passphrase to unlock the private key 1123 | */ 1124 | function setCertificate($certificateFile, $keyFile, $keyPhrase='') 1125 | { 1126 | // Check the files all exist 1127 | if (is_file($certificateFile)) { 1128 | $this->_certFile = $certificateFile; 1129 | } else { 1130 | die('Could not open certificate: ' . $certificateFile); 1131 | } 1132 | 1133 | if (is_file($keyFile)) { 1134 | $this->_keyFile = $keyFile; 1135 | } else { 1136 | die('Could not open private key: ' . $keyFile); 1137 | } 1138 | 1139 | $this->_passphrase=(string)$keyPhrase; 1140 | } 1141 | 1142 | function setCACertificate($caFile) 1143 | { 1144 | if (is_file($caFile)) { 1145 | $this->_caFile = $caFile; 1146 | } else { 1147 | die('Could not open CA certificate: ' . $caFile); 1148 | } 1149 | } 1150 | 1151 | /** 1152 | * Sets the connection timeout (in seconds) 1153 | * @param int $newTimeOut Timeout in seconds 1154 | * @returns void 1155 | * @since 0.1.2 1156 | */ 1157 | function setTimeOut($newTimeOut) 1158 | { 1159 | $this->timeout = (int)$newTimeOut; 1160 | } 1161 | 1162 | /** 1163 | * Returns the connection timeout (in seconds) 1164 | * @returns int 1165 | * @since 0.1.2 1166 | */ 1167 | function getTimeOut() 1168 | { 1169 | return $this->timeout; 1170 | } 1171 | 1172 | /** 1173 | * Set the query to send to the XML-RPC Server 1174 | * @since 0.1.0 1175 | */ 1176 | function query() 1177 | { 1178 | $args = func_get_args(); 1179 | $method = array_shift($args); 1180 | $request = new IXR_Request($method, $args); 1181 | $length = $request->getLength(); 1182 | $xml = $request->getXml(); 1183 | 1184 | if ($this->debug) { 1185 | echo '
'.htmlspecialchars($xml)."\n
\n\n"; 1186 | } 1187 | 1188 | //This is where we deviate from the normal query() 1189 | //Rather than open a normal sock, we will actually use the cURL 1190 | //extensions to make the calls, and handle the SSL stuff. 1191 | 1192 | //Since 04Aug2004 (0.1.3) - Need to include the port (duh...) 1193 | //Since 06Oct2004 (0.1.4) - Need to include the colon!!! 1194 | // (I swear I've fixed this before... ESP in live... But anyhu...) 1195 | $curl=curl_init('https://' . $this->server . ':' . $this->port . $this->path); 1196 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 1197 | 1198 | //Since 23Jun2004 (0.1.2) - Made timeout a class field 1199 | curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout); 1200 | 1201 | if ($this->debug) { 1202 | curl_setopt($curl, CURLOPT_VERBOSE, 1); 1203 | } 1204 | 1205 | curl_setopt($curl, CURLOPT_HEADER, 1); 1206 | curl_setopt($curl, CURLOPT_POST, 1); 1207 | curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); 1208 | curl_setopt($curl, CURLOPT_PORT, $this->port); 1209 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 1210 | "Content-Type: text/xml", 1211 | "Content-length: {$length}")); 1212 | 1213 | // Process the SSL certificates, etc. to use 1214 | if (!($this->_certFile === false)) { 1215 | // We have a certificate file set, so add these to the cURL handler 1216 | curl_setopt($curl, CURLOPT_SSLCERT, $this->_certFile); 1217 | curl_setopt($curl, CURLOPT_SSLKEY, $this->_keyFile); 1218 | 1219 | if ($this->debug) { 1220 | echo "SSL Cert at : " . $this->_certFile . "\n"; 1221 | echo "SSL Key at : " . $this->_keyFile . "\n"; 1222 | } 1223 | 1224 | // See if we need to give a passphrase 1225 | if (!($this->_passphrase === '')) { 1226 | curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $this->_passphrase); 1227 | } 1228 | 1229 | if ($this->_caFile === false) { 1230 | // Don't verify their certificate, as we don't have a CA to verify against 1231 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); 1232 | } else { 1233 | // Verify against a CA 1234 | curl_setopt($curl, CURLOPT_CAINFO, $this->_caFile); 1235 | } 1236 | } 1237 | 1238 | // Call cURL to do it's stuff and return us the content 1239 | $contents = curl_exec($curl); 1240 | curl_close($curl); 1241 | 1242 | // Check for 200 Code in $contents 1243 | if (!strstr($contents, '200 OK')) { 1244 | //There was no "200 OK" returned - we failed 1245 | $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); 1246 | return false; 1247 | } 1248 | 1249 | if ($this->debug) { 1250 | echo '
'.htmlspecialchars($contents)."\n
\n\n"; 1251 | } 1252 | // Now parse what we've got back 1253 | // Since 20Jun2004 (0.1.1) - We need to remove the headers first 1254 | // Why I have only just found this, I will never know... 1255 | // So, remove everything before the first < 1256 | $contents = substr($contents,strpos($contents, '<')); 1257 | 1258 | $this->message = new IXR_Message($contents); 1259 | if (!$this->message->parse()) { 1260 | // XML error 1261 | $this->error = new IXR_Error(-32700, 'parse error. not well formed'); 1262 | return false; 1263 | } 1264 | // Is the message a fault? 1265 | if ($this->message->messageType == 'fault') { 1266 | $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); 1267 | return false; 1268 | } 1269 | 1270 | // Message must be OK 1271 | return true; 1272 | } 1273 | } 1274 | 1275 | /** 1276 | * Extension of the {@link IXR_Server} class to easily wrap objects. 1277 | * 1278 | * Class is designed to extend the existing XML-RPC server to allow the 1279 | * presentation of methods from a variety of different objects via an 1280 | * XML-RPC server. 1281 | * It is intended to assist in organization of your XML-RPC methods by allowing 1282 | * you to "write once" in your existing model classes and present them. 1283 | * 1284 | * @author Jason Stirk 1285 | * @version 1.0.1 19Apr2005 17:40 +0800 1286 | * @copyright Copyright (c) 2005 Jason Stirk 1287 | * @package IXR 1288 | */ 1289 | class IXR_ClassServer extends IXR_Server 1290 | { 1291 | var $_objects; 1292 | var $_delim; 1293 | 1294 | function IXR_ClassServer($delim = '.', $wait = false) 1295 | { 1296 | $this->IXR_Server(array(), false, $wait); 1297 | $this->_delimiter = $delim; 1298 | $this->_objects = array(); 1299 | } 1300 | 1301 | function addMethod($rpcName, $functionName) 1302 | { 1303 | $this->callbacks[$rpcName] = $functionName; 1304 | } 1305 | 1306 | function registerObject($object, $methods, $prefix=null) 1307 | { 1308 | if (is_null($prefix)) 1309 | { 1310 | $prefix = get_class($object); 1311 | } 1312 | $this->_objects[$prefix] = $object; 1313 | 1314 | // Add to our callbacks array 1315 | foreach($methods as $method) 1316 | { 1317 | if (is_array($method)) 1318 | { 1319 | $targetMethod = $method[0]; 1320 | $method = $method[1]; 1321 | } 1322 | else 1323 | { 1324 | $targetMethod = $method; 1325 | } 1326 | $this->callbacks[$prefix . $this->_delimiter . $method]=array($prefix, $targetMethod); 1327 | } 1328 | } 1329 | 1330 | function call($methodname, $args) 1331 | { 1332 | if (!$this->hasMethod($methodname)) { 1333 | return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); 1334 | } 1335 | $method = $this->callbacks[$methodname]; 1336 | 1337 | // Perform the callback and send the response 1338 | if (count($args) == 1) { 1339 | // If only one paramater just send that instead of the whole array 1340 | $args = $args[0]; 1341 | } 1342 | 1343 | // See if this method comes from one of our objects or maybe self 1344 | if (is_array($method) || (substr($method, 0, 5) == 'this:')) { 1345 | if (is_array($method)) { 1346 | $object=$this->_objects[$method[0]]; 1347 | $method=$method[1]; 1348 | } else { 1349 | $object=$this; 1350 | $method = substr($method, 5); 1351 | } 1352 | 1353 | // It's a class method - check it exists 1354 | if (!method_exists($object, $method)) { 1355 | return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); 1356 | } 1357 | 1358 | // Call the method 1359 | $result = $object->$method($args); 1360 | } else { 1361 | // It's a function - does it exist? 1362 | if (!function_exists($method)) { 1363 | return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); 1364 | } 1365 | 1366 | // Call the function 1367 | $result = $method($args); 1368 | } 1369 | return $result; 1370 | } 1371 | } 1372 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP XML-RPC URL Ping 2 | ==================== 3 | 4 | XML-RPC based Ping tool. 5 | 6 | More details coming soon! -------------------------------------------------------------------------------- /ping.php: -------------------------------------------------------------------------------- 1 | timeout = 3; 97 | $client->useragent .= ' -- PingTool/1.0.0'; 98 | $client->debug = false; 99 | 100 | if( $client->query( 'weblogUpdates.extendedPing', $myBlogName, $myBlogUrl, $myBlogUpdateUrl, $myBlogRSSFeedUrl ) ) 101 | { 102 | return $client->getResponse(); 103 | } 104 | 105 | echo 'Failed extended XML-RPC ping for "' . $url . '": ' . $client->getErrorCode() . '->' . $client->getErrorMessage() . '
'; 106 | 107 | if( $client->query( 'weblogUpdates.ping', $myBlogName, $myBlogUrl ) ) 108 | { 109 | return $client->getResponse(); 110 | } 111 | 112 | echo 'Failed basic XML-RPC ping for "' . $url . '": ' . $client->getErrorCode() . '->' . $client->getErrorMessage() . '
'; 113 | 114 | return false; 115 | } 116 | 117 | foreach( $xmlRpcPingUrls as $url ) 118 | { 119 | echo '

XML-RPC pinging ' . $url . '

'; 120 | echo '
';
121 |     print_r( xmlRpcPing( $url ) );
122 |     echo '
'; 123 | ob_flush(); 124 | flush(); 125 | } --------------------------------------------------------------------------------