1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * PHP implementation of the XML-RPC protocol 7 * 8 * This is a PEAR-ified version of Useful inc's XML-RPC for PHP. 9 * It has support for HTTP transport, proxies and authentication. 10 * 11 * PHP versions 4 and 5 12 * 13 * @category Web Services 14 * @package XML_RPC 15 * @author Edd Dumbill <edd@usefulinc.com> 16 * @author Stig Bakken <stig@php.net> 17 * @author Martin Jansen <mj@php.net> 18 * @author Daniel Convissor <danielc@php.net> 19 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 20 * @license http://www.php.net/license/3_01.txt PHP License 21 * @version SVN: $Id: RPC.php 315594 2011-08-27 01:03:57Z danielc $ 22 * @link http://pear.php.net/package/XML_RPC 23 */ 24 25 26if (!function_exists('xml_parser_create')) { 27 include_once 'PEAR.php'; 28 PEAR::loadExtension('xml'); 29} 30 31/**#@+ 32 * Error constants 33 */ 34/** 35 * Parameter values don't match parameter types 36 */ 37define('XML_RPC_ERROR_INVALID_TYPE', 101); 38/** 39 * Parameter declared to be numeric but the values are not 40 */ 41define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102); 42/** 43 * Communication error 44 */ 45define('XML_RPC_ERROR_CONNECTION_FAILED', 103); 46/** 47 * The array or struct has already been started 48 */ 49define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104); 50/** 51 * Incorrect parameters submitted 52 */ 53define('XML_RPC_ERROR_INCORRECT_PARAMS', 105); 54/** 55 * Programming error by developer 56 */ 57define('XML_RPC_ERROR_PROGRAMMING', 106); 58/**#@-*/ 59 60 61/** 62 * Data types 63 * @global string $GLOBALS['XML_RPC_I4'] 64 */ 65$GLOBALS['XML_RPC_I4'] = 'i4'; 66 67/** 68 * Data types 69 * @global string $GLOBALS['XML_RPC_Int'] 70 */ 71$GLOBALS['XML_RPC_Int'] = 'int'; 72 73/** 74 * Data types 75 * @global string $GLOBALS['XML_RPC_Boolean'] 76 */ 77$GLOBALS['XML_RPC_Boolean'] = 'boolean'; 78 79/** 80 * Data types 81 * @global string $GLOBALS['XML_RPC_Double'] 82 */ 83$GLOBALS['XML_RPC_Double'] = 'double'; 84 85/** 86 * Data types 87 * @global string $GLOBALS['XML_RPC_String'] 88 */ 89$GLOBALS['XML_RPC_String'] = 'string'; 90 91/** 92 * Data types 93 * @global string $GLOBALS['XML_RPC_DateTime'] 94 */ 95$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601'; 96 97/** 98 * Data types 99 * @global string $GLOBALS['XML_RPC_Base64'] 100 */ 101$GLOBALS['XML_RPC_Base64'] = 'base64'; 102 103/** 104 * Data types 105 * @global string $GLOBALS['XML_RPC_Array'] 106 */ 107$GLOBALS['XML_RPC_Array'] = 'array'; 108 109/** 110 * Data types 111 * @global string $GLOBALS['XML_RPC_Struct'] 112 */ 113$GLOBALS['XML_RPC_Struct'] = 'struct'; 114 115 116/** 117 * Data type meta-types 118 * @global array $GLOBALS['XML_RPC_Types'] 119 */ 120$GLOBALS['XML_RPC_Types'] = array( 121 $GLOBALS['XML_RPC_I4'] => 1, 122 $GLOBALS['XML_RPC_Int'] => 1, 123 $GLOBALS['XML_RPC_Boolean'] => 1, 124 $GLOBALS['XML_RPC_String'] => 1, 125 $GLOBALS['XML_RPC_Double'] => 1, 126 $GLOBALS['XML_RPC_DateTime'] => 1, 127 $GLOBALS['XML_RPC_Base64'] => 1, 128 $GLOBALS['XML_RPC_Array'] => 2, 129 $GLOBALS['XML_RPC_Struct'] => 3, 130); 131 132 133/** 134 * Error message numbers 135 * @global array $GLOBALS['XML_RPC_err'] 136 */ 137$GLOBALS['XML_RPC_err'] = array( 138 'unknown_method' => 1, 139 'invalid_return' => 2, 140 'incorrect_params' => 3, 141 'introspect_unknown' => 4, 142 'http_error' => 5, 143 'not_response_object' => 6, 144 'invalid_request' => 7, 145); 146 147/** 148 * Error message strings 149 * @global array $GLOBALS['XML_RPC_str'] 150 */ 151$GLOBALS['XML_RPC_str'] = array( 152 'unknown_method' => 'Unknown method', 153 'invalid_return' => 'Invalid return payload: enable debugging to examine incoming payload', 154 'incorrect_params' => 'Incorrect parameters passed to method', 155 'introspect_unknown' => 'Can\'t introspect: method unknown', 156 'http_error' => 'Didn\'t receive 200 OK from remote server.', 157 'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.', 158 'invalid_request' => 'Invalid request payload', 159); 160 161 162/** 163 * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII) 164 * @global string $GLOBALS['XML_RPC_defencoding'] 165 */ 166$GLOBALS['XML_RPC_defencoding'] = 'UTF-8'; 167 168/** 169 * User error codes start at 800 170 * @global int $GLOBALS['XML_RPC_erruser'] 171 */ 172$GLOBALS['XML_RPC_erruser'] = 800; 173 174/** 175 * XML parse error codes start at 100 176 * @global int $GLOBALS['XML_RPC_errxml'] 177 */ 178$GLOBALS['XML_RPC_errxml'] = 100; 179 180 181/** 182 * Compose backslashes for escaping regexp 183 * @global string $GLOBALS['XML_RPC_backslash'] 184 */ 185$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92); 186 187 188/** 189 * Should we automatically base64 encode strings that contain characters 190 * which can cause PHP's SAX-based XML parser to break? 191 * @global boolean $GLOBALS['XML_RPC_auto_base64'] 192 */ 193$GLOBALS['XML_RPC_auto_base64'] = false; 194 195 196/** 197 * Valid parents of XML elements 198 * @global array $GLOBALS['XML_RPC_valid_parents'] 199 */ 200$GLOBALS['XML_RPC_valid_parents'] = array( 201 'BOOLEAN' => array('VALUE'), 202 'I4' => array('VALUE'), 203 'INT' => array('VALUE'), 204 'STRING' => array('VALUE'), 205 'DOUBLE' => array('VALUE'), 206 'DATETIME.ISO8601' => array('VALUE'), 207 'BASE64' => array('VALUE'), 208 'ARRAY' => array('VALUE'), 209 'STRUCT' => array('VALUE'), 210 'PARAM' => array('PARAMS'), 211 'METHODNAME' => array('METHODCALL'), 212 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 213 'MEMBER' => array('STRUCT'), 214 'NAME' => array('MEMBER'), 215 'DATA' => array('ARRAY'), 216 'FAULT' => array('METHODRESPONSE'), 217 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'), 218); 219 220 221/** 222 * Stores state during parsing 223 * 224 * quick explanation of components: 225 * + ac = accumulates values 226 * + qt = decides if quotes are needed for evaluation 227 * + cm = denotes struct or array (comma needed) 228 * + isf = indicates a fault 229 * + lv = indicates "looking for a value": implements the logic 230 * to allow values with no types to be strings 231 * + params = stores parameters in method calls 232 * + method = stores method name 233 * 234 * @global array $GLOBALS['XML_RPC_xh'] 235 */ 236$GLOBALS['XML_RPC_xh'] = array(); 237 238 239/** 240 * Start element handler for the XML parser 241 * 242 * @return void 243 */ 244function XML_RPC_se($parser_resource, $name, $attrs) 245{ 246 global $XML_RPC_xh, $XML_RPC_valid_parents; 247 248 $parser = (int) $parser_resource; 249 250 // if invalid xmlrpc already detected, skip all processing 251 if ($XML_RPC_xh[$parser]['isf'] >= 2) { 252 return; 253 } 254 255 // check for correct element nesting 256 // top level element can only be of 2 types 257 if (count($XML_RPC_xh[$parser]['stack']) == 0) { 258 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') { 259 $XML_RPC_xh[$parser]['isf'] = 2; 260 $XML_RPC_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element'; 261 return; 262 } 263 } else { 264 // not top level element: see if parent is OK 265 if (!in_array($XML_RPC_xh[$parser]['stack'][0], $XML_RPC_valid_parents[$name])) { 266 $name = preg_replace('@[^a-zA-Z0-9._-]@', '', $name); 267 $XML_RPC_xh[$parser]['isf'] = 2; 268 $XML_RPC_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$XML_RPC_xh[$parser]['stack'][0]}"; 269 return; 270 } 271 } 272 273 switch ($name) { 274 case 'STRUCT': 275 $XML_RPC_xh[$parser]['cm']++; 276 277 // turn quoting off 278 $XML_RPC_xh[$parser]['qt'] = 0; 279 280 $cur_val = array(); 281 $cur_val['value'] = array(); 282 $cur_val['members'] = 1; 283 array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); 284 break; 285 286 case 'ARRAY': 287 $XML_RPC_xh[$parser]['cm']++; 288 289 // turn quoting off 290 $XML_RPC_xh[$parser]['qt'] = 0; 291 292 $cur_val = array(); 293 $cur_val['value'] = array(); 294 $cur_val['members'] = 0; 295 array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); 296 break; 297 298 case 'NAME': 299 $XML_RPC_xh[$parser]['ac'] = ''; 300 break; 301 302 case 'FAULT': 303 $XML_RPC_xh[$parser]['isf'] = 1; 304 break; 305 306 case 'PARAM': 307 $XML_RPC_xh[$parser]['valuestack'] = array(); 308 break; 309 310 case 'VALUE': 311 $XML_RPC_xh[$parser]['lv'] = 1; 312 $XML_RPC_xh[$parser]['vt'] = $GLOBALS['XML_RPC_String']; 313 $XML_RPC_xh[$parser]['ac'] = ''; 314 $XML_RPC_xh[$parser]['qt'] = 0; 315 // look for a value: if this is still 1 by the 316 // time we reach the first data segment then the type is string 317 // by implication and we need to add in a quote 318 break; 319 320 case 'I4': 321 case 'INT': 322 case 'STRING': 323 case 'BOOLEAN': 324 case 'DOUBLE': 325 case 'DATETIME.ISO8601': 326 case 'BASE64': 327 $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator 328 329 if ($name == 'DATETIME.ISO8601' || $name == 'STRING') { 330 $XML_RPC_xh[$parser]['qt'] = 1; 331 332 if ($name == 'DATETIME.ISO8601') { 333 $XML_RPC_xh[$parser]['vt'] = $GLOBALS['XML_RPC_DateTime']; 334 } 335 336 } elseif ($name == 'BASE64') { 337 $XML_RPC_xh[$parser]['qt'] = 2; 338 } else { 339 // No quoting is required here -- but 340 // at the end of the element we must check 341 // for data format errors. 342 $XML_RPC_xh[$parser]['qt'] = 0; 343 } 344 break; 345 346 case 'MEMBER': 347 $XML_RPC_xh[$parser]['ac'] = ''; 348 break; 349 350 case 'DATA': 351 case 'METHODCALL': 352 case 'METHODNAME': 353 case 'METHODRESPONSE': 354 case 'PARAMS': 355 // valid elements that add little to processing 356 break; 357 } 358 359 360 // Save current element to stack 361 array_unshift($XML_RPC_xh[$parser]['stack'], $name); 362 363 if ($name != 'VALUE') { 364 $XML_RPC_xh[$parser]['lv'] = 0; 365 } 366} 367 368/** 369 * End element handler for the XML parser 370 * 371 * @return void 372 */ 373function XML_RPC_ee($parser_resource, $name) 374{ 375 global $XML_RPC_xh; 376 377 $parser = (int) $parser_resource; 378 379 if ($XML_RPC_xh[$parser]['isf'] >= 2) { 380 return; 381 } 382 383 // push this element from stack 384 // NB: if XML validates, correct opening/closing is guaranteed and 385 // we do not have to check for $name == $curr_elem. 386 // we also checked for proper nesting at start of elements... 387 $curr_elem = array_shift($XML_RPC_xh[$parser]['stack']); 388 389 switch ($name) { 390 case 'STRUCT': 391 case 'ARRAY': 392 $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); 393 $XML_RPC_xh[$parser]['value'] = $cur_val['value']; 394 $XML_RPC_xh[$parser]['vt'] = strtolower($name); 395 $XML_RPC_xh[$parser]['cm']--; 396 break; 397 398 case 'NAME': 399 $XML_RPC_xh[$parser]['valuestack'][0]['name'] = $XML_RPC_xh[$parser]['ac']; 400 break; 401 402 case 'BOOLEAN': 403 // special case here: we translate boolean 1 or 0 into PHP 404 // constants true or false 405 if ($XML_RPC_xh[$parser]['ac'] == '1') { 406 $XML_RPC_xh[$parser]['ac'] = 'true'; 407 } else { 408 $XML_RPC_xh[$parser]['ac'] = 'false'; 409 } 410 411 $XML_RPC_xh[$parser]['vt'] = strtolower($name); 412 // Drop through intentionally. 413 414 case 'I4': 415 case 'INT': 416 case 'STRING': 417 case 'DOUBLE': 418 case 'DATETIME.ISO8601': 419 case 'BASE64': 420 if ($XML_RPC_xh[$parser]['qt'] == 1) { 421 // we use double quotes rather than single so backslashification works OK 422 $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; 423 } elseif ($XML_RPC_xh[$parser]['qt'] == 2) { 424 $XML_RPC_xh[$parser]['value'] = base64_decode($XML_RPC_xh[$parser]['ac']); 425 } elseif ($name == 'BOOLEAN') { 426 $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; 427 } else { 428 // we have an I4, INT or a DOUBLE 429 // we must check that only 0123456789-.<space> are characters here 430 if (!preg_match("@^[+-]?[0123456789 \t\.]+$@", $XML_RPC_xh[$parser]['ac'])) { 431 XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE', 432 XML_RPC_ERROR_NON_NUMERIC_FOUND); 433 $XML_RPC_xh[$parser]['value'] = XML_RPC_ERROR_NON_NUMERIC_FOUND; 434 } else { 435 // it's ok, add it on 436 $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; 437 } 438 } 439 440 $XML_RPC_xh[$parser]['ac'] = ''; 441 $XML_RPC_xh[$parser]['qt'] = 0; 442 $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value 443 break; 444 445 case 'VALUE': 446 if ($XML_RPC_xh[$parser]['vt'] == $GLOBALS['XML_RPC_String']) { 447 if (strlen($XML_RPC_xh[$parser]['ac']) > 0) { 448 $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac']; 449 } elseif ($XML_RPC_xh[$parser]['lv'] == 1) { 450 // The <value> element was empty. 451 $XML_RPC_xh[$parser]['value'] = ''; 452 } 453 } 454 455 $temp = new XML_RPC_Value($XML_RPC_xh[$parser]['value'], $XML_RPC_xh[$parser]['vt']); 456 457 $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); 458 if (is_array($cur_val)) { 459 if ($cur_val['members']==0) { 460 $cur_val['value'][] = $temp; 461 } else { 462 $XML_RPC_xh[$parser]['value'] = $temp; 463 } 464 array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); 465 } else { 466 $XML_RPC_xh[$parser]['value'] = $temp; 467 } 468 break; 469 470 case 'MEMBER': 471 $XML_RPC_xh[$parser]['ac'] = ''; 472 $XML_RPC_xh[$parser]['qt'] = 0; 473 474 $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']); 475 if (is_array($cur_val)) { 476 if ($cur_val['members']==1) { 477 $cur_val['value'][$cur_val['name']] = $XML_RPC_xh[$parser]['value']; 478 } 479 array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val); 480 } 481 break; 482 483 case 'DATA': 484 $XML_RPC_xh[$parser]['ac'] = ''; 485 $XML_RPC_xh[$parser]['qt'] = 0; 486 break; 487 488 case 'PARAM': 489 $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['value']; 490 break; 491 492 case 'METHODNAME': 493 case 'RPCMETHODNAME': 494 $XML_RPC_xh[$parser]['method'] = preg_replace("@^[\n\r\t ]+@", '', 495 $XML_RPC_xh[$parser]['ac']); 496 break; 497 } 498 499 // if it's a valid type name, set the type 500 if (isset($GLOBALS['XML_RPC_Types'][strtolower($name)])) { 501 $XML_RPC_xh[$parser]['vt'] = strtolower($name); 502 } 503} 504 505/** 506 * Character data handler for the XML parser 507 * 508 * @return void 509 */ 510function XML_RPC_cd($parser_resource, $data) 511{ 512 global $XML_RPC_xh, $XML_RPC_backslash; 513 514 $parser = (int) $parser_resource; 515 516 if ($XML_RPC_xh[$parser]['lv'] != 3) { 517 // "lookforvalue==3" means that we've found an entire value 518 // and should discard any further character data 519 520 if ($XML_RPC_xh[$parser]['lv'] == 1) { 521 // if we've found text and we're just in a <value> then 522 // turn quoting on, as this will be a string 523 $XML_RPC_xh[$parser]['qt'] = 1; 524 // and say we've found a value 525 $XML_RPC_xh[$parser]['lv'] = 2; 526 } 527 528 // replace characters that eval would 529 // do special things with 530 if (!isset($XML_RPC_xh[$parser]['ac'])) { 531 $XML_RPC_xh[$parser]['ac'] = ''; 532 } 533 $XML_RPC_xh[$parser]['ac'] .= $data; 534 } 535} 536 537/** 538 * The common methods and properties for all of the XML_RPC classes 539 * 540 * @category Web Services 541 * @package XML_RPC 542 * @author Edd Dumbill <edd@usefulinc.com> 543 * @author Stig Bakken <stig@php.net> 544 * @author Martin Jansen <mj@php.net> 545 * @author Daniel Convissor <danielc@php.net> 546 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 547 * @license http://www.php.net/license/3_01.txt PHP License 548 * @version Release: @package_version@ 549 * @link http://pear.php.net/package/XML_RPC 550 */ 551class XML_RPC_Base { 552 553 /** 554 * PEAR Error handling 555 * 556 * @return object PEAR_Error object 557 */ 558 function raiseError($msg, $code) 559 { 560 include_once 'PEAR.php'; 561 if (is_object(@$this)) { 562 return PEAR::raiseError(get_class($this) . ': ' . $msg, $code); 563 } else { 564 return PEAR::raiseError('XML_RPC: ' . $msg, $code); 565 } 566 } 567 568 /** 569 * Tell whether something is a PEAR_Error object 570 * 571 * @param mixed $value the item to check 572 * 573 * @return bool whether $value is a PEAR_Error object or not 574 * 575 * @access public 576 */ 577 function isError($value) 578 { 579 return is_object($value) && is_a($value, 'PEAR_Error'); 580 } 581} 582 583/** 584 * The methods and properties for submitting XML RPC requests 585 * 586 * @category Web Services 587 * @package XML_RPC 588 * @author Edd Dumbill <edd@usefulinc.com> 589 * @author Stig Bakken <stig@php.net> 590 * @author Martin Jansen <mj@php.net> 591 * @author Daniel Convissor <danielc@php.net> 592 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 593 * @license http://www.php.net/license/3_01.txt PHP License 594 * @version Release: @package_version@ 595 * @link http://pear.php.net/package/XML_RPC 596 */ 597class XML_RPC_Client extends XML_RPC_Base { 598 599 /** 600 * The path and name of the RPC server script you want the request to go to 601 * @var string 602 */ 603 var $path = ''; 604 605 /** 606 * The name of the remote server to connect to 607 * @var string 608 */ 609 var $server = ''; 610 611 /** 612 * The protocol to use in contacting the remote server 613 * @var string 614 */ 615 var $protocol = 'http://'; 616 617 /** 618 * The port for connecting to the remote server 619 * 620 * The default is 80 for http:// connections 621 * and 443 for https:// and ssl:// connections. 622 * 623 * @var integer 624 */ 625 var $port = 80; 626 627 /** 628 * A user name for accessing the RPC server 629 * @var string 630 * @see XML_RPC_Client::setCredentials() 631 */ 632 var $username = ''; 633 634 /** 635 * A password for accessing the RPC server 636 * @var string 637 * @see XML_RPC_Client::setCredentials() 638 */ 639 var $password = ''; 640 641 /** 642 * The name of the proxy server to use, if any 643 * @var string 644 */ 645 var $proxy = ''; 646 647 /** 648 * The protocol to use in contacting the proxy server, if any 649 * @var string 650 */ 651 var $proxy_protocol = 'http://'; 652 653 /** 654 * The port for connecting to the proxy server 655 * 656 * The default is 8080 for http:// connections 657 * and 443 for https:// and ssl:// connections. 658 * 659 * @var integer 660 */ 661 var $proxy_port = 8080; 662 663 /** 664 * A user name for accessing the proxy server 665 * @var string 666 */ 667 var $proxy_user = ''; 668 669 /** 670 * A password for accessing the proxy server 671 * @var string 672 */ 673 var $proxy_pass = ''; 674 675 /** 676 * The error number, if any 677 * @var integer 678 */ 679 var $errno = 0; 680 681 /** 682 * The error message, if any 683 * @var string 684 */ 685 var $errstr = ''; 686 687 /** 688 * The current debug mode (1 = on, 0 = off) 689 * @var integer 690 */ 691 var $debug = 0; 692 693 /** 694 * The HTTP headers for the current request. 695 * @var string 696 */ 697 var $headers = ''; 698 699 700 /** 701 * Sets the object's properties 702 * 703 * @param string $path the path and name of the RPC server script 704 * you want the request to go to 705 * @param string $server the URL of the remote server to connect to. 706 * If this parameter doesn't specify a 707 * protocol and $port is 443, ssl:// is 708 * assumed. 709 * @param integer $port a port for connecting to the remote server. 710 * Defaults to 80 for http:// connections and 711 * 443 for https:// and ssl:// connections. 712 * @param string $proxy the URL of the proxy server to use, if any. 713 * If this parameter doesn't specify a 714 * protocol and $port is 443, ssl:// is 715 * assumed. 716 * @param integer $proxy_port a port for connecting to the remote server. 717 * Defaults to 8080 for http:// connections and 718 * 443 for https:// and ssl:// connections. 719 * @param string $proxy_user a user name for accessing the proxy server 720 * @param string $proxy_pass a password for accessing the proxy server 721 * 722 * @return void 723 */ 724 function XML_RPC_Client($path, $server, $port = 0, 725 $proxy = '', $proxy_port = 0, 726 $proxy_user = '', $proxy_pass = '') 727 { 728 $this->path = $path; 729 $this->proxy_user = $proxy_user; 730 $this->proxy_pass = $proxy_pass; 731 732 preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match); 733 if ($match[1] == '') { 734 if ($port == 443) { 735 $this->server = $match[2]; 736 $this->protocol = 'ssl://'; 737 $this->port = 443; 738 } else { 739 $this->server = $match[2]; 740 if ($port) { 741 $this->port = $port; 742 } 743 } 744 } elseif ($match[1] == 'http://') { 745 $this->server = $match[2]; 746 if ($port) { 747 $this->port = $port; 748 } 749 } else { 750 $this->server = $match[2]; 751 $this->protocol = 'ssl://'; 752 if ($port) { 753 $this->port = $port; 754 } else { 755 $this->port = 443; 756 } 757 } 758 759 if ($proxy) { 760 preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match); 761 if ($match[1] == '') { 762 if ($proxy_port == 443) { 763 $this->proxy = $match[2]; 764 $this->proxy_protocol = 'ssl://'; 765 $this->proxy_port = 443; 766 } else { 767 $this->proxy = $match[2]; 768 if ($proxy_port) { 769 $this->proxy_port = $proxy_port; 770 } 771 } 772 } elseif ($match[1] == 'http://') { 773 $this->proxy = $match[2]; 774 if ($proxy_port) { 775 $this->proxy_port = $proxy_port; 776 } 777 } else { 778 $this->proxy = $match[2]; 779 $this->proxy_protocol = 'ssl://'; 780 if ($proxy_port) { 781 $this->proxy_port = $proxy_port; 782 } else { 783 $this->proxy_port = 443; 784 } 785 } 786 } 787 } 788 789 /** 790 * Change the current debug mode 791 * 792 * @param int $in where 1 = on, 0 = off 793 * 794 * @return void 795 */ 796 function setDebug($in) 797 { 798 if ($in) { 799 $this->debug = 1; 800 } else { 801 $this->debug = 0; 802 } 803 } 804 805 /** 806 * Sets whether strings that contain characters which may cause PHP's 807 * SAX-based XML parser to break should be automatically base64 encoded 808 * 809 * This is is a workaround for systems that don't have PHP's mbstring 810 * extension available. 811 * 812 * @param int $in where 1 = on, 0 = off 813 * 814 * @return void 815 */ 816 function setAutoBase64($in) 817 { 818 if ($in) { 819 $GLOBALS['XML_RPC_auto_base64'] = true; 820 } else { 821 $GLOBALS['XML_RPC_auto_base64'] = false; 822 } 823 } 824 825 /** 826 * Set username and password properties for connecting to the RPC server 827 * 828 * @param string $u the user name 829 * @param string $p the password 830 * 831 * @return void 832 * 833 * @see XML_RPC_Client::$username, XML_RPC_Client::$password 834 */ 835 function setCredentials($u, $p) 836 { 837 $this->username = $u; 838 $this->password = $p; 839 } 840 841 /** 842 * Transmit the RPC request via HTTP 1.0 protocol 843 * 844 * @param object $msg the XML_RPC_Message object 845 * @param int $timeout how many seconds to wait for the request 846 * 847 * @return object an XML_RPC_Response object. 0 is returned if any 848 * problems happen. 849 * 850 * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(), 851 * XML_RPC_Client::setCredentials() 852 */ 853 function send($msg, $timeout = 0) 854 { 855 if (!is_object($msg) || !is_a($msg, 'XML_RPC_Message')) { 856 $this->errstr = 'send()\'s $msg parameter must be an' 857 . ' XML_RPC_Message object.'; 858 $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING); 859 return 0; 860 } 861 $msg->debug = $this->debug; 862 return $this->sendPayloadHTTP10($msg, $this->server, $this->port, 863 $timeout, $this->username, 864 $this->password); 865 } 866 867 /** 868 * Transmit the RPC request via HTTP 1.0 protocol 869 * 870 * Requests should be sent using XML_RPC_Client send() rather than 871 * calling this method directly. 872 * 873 * @param object $msg the XML_RPC_Message object 874 * @param string $server the server to send the request to 875 * @param int $port the server port send the request to 876 * @param int $timeout how many seconds to wait for the request 877 * before giving up 878 * @param string $username a user name for accessing the RPC server 879 * @param string $password a password for accessing the RPC server 880 * 881 * @return object an XML_RPC_Response object. 0 is returned if any 882 * problems happen. 883 * 884 * @access protected 885 * @see XML_RPC_Client::send() 886 */ 887 function sendPayloadHTTP10($msg, $server, $port, $timeout = 0, 888 $username = '', $password = '') 889 { 890 // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly 891 if ($username != $this->username) { 892 $this->setCredentials($username, $password); 893 } 894 895 // Only create the payload if it was not created previously 896 if (empty($msg->payload)) { 897 $msg->createPayload(); 898 } 899 $this->createHeaders($msg); 900 901 $op = $this->headers . "\r\n\r\n"; 902 $op .= $msg->payload; 903 904 if ($this->debug) { 905 print "\n<pre>---SENT---\n"; 906 print $op; 907 print "\n---END---</pre>\n"; 908 } 909 910 /* 911 * If we're using a proxy open a socket to the proxy server 912 * instead to the xml-rpc server 913 */ 914 if ($this->proxy) { 915 if ($this->proxy_protocol == 'http://') { 916 $protocol = ''; 917 } else { 918 $protocol = $this->proxy_protocol; 919 } 920 if ($timeout > 0) { 921 $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port, 922 $this->errno, $this->errstr, $timeout); 923 } else { 924 $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port, 925 $this->errno, $this->errstr); 926 } 927 } else { 928 if ($this->protocol == 'http://') { 929 $protocol = ''; 930 } else { 931 $protocol = $this->protocol; 932 } 933 if ($timeout > 0) { 934 $fp = @fsockopen($protocol . $server, $port, 935 $this->errno, $this->errstr, $timeout); 936 } else { 937 $fp = @fsockopen($protocol . $server, $port, 938 $this->errno, $this->errstr); 939 } 940 } 941 942 /* 943 * Just raising the error without returning it is strange, 944 * but keep it here for backwards compatibility. 945 */ 946 if (!$fp && $this->proxy) { 947 $this->raiseError('Connection to proxy server ' 948 . $this->proxy . ':' . $this->proxy_port 949 . ' failed. ' . $this->errstr, 950 XML_RPC_ERROR_CONNECTION_FAILED); 951 return 0; 952 } elseif (!$fp) { 953 $this->raiseError('Connection to RPC server ' 954 . $server . ':' . $port 955 . ' failed. ' . $this->errstr, 956 XML_RPC_ERROR_CONNECTION_FAILED); 957 return 0; 958 } 959 960 if ($timeout) { 961 /* 962 * Using socket_set_timeout() because stream_set_timeout() 963 * was introduced in 4.3.0, but we need to support 4.2.0. 964 */ 965 socket_set_timeout($fp, $timeout); 966 } 967 968 if (!fputs($fp, $op, strlen($op))) { 969 $this->errstr = 'Write error'; 970 return 0; 971 } 972 $resp = $msg->parseResponseFile($fp); 973 974 $meta = socket_get_status($fp); 975 if ($meta['timed_out']) { 976 fclose($fp); 977 $this->errstr = 'RPC server did not send response before timeout.'; 978 $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED); 979 return 0; 980 } 981 982 fclose($fp); 983 return $resp; 984 } 985 986 /** 987 * Determines the HTTP headers and puts it in the $headers property 988 * 989 * @param object $msg the XML_RPC_Message object 990 * 991 * @return boolean TRUE if okay, FALSE if the message payload isn't set. 992 * 993 * @access protected 994 */ 995 function createHeaders($msg) 996 { 997 if (empty($msg->payload)) { 998 return false; 999 } 1000 if ($this->proxy) { 1001 $this->headers = 'POST ' . $this->protocol . $this->server; 1002 if ($this->proxy_port) { 1003 $this->headers .= ':' . $this->port; 1004 } 1005 } else { 1006 $this->headers = 'POST '; 1007 } 1008 $this->headers .= $this->path. " HTTP/1.0\r\n"; 1009 1010 $this->headers .= "User-Agent: PEAR XML_RPC\r\n"; 1011 $this->headers .= 'Host: ' . $this->server . "\r\n"; 1012 1013 if ($this->proxy && $this->proxy_user) { 1014 $this->headers .= 'Proxy-Authorization: Basic ' 1015 . base64_encode("$this->proxy_user:$this->proxy_pass") 1016 . "\r\n"; 1017 } 1018 1019 // thanks to Grant Rauscher <grant7@firstworld.net> for this 1020 if ($this->username) { 1021 $this->headers .= 'Authorization: Basic ' 1022 . base64_encode("$this->username:$this->password") 1023 . "\r\n"; 1024 } 1025 1026 $this->headers .= "Content-Type: text/xml\r\n"; 1027 $this->headers .= 'Content-Length: ' . strlen($msg->payload); 1028 return true; 1029 } 1030} 1031 1032/** 1033 * The methods and properties for interpreting responses to XML RPC requests 1034 * 1035 * @category Web Services 1036 * @package XML_RPC 1037 * @author Edd Dumbill <edd@usefulinc.com> 1038 * @author Stig Bakken <stig@php.net> 1039 * @author Martin Jansen <mj@php.net> 1040 * @author Daniel Convissor <danielc@php.net> 1041 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 1042 * @license http://www.php.net/license/3_01.txt PHP License 1043 * @version Release: @package_version@ 1044 * @link http://pear.php.net/package/XML_RPC 1045 */ 1046class XML_RPC_Response extends XML_RPC_Base 1047{ 1048 var $xv; 1049 var $fn; 1050 var $fs; 1051 var $hdrs; 1052 1053 /** 1054 * @return void 1055 */ 1056 function XML_RPC_Response($val, $fcode = 0, $fstr = '') 1057 { 1058 if ($fcode != 0) { 1059 $this->fn = $fcode; 1060 $this->fs = htmlspecialchars($fstr); 1061 } else { 1062 $this->xv = $val; 1063 } 1064 } 1065 1066 /** 1067 * @return int the error code 1068 */ 1069 function faultCode() 1070 { 1071 if (isset($this->fn)) { 1072 return $this->fn; 1073 } else { 1074 return 0; 1075 } 1076 } 1077 1078 /** 1079 * @return string the error string 1080 */ 1081 function faultString() 1082 { 1083 return $this->fs; 1084 } 1085 1086 /** 1087 * @return mixed the value 1088 */ 1089 function value() 1090 { 1091 return $this->xv; 1092 } 1093 1094 /** 1095 * @return string the error message in XML format 1096 */ 1097 function serialize() 1098 { 1099 $rs = "<methodResponse>\n"; 1100 if ($this->fn) { 1101 $rs .= "<fault> 1102 <value> 1103 <struct> 1104 <member> 1105 <name>faultCode</name> 1106 <value><int>" . $this->fn . "</int></value> 1107 </member> 1108 <member> 1109 <name>faultString</name> 1110 <value><string>" . $this->fs . "</string></value> 1111 </member> 1112 </struct> 1113 </value> 1114</fault>"; 1115 } else { 1116 $rs .= "<params>\n<param>\n" . $this->xv->serialize() . 1117 "</param>\n</params>"; 1118 } 1119 $rs .= "\n</methodResponse>"; 1120 return $rs; 1121 } 1122} 1123 1124/** 1125 * The methods and properties for composing XML RPC messages 1126 * 1127 * @category Web Services 1128 * @package XML_RPC 1129 * @author Edd Dumbill <edd@usefulinc.com> 1130 * @author Stig Bakken <stig@php.net> 1131 * @author Martin Jansen <mj@php.net> 1132 * @author Daniel Convissor <danielc@php.net> 1133 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 1134 * @license http://www.php.net/license/3_01.txt PHP License 1135 * @version Release: @package_version@ 1136 * @link http://pear.php.net/package/XML_RPC 1137 */ 1138class XML_RPC_Message extends XML_RPC_Base 1139{ 1140 /** 1141 * Should the payload's content be passed through mb_convert_encoding()? 1142 * 1143 * @see XML_RPC_Message::setConvertPayloadEncoding() 1144 * @since Property available since Release 1.5.1 1145 * @var boolean 1146 */ 1147 var $convert_payload_encoding = false; 1148 1149 /** 1150 * The current debug mode (1 = on, 0 = off) 1151 * @var integer 1152 */ 1153 var $debug = 0; 1154 1155 /** 1156 * The encoding to be used for outgoing messages 1157 * 1158 * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var> 1159 * 1160 * @var string 1161 * @see XML_RPC_Message::setSendEncoding(), 1162 * $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header() 1163 */ 1164 var $send_encoding = ''; 1165 1166 /** 1167 * The method presently being evaluated 1168 * @var string 1169 */ 1170 var $methodname = ''; 1171 1172 /** 1173 * @var array 1174 */ 1175 var $params = array(); 1176 1177 /** 1178 * The XML message being generated 1179 * @var string 1180 */ 1181 var $payload = ''; 1182 1183 /** 1184 * Should extra line breaks be removed from the payload? 1185 * @since Property available since Release 1.4.6 1186 * @var boolean 1187 */ 1188 var $remove_extra_lines = true; 1189 1190 /** 1191 * The XML response from the remote server 1192 * @since Property available since Release 1.4.6 1193 * @var string 1194 */ 1195 var $response_payload = ''; 1196 1197 1198 /** 1199 * @return void 1200 */ 1201 function XML_RPC_Message($meth, $pars = 0) 1202 { 1203 $this->methodname = $meth; 1204 if (is_array($pars) && sizeof($pars) > 0) { 1205 for ($i = 0; $i < sizeof($pars); $i++) { 1206 $this->addParam($pars[$i]); 1207 } 1208 } 1209 } 1210 1211 /** 1212 * Produces the XML declaration including the encoding attribute 1213 * 1214 * The encoding is determined by this class' <var>$send_encoding</var> 1215 * property. If the <var>$send_encoding</var> property is not set, use 1216 * <var>$GLOBALS['XML_RPC_defencoding']</var>. 1217 * 1218 * @return string the XML declaration and <methodCall> element 1219 * 1220 * @see XML_RPC_Message::setSendEncoding(), 1221 * XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding'] 1222 */ 1223 function xml_header() 1224 { 1225 global $XML_RPC_defencoding; 1226 1227 if (!$this->send_encoding) { 1228 $this->send_encoding = $XML_RPC_defencoding; 1229 } 1230 return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>' 1231 . "\n<methodCall>\n"; 1232 } 1233 1234 /** 1235 * @return string the closing </methodCall> tag 1236 */ 1237 function xml_footer() 1238 { 1239 return "</methodCall>\n"; 1240 } 1241 1242 /** 1243 * Fills the XML_RPC_Message::$payload property 1244 * 1245 * Part of the process makes sure all line endings are in DOS format 1246 * (CRLF), which is probably required by specifications. 1247 * 1248 * If XML_RPC_Message::setConvertPayloadEncoding() was set to true, 1249 * the payload gets passed through mb_convert_encoding() 1250 * to ensure the payload matches the encoding set in the 1251 * XML declaration. The encoding type can be manually set via 1252 * XML_RPC_Message::setSendEncoding(). 1253 * 1254 * @return void 1255 * 1256 * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer() 1257 * @see XML_RPC_Message::setSendEncoding(), $GLOBALS['XML_RPC_defencoding'], 1258 * XML_RPC_Message::setConvertPayloadEncoding() 1259 */ 1260 function createPayload() 1261 { 1262 $this->payload = $this->xml_header(); 1263 $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n"; 1264 $this->payload .= "<params>\n"; 1265 for ($i = 0; $i < sizeof($this->params); $i++) { 1266 $p = $this->params[$i]; 1267 $this->payload .= "<param>\n" . $p->serialize() . "</param>\n"; 1268 } 1269 $this->payload .= "</params>\n"; 1270 $this->payload .= $this->xml_footer(); 1271 if ($this->remove_extra_lines) { 1272 $this->payload = preg_replace("@[\r\n]+@", "\r\n", $this->payload); 1273 } else { 1274 $this->payload = preg_replace("@\r\n|\n|\r|\n\r@", "\r\n", $this->payload); 1275 } 1276 if ($this->convert_payload_encoding) { 1277 $this->payload = mb_convert_encoding($this->payload, $this->send_encoding); 1278 } 1279 } 1280 1281 /** 1282 * @return string the name of the method 1283 */ 1284 function method($meth = '') 1285 { 1286 if ($meth != '') { 1287 $this->methodname = $meth; 1288 } 1289 return $this->methodname; 1290 } 1291 1292 /** 1293 * @return string the payload 1294 */ 1295 function serialize() 1296 { 1297 $this->createPayload(); 1298 return $this->payload; 1299 } 1300 1301 /** 1302 * @return void 1303 */ 1304 function addParam($par) 1305 { 1306 $this->params[] = $par; 1307 } 1308 1309 /** 1310 * Obtains an XML_RPC_Value object for the given parameter 1311 * 1312 * @param int $i the index number of the parameter to obtain 1313 * 1314 * @return object the XML_RPC_Value object. 1315 * If the parameter doesn't exist, an XML_RPC_Response object. 1316 * 1317 * @since Returns XML_RPC_Response object on error since Release 1.3.0 1318 */ 1319 function getParam($i) 1320 { 1321 global $XML_RPC_err, $XML_RPC_str; 1322 1323 if (isset($this->params[$i])) { 1324 return $this->params[$i]; 1325 } else { 1326 $this->raiseError('The submitted request did not contain this parameter', 1327 XML_RPC_ERROR_INCORRECT_PARAMS); 1328 return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'], 1329 $XML_RPC_str['incorrect_params']); 1330 } 1331 } 1332 1333 /** 1334 * @return int the number of parameters 1335 */ 1336 function getNumParams() 1337 { 1338 return sizeof($this->params); 1339 } 1340 1341 /** 1342 * Sets whether the payload's content gets passed through 1343 * mb_convert_encoding() 1344 * 1345 * Returns PEAR_ERROR object if mb_convert_encoding() isn't available. 1346 * 1347 * @param int $in where 1 = on, 0 = off 1348 * 1349 * @return void 1350 * 1351 * @see XML_RPC_Message::setSendEncoding() 1352 * @since Method available since Release 1.5.1 1353 */ 1354 function setConvertPayloadEncoding($in) 1355 { 1356 if ($in && !function_exists('mb_convert_encoding')) { 1357 return $this->raiseError('mb_convert_encoding() is not available', 1358 XML_RPC_ERROR_PROGRAMMING); 1359 } 1360 $this->convert_payload_encoding = $in; 1361 } 1362 1363 /** 1364 * Sets the XML declaration's encoding attribute 1365 * 1366 * @param string $type the encoding type (ISO-8859-1, UTF-8 or US-ASCII) 1367 * 1368 * @return void 1369 * 1370 * @see XML_RPC_Message::setConvertPayloadEncoding(), XML_RPC_Message::xml_header() 1371 * @since Method available since Release 1.2.0 1372 */ 1373 function setSendEncoding($type) 1374 { 1375 $this->send_encoding = $type; 1376 } 1377 1378 /** 1379 * Determine the XML's encoding via the encoding attribute 1380 * in the XML declaration 1381 * 1382 * If the encoding parameter is not set or is not ISO-8859-1, UTF-8 1383 * or US-ASCII, $XML_RPC_defencoding will be returned. 1384 * 1385 * @param string $data the XML that will be parsed 1386 * 1387 * @return string the encoding to be used 1388 * 1389 * @link http://php.net/xml_parser_create 1390 * @since Method available since Release 1.2.0 1391 */ 1392 function getEncoding($data) 1393 { 1394 global $XML_RPC_defencoding; 1395 1396 if (preg_match('@<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]@', 1397 $data, $match)) 1398 { 1399 $match[1] = trim(strtoupper($match[1])); 1400 switch ($match[1]) { 1401 case 'ISO-8859-1': 1402 case 'UTF-8': 1403 case 'US-ASCII': 1404 return $match[1]; 1405 break; 1406 1407 default: 1408 return $XML_RPC_defencoding; 1409 } 1410 } else { 1411 return $XML_RPC_defencoding; 1412 } 1413 } 1414 1415 /** 1416 * @return object a new XML_RPC_Response object 1417 */ 1418 function parseResponseFile($fp) 1419 { 1420 $ipd = ''; 1421 while ($data = @fread($fp, 8192)) { 1422 $ipd .= $data; 1423 } 1424 return $this->parseResponse($ipd); 1425 } 1426 1427 /** 1428 * @return object a new XML_RPC_Response object 1429 */ 1430 function parseResponse($data = '') 1431 { 1432 global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding; 1433 1434 $encoding = $this->getEncoding($data); 1435 $parser_resource = xml_parser_create($encoding); 1436 $parser = (int) $parser_resource; 1437 1438 $XML_RPC_xh = array(); 1439 $XML_RPC_xh[$parser] = array(); 1440 1441 $XML_RPC_xh[$parser]['cm'] = 0; 1442 $XML_RPC_xh[$parser]['isf'] = 0; 1443 $XML_RPC_xh[$parser]['ac'] = ''; 1444 $XML_RPC_xh[$parser]['qt'] = ''; 1445 $XML_RPC_xh[$parser]['stack'] = array(); 1446 $XML_RPC_xh[$parser]['valuestack'] = array(); 1447 1448 xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true); 1449 xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee'); 1450 xml_set_character_data_handler($parser_resource, 'XML_RPC_cd'); 1451 1452 $hdrfnd = 0; 1453 if ($this->debug) { 1454 print "\n<pre>---GOT---\n"; 1455 print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data; 1456 print "\n---END---</pre>\n"; 1457 } 1458 1459 // See if response is a 200 or a 100 then a 200, else raise error. 1460 // But only do this if we're using the HTTP protocol. 1461 if (preg_match('@^HTTP@', $data) && 1462 !preg_match('@^HTTP/[0-9\.]+ 200 @', $data) && 1463 !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data)) 1464 { 1465 $errstr = substr($data, 0, strpos($data, "\n") - 1); 1466 error_log('HTTP error, got response: ' . $errstr); 1467 $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'], 1468 $XML_RPC_str['http_error'] . ' (' . 1469 $errstr . ')'); 1470 xml_parser_free($parser_resource); 1471 return $r; 1472 } 1473 1474 // gotta get rid of headers here 1475 if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) { 1476 $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos); 1477 $data = substr($data, $brpos + 4); 1478 $hdrfnd = 1; 1479 } 1480 1481 /* 1482 * be tolerant of junk after methodResponse 1483 * (e.g. javascript automatically inserted by free hosts) 1484 * thanks to Luca Mariano <luca.mariano@email.it> 1485 */ 1486 $data = substr($data, 0, strpos($data, "</methodResponse>") + 17); 1487 $this->response_payload = $data; 1488 1489 if (!xml_parse($parser_resource, $data, sizeof($data))) { 1490 // thanks to Peter Kocks <peter.kocks@baygate.com> 1491 if (xml_get_current_line_number($parser_resource) == 1) { 1492 $errstr = 'XML error at line 1, check URL'; 1493 } else { 1494 $errstr = sprintf('XML error: %s at line %d', 1495 xml_error_string(xml_get_error_code($parser_resource)), 1496 xml_get_current_line_number($parser_resource)); 1497 } 1498 error_log($errstr); 1499 $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], 1500 $XML_RPC_str['invalid_return']); 1501 xml_parser_free($parser_resource); 1502 return $r; 1503 } 1504 1505 xml_parser_free($parser_resource); 1506 1507 if ($this->debug) { 1508 print "\n<pre>---PARSED---\n"; 1509 var_dump($XML_RPC_xh[$parser]['value']); 1510 print "---END---</pre>\n"; 1511 } 1512 1513 if ($XML_RPC_xh[$parser]['isf'] > 1) { 1514 $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], 1515 $XML_RPC_str['invalid_return'].' '.$XML_RPC_xh[$parser]['isf_reason']); 1516 } elseif (!is_object($XML_RPC_xh[$parser]['value'])) { 1517 // then something odd has happened 1518 // and it's time to generate a client side error 1519 // indicating something odd went on 1520 $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'], 1521 $XML_RPC_str['invalid_return']); 1522 } else { 1523 $v = $XML_RPC_xh[$parser]['value']; 1524 if ($XML_RPC_xh[$parser]['isf']) { 1525 $f = $v->structmem('faultCode'); 1526 $fs = $v->structmem('faultString'); 1527 $r = new XML_RPC_Response($v, $f->scalarval(), 1528 $fs->scalarval()); 1529 } else { 1530 $r = new XML_RPC_Response($v); 1531 } 1532 } 1533 $r->hdrs = preg_split("@\r?\n@", $XML_RPC_xh[$parser]['ha']); 1534 return $r; 1535 } 1536} 1537 1538/** 1539 * The methods and properties that represent data in XML RPC format 1540 * 1541 * @category Web Services 1542 * @package XML_RPC 1543 * @author Edd Dumbill <edd@usefulinc.com> 1544 * @author Stig Bakken <stig@php.net> 1545 * @author Martin Jansen <mj@php.net> 1546 * @author Daniel Convissor <danielc@php.net> 1547 * @copyright 1999-2001 Edd Dumbill, 2001-2010 The PHP Group 1548 * @license http://www.php.net/license/3_01.txt PHP License 1549 * @version Release: @package_version@ 1550 * @link http://pear.php.net/package/XML_RPC 1551 */ 1552class XML_RPC_Value extends XML_RPC_Base 1553{ 1554 var $me = array(); 1555 var $mytype = 0; 1556 1557 /** 1558 * @return void 1559 */ 1560 function XML_RPC_Value($val = -1, $type = '') 1561 { 1562 $this->me = array(); 1563 $this->mytype = 0; 1564 if ($val != -1 || $type != '') { 1565 if ($type == '') { 1566 $type = 'string'; 1567 } 1568 if (!array_key_exists($type, $GLOBALS['XML_RPC_Types'])) { 1569 // XXX 1570 // need some way to report this error 1571 } elseif ($GLOBALS['XML_RPC_Types'][$type] == 1) { 1572 $this->addScalar($val, $type); 1573 } elseif ($GLOBALS['XML_RPC_Types'][$type] == 2) { 1574 $this->addArray($val); 1575 } elseif ($GLOBALS['XML_RPC_Types'][$type] == 3) { 1576 $this->addStruct($val); 1577 } 1578 } 1579 } 1580 1581 /** 1582 * @return int returns 1 if successful or 0 if there are problems 1583 */ 1584 function addScalar($val, $type = 'string') 1585 { 1586 if ($this->mytype == 1) { 1587 $this->raiseError('Scalar can have only one value', 1588 XML_RPC_ERROR_INVALID_TYPE); 1589 return 0; 1590 } 1591 $typeof = $GLOBALS['XML_RPC_Types'][$type]; 1592 if ($typeof != 1) { 1593 $this->raiseError("Not a scalar type (${typeof})", 1594 XML_RPC_ERROR_INVALID_TYPE); 1595 return 0; 1596 } 1597 1598 if ($type == $GLOBALS['XML_RPC_Boolean']) { 1599 if (strcasecmp($val, 'true') == 0 1600 || $val == 1 1601 || ($val == true && strcasecmp($val, 'false'))) 1602 { 1603 $val = 1; 1604 } else { 1605 $val = 0; 1606 } 1607 } 1608 1609 if ($this->mytype == 2) { 1610 // we're adding to an array here 1611 $ar = $this->me['array']; 1612 $ar[] = new XML_RPC_Value($val, $type); 1613 $this->me['array'] = $ar; 1614 } else { 1615 // a scalar, so set the value and remember we're scalar 1616 $this->me[$type] = $val; 1617 $this->mytype = $typeof; 1618 } 1619 return 1; 1620 } 1621 1622 /** 1623 * @return int returns 1 if successful or 0 if there are problems 1624 */ 1625 function addArray($vals) 1626 { 1627 if ($this->mytype != 0) { 1628 $this->raiseError( 1629 'Already initialized as a [' . $this->kindOf() . ']', 1630 XML_RPC_ERROR_ALREADY_INITIALIZED); 1631 return 0; 1632 } 1633 $this->mytype = $GLOBALS['XML_RPC_Types']['array']; 1634 $this->me['array'] = $vals; 1635 return 1; 1636 } 1637 1638 /** 1639 * @return int returns 1 if successful or 0 if there are problems 1640 */ 1641 function addStruct($vals) 1642 { 1643 if ($this->mytype != 0) { 1644 $this->raiseError( 1645 'Already initialized as a [' . $this->kindOf() . ']', 1646 XML_RPC_ERROR_ALREADY_INITIALIZED); 1647 return 0; 1648 } 1649 $this->mytype = $GLOBALS['XML_RPC_Types']['struct']; 1650 $this->me['struct'] = $vals; 1651 return 1; 1652 } 1653 1654 /** 1655 * @return void 1656 */ 1657 function dump($ar) 1658 { 1659 reset($ar); 1660 foreach ($ar as $key => $val) { 1661 echo "$key => $val<br />"; 1662 if ($key == 'array') { 1663 foreach ($val as $key2 => $val2) { 1664 echo "-- $key2 => $val2<br />"; 1665 } 1666 } 1667 } 1668 } 1669 1670 /** 1671 * @return string the data type of the current value 1672 */ 1673 function kindOf() 1674 { 1675 switch ($this->mytype) { 1676 case 3: 1677 return 'struct'; 1678 1679 case 2: 1680 return 'array'; 1681 1682 case 1: 1683 return 'scalar'; 1684 1685 default: 1686 return 'undef'; 1687 } 1688 } 1689 1690 /** 1691 * @return string the data in XML format 1692 */ 1693 function serializedata($typ, $val) 1694 { 1695 $rs = ''; 1696 if (!array_key_exists($typ, $GLOBALS['XML_RPC_Types'])) { 1697 // XXX 1698 // need some way to report this error 1699 return; 1700 } 1701 switch ($GLOBALS['XML_RPC_Types'][$typ]) { 1702 case 3: 1703 // struct 1704 $rs .= "<struct>\n"; 1705 reset($val); 1706 foreach ($val as $key2 => $val2) { 1707 $rs .= "<member><name>" . htmlspecialchars($key2) . "</name>\n"; 1708 $rs .= $this->serializeval($val2); 1709 $rs .= "</member>\n"; 1710 } 1711 $rs .= '</struct>'; 1712 break; 1713 1714 case 2: 1715 // array 1716 $rs .= "<array>\n<data>\n"; 1717 foreach ($val as $value) { 1718 $rs .= $this->serializeval($value); 1719 } 1720 $rs .= "</data>\n</array>"; 1721 break; 1722 1723 case 1: 1724 switch ($typ) { 1725 case $GLOBALS['XML_RPC_Base64']: 1726 $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>"; 1727 break; 1728 case $GLOBALS['XML_RPC_Boolean']: 1729 $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>"; 1730 break; 1731 case $GLOBALS['XML_RPC_String']: 1732 $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>"; 1733 break; 1734 default: 1735 $rs .= "<${typ}>${val}</${typ}>"; 1736 } 1737 } 1738 return $rs; 1739 } 1740 1741 /** 1742 * @return string the data in XML format 1743 */ 1744 function serialize() 1745 { 1746 return $this->serializeval($this); 1747 } 1748 1749 /** 1750 * @return string the data in XML format 1751 */ 1752 function serializeval($o) 1753 { 1754 if (!is_object($o) || empty($o->me) || !is_array($o->me)) { 1755 return ''; 1756 } 1757 $ar = $o->me; 1758 reset($ar); 1759 list($typ, $val) = each($ar); 1760 return '<value>' . $this->serializedata($typ, $val) . "</value>\n"; 1761 } 1762 1763 /** 1764 * @return mixed the contents of the element requested 1765 */ 1766 function structmem($m) 1767 { 1768 return $this->me['struct'][$m]; 1769 } 1770 1771 /** 1772 * @return void 1773 */ 1774 function structreset() 1775 { 1776 reset($this->me['struct']); 1777 } 1778 1779 /** 1780 * @return the key/value pair of the struct's current element 1781 */ 1782 function structeach() 1783 { 1784 return each($this->me['struct']); 1785 } 1786 1787 /** 1788 * @return mixed the current value 1789 */ 1790 function getval() 1791 { 1792 // UNSTABLE 1793 1794 reset($this->me); 1795 $b = current($this->me); 1796 1797 // contributed by I Sofer, 2001-03-24 1798 // add support for nested arrays to scalarval 1799 // i've created a new method here, so as to 1800 // preserve back compatibility 1801 1802 if (is_array($b)) { 1803 foreach ($b as $id => $cont) { 1804 $b[$id] = $cont->scalarval(); 1805 } 1806 } 1807 1808 // add support for structures directly encoding php objects 1809 if (is_object($b)) { 1810 $t = get_object_vars($b); 1811 foreach ($t as $id => $cont) { 1812 $t[$id] = $cont->scalarval(); 1813 } 1814 foreach ($t as $id => $cont) { 1815 $b->$id = $cont; 1816 } 1817 } 1818 1819 // end contrib 1820 return $b; 1821 } 1822 1823 /** 1824 * @return mixed the current element's scalar value. If the value is 1825 * not scalar, FALSE is returned. 1826 */ 1827 function scalarval() 1828 { 1829 reset($this->me); 1830 $v = current($this->me); 1831 if (!is_scalar($v)) { 1832 $v = false; 1833 } 1834 return $v; 1835 } 1836 1837 /** 1838 * @return string 1839 */ 1840 function scalartyp() 1841 { 1842 reset($this->me); 1843 $a = key($this->me); 1844 if ($a == $GLOBALS['XML_RPC_I4']) { 1845 $a = $GLOBALS['XML_RPC_Int']; 1846 } 1847 return $a; 1848 } 1849 1850 /** 1851 * @return mixed the struct's current element 1852 */ 1853 function arraymem($m) 1854 { 1855 return $this->me['array'][$m]; 1856 } 1857 1858 /** 1859 * @return int the number of elements in the array 1860 */ 1861 function arraysize() 1862 { 1863 reset($this->me); 1864 list($a, $b) = each($this->me); 1865 return sizeof($b); 1866 } 1867 1868 /** 1869 * Determines if the item submitted is an XML_RPC_Value object 1870 * 1871 * @param mixed $val the variable to be evaluated 1872 * 1873 * @return bool TRUE if the item is an XML_RPC_Value object 1874 * 1875 * @static 1876 * @since Method available since Release 1.3.0 1877 */ 1878 function isValue($val) 1879 { 1880 return (strtolower(get_class($val)) == 'xml_rpc_value'); 1881 } 1882} 1883 1884/** 1885 * Return an ISO8601 encoded string 1886 * 1887 * While timezones ought to be supported, the XML-RPC spec says: 1888 * 1889 * "Don't assume a timezone. It should be specified by the server in its 1890 * documentation what assumptions it makes about timezones." 1891 * 1892 * This routine always assumes localtime unless $utc is set to 1, in which 1893 * case UTC is assumed and an adjustment for locale is made when encoding. 1894 * 1895 * @return string the formatted date 1896 */ 1897function XML_RPC_iso8601_encode($timet, $utc = 0) 1898{ 1899 if (!$utc) { 1900 $t = strftime('%Y%m%dT%H:%M:%S', $timet); 1901 } else { 1902 if (function_exists('gmstrftime')) { 1903 // gmstrftime doesn't exist in some versions 1904 // of PHP 1905 $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet); 1906 } else { 1907 $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z')); 1908 } 1909 } 1910 return $t; 1911} 1912 1913/** 1914 * Convert a datetime string into a Unix timestamp 1915 * 1916 * While timezones ought to be supported, the XML-RPC spec says: 1917 * 1918 * "Don't assume a timezone. It should be specified by the server in its 1919 * documentation what assumptions it makes about timezones." 1920 * 1921 * This routine always assumes localtime unless $utc is set to 1, in which 1922 * case UTC is assumed and an adjustment for locale is made when encoding. 1923 * 1924 * @return int the unix timestamp of the date submitted 1925 */ 1926function XML_RPC_iso8601_decode($idate, $utc = 0) 1927{ 1928 $t = 0; 1929 if (preg_match('@([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})@', $idate, $regs)) { 1930 if ($utc) { 1931 $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); 1932 } else { 1933 $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); 1934 } 1935 } 1936 return $t; 1937} 1938 1939/** 1940 * Converts an XML_RPC_Value object into native PHP types 1941 * 1942 * @param object $XML_RPC_val the XML_RPC_Value object to decode 1943 * 1944 * @return mixed the PHP values 1945 */ 1946function XML_RPC_decode($XML_RPC_val) 1947{ 1948 $kind = $XML_RPC_val->kindOf(); 1949 1950 if ($kind == 'scalar') { 1951 return $XML_RPC_val->scalarval(); 1952 1953 } elseif ($kind == 'array') { 1954 $size = $XML_RPC_val->arraysize(); 1955 $arr = array(); 1956 for ($i = 0; $i < $size; $i++) { 1957 $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i)); 1958 } 1959 return $arr; 1960 1961 } elseif ($kind == 'struct') { 1962 $XML_RPC_val->structreset(); 1963 $arr = array(); 1964 while (list($key, $value) = $XML_RPC_val->structeach()) { 1965 $arr[$key] = XML_RPC_decode($value); 1966 } 1967 return $arr; 1968 } 1969} 1970 1971/** 1972 * Converts native PHP types into an XML_RPC_Value object 1973 * 1974 * @param mixed $php_val the PHP value or variable you want encoded 1975 * 1976 * @return object the XML_RPC_Value object 1977 */ 1978function XML_RPC_encode($php_val) 1979{ 1980 $type = gettype($php_val); 1981 $XML_RPC_val = new XML_RPC_Value; 1982 1983 switch ($type) { 1984 case 'array': 1985 if (empty($php_val)) { 1986 $XML_RPC_val->addArray($php_val); 1987 break; 1988 } 1989 $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1)); 1990 if (empty($tmp)) { 1991 $arr = array(); 1992 foreach ($php_val as $k => $v) { 1993 $arr[$k] = XML_RPC_encode($v); 1994 } 1995 $XML_RPC_val->addArray($arr); 1996 break; 1997 } 1998 // fall though if it's not an enumerated array 1999 2000 case 'object': 2001 $arr = array(); 2002 foreach ($php_val as $k => $v) { 2003 $arr[$k] = XML_RPC_encode($v); 2004 } 2005 $XML_RPC_val->addStruct($arr); 2006 break; 2007 2008 case 'integer': 2009 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Int']); 2010 break; 2011 2012 case 'double': 2013 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Double']); 2014 break; 2015 2016 case 'string': 2017 case 'NULL': 2018 if (preg_match('@^[0-9]{8}\T{1}[0-9]{2}\:[0-9]{2}\:[0-9]{2}$@', $php_val)) { 2019 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_DateTime']); 2020 } elseif ($GLOBALS['XML_RPC_auto_base64'] 2021 && preg_match("@[^ -~\t\r\n]@", $php_val)) 2022 { 2023 // Characters other than alpha-numeric, punctuation, SP, TAB, 2024 // LF and CR break the XML parser, encode value via Base 64. 2025 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Base64']); 2026 } else { 2027 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_String']); 2028 } 2029 break; 2030 2031 case 'boolean': 2032 // Add support for encoding/decoding of booleans, since they 2033 // are supported in PHP 2034 // by <G_Giunta_2001-02-29> 2035 $XML_RPC_val->addScalar($php_val, $GLOBALS['XML_RPC_Boolean']); 2036 break; 2037 2038 case 'unknown type': 2039 default: 2040 $XML_RPC_val = false; 2041 } 2042 return $XML_RPC_val; 2043} 2044 2045/* 2046 * Local variables: 2047 * tab-width: 4 2048 * c-basic-offset: 4 2049 * c-hanging-comment-ender-p: nil 2050 * End: 2051 */ 2052 2053?> 2054