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