| Total Complexity | 115 |
| Total Lines | 813 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like xmlrpc_server often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use xmlrpc_server, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 433 | class xmlrpc_server |
||
| 434 | {
|
||
| 435 | /** |
||
| 436 | * Array defining php functions exposed as xmlrpc methods by this server |
||
| 437 | * @access private |
||
| 438 | */ |
||
| 439 | var $dmap=array(); |
||
| 440 | /** |
||
| 441 | * Defines how functions in dmap will be invoked: either using an xmlrpc msg object |
||
| 442 | * or plain php values. |
||
| 443 | * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals' |
||
| 444 | */ |
||
| 445 | var $functions_parameters_type='xmlrpcvals'; |
||
| 446 | /** |
||
| 447 | * Option used for fine-tuning the encoding the php values returned from |
||
| 448 | * functions registered in the dispatch map when the functions_parameters_types |
||
| 449 | * member is set to 'phpvals' |
||
| 450 | * @see php_xmlrpc_encode for a list of values |
||
| 451 | */ |
||
| 452 | var $phpvals_encoding_options = array( 'auto_dates' ); |
||
| 453 | /// controls whether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3 |
||
| 454 | var $debug = 1; |
||
| 455 | /** |
||
| 456 | * Controls behaviour of server when invoked user function throws an exception: |
||
| 457 | * 0 = catch it and return an 'internal error' xmlrpc response (default) |
||
| 458 | * 1 = catch it and return an xmlrpc response with the error corresponding to the exception |
||
| 459 | * 2 = allow the exception to float to the upper layers |
||
| 460 | */ |
||
| 461 | var $exception_handling = 0; |
||
| 462 | /** |
||
| 463 | * When set to true, it will enable HTTP compression of the response, in case |
||
| 464 | * the client has declared its support for compression in the request. |
||
| 465 | */ |
||
| 466 | var $compress_response = false; |
||
| 467 | /** |
||
| 468 | * List of http compression methods accepted by the server for requests. |
||
| 469 | * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib |
||
| 470 | */ |
||
| 471 | var $accepted_compression = array(); |
||
| 472 | /// shall we serve calls to system.* methods? |
||
| 473 | var $allow_system_funcs = true; |
||
| 474 | /// list of charset encodings natively accepted for requests |
||
| 475 | var $accepted_charset_encodings = array(); |
||
| 476 | /** |
||
| 477 | * charset encoding to be used for response. |
||
| 478 | * NB: if we can, we will convert the generated response from internal_encoding to the intended one. |
||
| 479 | * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled), |
||
| 480 | * null (leave unspecified in response, convert output stream to US_ASCII), |
||
| 481 | * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed), |
||
| 482 | * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway). |
||
| 483 | * NB: pretty dangerous if you accept every charset and do not have mbstring enabled) |
||
| 484 | */ |
||
| 485 | var $response_charset_encoding = ''; |
||
| 486 | /** |
||
| 487 | * Storage for internal debug info |
||
| 488 | * @access private |
||
| 489 | */ |
||
| 490 | var $debug_info = ''; |
||
| 491 | /** |
||
| 492 | * Extra data passed at runtime to method handling functions. Used only by EPI layer |
||
| 493 | */ |
||
| 494 | var $user_data = null; |
||
| 495 | |||
| 496 | /** |
||
| 497 | * @param array $dispmap the dispatch map with definition of exposed services |
||
| 498 | * @param boolean $servicenow set to false to prevent the server from running upon construction |
||
| 499 | */ |
||
| 500 | function __construct($dispMap=null, $serviceNow=true) |
||
| 501 | {
|
||
| 502 | // if ZLIB is enabled, let the server by default accept compressed requests, |
||
| 503 | // and compress responses sent to clients that support them |
||
| 504 | if(function_exists('gzinflate'))
|
||
| 505 | {
|
||
| 506 | $this->accepted_compression = array('gzip', 'deflate');
|
||
| 507 | $this->compress_response = true; |
||
| 508 | } |
||
| 509 | |||
| 510 | // by default the xml parser can support these 3 charset encodings |
||
| 511 | $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
|
||
| 512 | |||
| 513 | // dispMap is a dispatch array of methods |
||
| 514 | // mapped to function names and signatures |
||
| 515 | // if a method |
||
| 516 | // doesn't appear in the map then an unknown |
||
| 517 | // method error is generated |
||
| 518 | /* milosch - changed to make passing dispMap optional. |
||
| 519 | * instead, you can use the class add_to_map() function |
||
| 520 | * to add functions manually (borrowed from SOAPX4) |
||
| 521 | */ |
||
| 522 | if($dispMap) |
||
| 523 | {
|
||
| 524 | $this->dmap = $dispMap; |
||
| 525 | if($serviceNow) |
||
| 526 | {
|
||
| 527 | $this->service(); |
||
| 528 | } |
||
| 529 | } |
||
| 530 | } |
||
| 531 | |||
| 532 | /** |
||
| 533 | * @deprecated |
||
| 534 | */ |
||
| 535 | function xmlrpc_client($dispMap=null, $serviceNow=true) |
||
| 536 | {
|
||
| 537 | self::__construct($dispMap, $serviceNow); |
||
| 538 | } |
||
| 539 | |||
| 540 | /** |
||
| 541 | * Set debug level of server. |
||
| 542 | * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments) |
||
| 543 | * 0 = no debug info, |
||
| 544 | * 1 = msgs set from user with debugmsg(), |
||
| 545 | * 2 = add complete xmlrpc request (headers and body), |
||
| 546 | * 3 = add also all processing warnings happened during method processing |
||
| 547 | * (NB: this involves setting a custom error handler, and might interfere |
||
| 548 | * with the standard processing of the php function exposed as method. In |
||
| 549 | * particular, triggering an USER_ERROR level error will not halt script |
||
| 550 | * execution anymore, but just end up logged in the xmlrpc response) |
||
| 551 | * Note that info added at level 2 and 3 will be base64 encoded |
||
| 552 | * @access public |
||
| 553 | */ |
||
| 554 | function setDebug($in) |
||
| 555 | {
|
||
| 556 | $this->debug=$in; |
||
| 557 | } |
||
| 558 | |||
| 559 | /** |
||
| 560 | * Return a string with the serialized representation of all debug info |
||
| 561 | * @param string $charset_encoding the target charset encoding for the serialization |
||
| 562 | * @return string an XML comment (or two) |
||
| 563 | */ |
||
| 564 | function serializeDebug($charset_encoding='') |
||
| 565 | {
|
||
| 566 | // Tough encoding problem: which internal charset should we assume for debug info? |
||
| 567 | // It might contain a copy of raw data received from client, ie with unknown encoding, |
||
| 568 | // intermixed with php generated data and user generated data... |
||
| 569 | // so we split it: system debug is base 64 encoded, |
||
| 570 | // user debug info should be encoded by the end user using the INTERNAL_ENCODING |
||
| 571 | $out = ''; |
||
| 572 | if ($this->debug_info != '') |
||
| 573 | {
|
||
| 574 | $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n"; |
||
| 575 | } |
||
| 576 | if($GLOBALS['_xmlrpc_debuginfo']!='') |
||
| 577 | {
|
||
| 578 | |||
| 579 | $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
|
||
| 580 | // NB: a better solution MIGHT be to use CDATA, but we need to insert it |
||
| 581 | // into return payload AFTER the beginning tag |
||
| 582 | //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
|
||
| 583 | } |
||
| 584 | return $out; |
||
| 585 | } |
||
| 586 | |||
| 587 | /** |
||
| 588 | * Execute the xmlrpc request, printing the response |
||
| 589 | * @param string $data the request body. If null, the http POST request will be examined |
||
| 590 | * @return xmlrpcresp the response object (usually not used by caller...) |
||
| 591 | * @access public |
||
| 592 | */ |
||
| 593 | function service($data=null, $return_payload=false) |
||
| 594 | {
|
||
| 595 | if ($data === null) |
||
| 596 | {
|
||
| 597 | // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA |
||
| 598 | $data = file_get_contents('php://input');
|
||
| 599 | } |
||
| 600 | $raw_data = $data; |
||
| 601 | |||
| 602 | // reset internal debug info |
||
| 603 | $this->debug_info = ''; |
||
| 604 | |||
| 605 | // Echo back what we received, before parsing it |
||
| 606 | if($this->debug > 1) |
||
| 607 | {
|
||
| 608 | $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
|
||
| 609 | } |
||
| 610 | |||
| 611 | $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding); |
||
| 612 | if (!$r) |
||
| 613 | {
|
||
| 614 | $r=$this->parseRequest($data, $req_charset); |
||
| 615 | } |
||
| 616 | |||
| 617 | // save full body of request into response, for more debugging usages |
||
| 618 | $r->raw_data = $raw_data; |
||
| 619 | |||
| 620 | if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors']) |
||
| 621 | {
|
||
| 622 | $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
|
||
| 623 | $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++"); |
||
| 624 | } |
||
| 625 | |||
| 626 | $payload=$this->xml_header($resp_charset); |
||
| 627 | if($this->debug > 0) |
||
| 628 | {
|
||
| 629 | $payload = $payload . $this->serializeDebug($resp_charset); |
||
| 630 | } |
||
| 631 | |||
| 632 | // G. Giunta 2006-01-27: do not create response serialization if it has |
||
| 633 | // already happened. Helps building json magic |
||
| 634 | if (empty($r->payload)) |
||
| 635 | {
|
||
| 636 | $r->serialize($resp_charset); |
||
| 637 | } |
||
| 638 | $payload = $payload . $r->payload; |
||
| 639 | |||
| 640 | if ($return_payload) |
||
| 641 | {
|
||
| 642 | return $payload; |
||
| 643 | } |
||
| 644 | |||
| 645 | // if we get a warning/error that has output some text before here, then we cannot |
||
| 646 | // add a new header. We cannot say we are sending xml, either... |
||
| 647 | if(!headers_sent()) |
||
| 648 | {
|
||
| 649 | header('Content-Type: '.$r->content_type);
|
||
| 650 | // we do not know if client actually told us an accepted charset, but if he did |
||
| 651 | // we have to tell him what we did |
||
| 652 | header("Vary: Accept-Charset");
|
||
| 653 | |||
| 654 | // http compression of output: only |
||
| 655 | // if we can do it, and we want to do it, and client asked us to, |
||
| 656 | // and php ini settings do not force it already |
||
| 657 | $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
|
||
| 658 | if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
|
||
| 659 | && $php_no_self_compress) |
||
| 660 | {
|
||
| 661 | if(strpos($resp_encoding, 'gzip') !== false) |
||
| 662 | {
|
||
| 663 | $payload = gzencode($payload); |
||
| 664 | header("Content-Encoding: gzip");
|
||
| 665 | header("Vary: Accept-Encoding");
|
||
| 666 | } |
||
| 667 | elseif (strpos($resp_encoding, 'deflate') !== false) |
||
| 668 | {
|
||
| 669 | $payload = gzcompress($payload); |
||
| 670 | header("Content-Encoding: deflate");
|
||
| 671 | header("Vary: Accept-Encoding");
|
||
| 672 | } |
||
| 673 | } |
||
| 674 | |||
| 675 | // do not ouput content-length header if php is compressing output for us: |
||
| 676 | // it will mess up measurements |
||
| 677 | if($php_no_self_compress) |
||
| 678 | {
|
||
| 679 | header('Content-Length: ' . (int)strlen($payload));
|
||
| 680 | } |
||
| 681 | } |
||
| 682 | else |
||
| 683 | {
|
||
| 684 | error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
|
||
| 685 | } |
||
| 686 | |||
| 687 | print $payload; |
||
| 688 | |||
| 689 | // return request, in case subclasses want it |
||
| 690 | return $r; |
||
| 691 | } |
||
| 692 | |||
| 693 | /** |
||
| 694 | * Add a method to the dispatch map |
||
| 695 | * @param string $methodname the name with which the method will be made available |
||
| 696 | * @param string $function the php function that will get invoked |
||
| 697 | * @param array $sig the array of valid method signatures |
||
| 698 | * @param string $doc method documentation |
||
| 699 | * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type) |
||
| 700 | * @access public |
||
| 701 | */ |
||
| 702 | function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false) |
||
| 703 | {
|
||
| 704 | $this->dmap[$methodname] = array( |
||
| 705 | 'function' => $function, |
||
| 706 | 'docstring' => $doc |
||
| 707 | ); |
||
| 708 | if ($sig) |
||
| 709 | {
|
||
| 710 | $this->dmap[$methodname]['signature'] = $sig; |
||
| 711 | } |
||
| 712 | if ($sigdoc) |
||
| 713 | {
|
||
| 714 | $this->dmap[$methodname]['signature_docs'] = $sigdoc; |
||
| 715 | } |
||
| 716 | } |
||
| 717 | |||
| 718 | /** |
||
| 719 | * Verify type and number of parameters received against a list of known signatures |
||
| 720 | * @param array $in array of either xmlrpcval objects or xmlrpc type definitions |
||
| 721 | * @param array $sig array of known signatures to match against |
||
| 722 | * @return array |
||
| 723 | * @access private |
||
| 724 | */ |
||
| 725 | function verifySignature($in, $sig) |
||
| 726 | {
|
||
| 727 | // check each possible signature in turn |
||
| 728 | if (is_object($in)) |
||
| 729 | {
|
||
| 730 | $numParams = $in->getNumParams(); |
||
| 731 | } |
||
| 732 | else |
||
| 733 | {
|
||
| 734 | $numParams = count($in); |
||
| 735 | } |
||
| 736 | foreach($sig as $cursig) |
||
| 737 | {
|
||
| 738 | if(count($cursig)==$numParams+1) |
||
| 739 | {
|
||
| 740 | $itsOK=1; |
||
| 741 | for($n=0; $n<$numParams; $n++) |
||
| 742 | {
|
||
| 743 | if (is_object($in)) |
||
| 744 | {
|
||
| 745 | $p=$in->getParam($n); |
||
| 746 | if($p->kindOf() == 'scalar') |
||
| 747 | {
|
||
| 748 | $pt=$p->scalartyp(); |
||
| 749 | } |
||
| 750 | else |
||
| 751 | {
|
||
| 752 | $pt=$p->kindOf(); |
||
| 753 | } |
||
| 754 | } |
||
| 755 | else |
||
| 756 | {
|
||
| 757 | $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4... |
||
| 758 | } |
||
| 759 | |||
| 760 | // param index is $n+1, as first member of sig is return type |
||
| 761 | if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue']) |
||
| 762 | {
|
||
| 763 | $itsOK=0; |
||
| 764 | $pno=$n+1; |
||
| 765 | $wanted=$cursig[$n+1]; |
||
| 766 | $got=$pt; |
||
| 767 | break; |
||
| 768 | } |
||
| 769 | } |
||
| 770 | if($itsOK) |
||
| 771 | {
|
||
| 772 | return array(1,''); |
||
| 773 | } |
||
| 774 | } |
||
| 775 | } |
||
| 776 | if(isset($wanted)) |
||
| 777 | {
|
||
| 778 | return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
|
||
| 779 | } |
||
| 780 | else |
||
| 781 | {
|
||
| 782 | return array(0, "No method signature matches number of parameters"); |
||
| 783 | } |
||
| 784 | } |
||
| 785 | |||
| 786 | /** |
||
| 787 | * Parse http headers received along with xmlrpc request. If needed, inflate request |
||
| 788 | * @return mixed null on success or an xmlrpcresp |
||
| 789 | * @access private |
||
| 790 | */ |
||
| 791 | function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression) |
||
| 792 | {
|
||
| 793 | // check if $_SERVER is populated: it might have been disabled via ini file |
||
| 794 | // (this is true even when in CLI mode) |
||
| 795 | if (count($_SERVER) == 0) |
||
| 796 | {
|
||
| 797 | error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
|
||
| 798 | } |
||
| 799 | |||
| 800 | if($this->debug > 1) |
||
| 801 | {
|
||
| 802 | if(function_exists('getallheaders'))
|
||
| 803 | {
|
||
| 804 | $this->debugmsg(''); // empty line
|
||
| 805 | foreach(getallheaders() as $name => $val) |
||
| 806 | {
|
||
| 807 | $this->debugmsg("HEADER: $name: $val");
|
||
| 808 | } |
||
| 809 | } |
||
| 810 | |||
| 811 | } |
||
| 812 | |||
| 813 | if(isset($_SERVER['HTTP_CONTENT_ENCODING'])) |
||
| 814 | {
|
||
| 815 | $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
|
||
| 816 | } |
||
| 817 | else |
||
| 818 | {
|
||
| 819 | $content_encoding = ''; |
||
| 820 | } |
||
| 821 | |||
| 822 | // check if request body has been compressed and decompress it |
||
| 823 | if($content_encoding != '' && strlen($data)) |
||
| 824 | {
|
||
| 825 | if($content_encoding == 'deflate' || $content_encoding == 'gzip') |
||
| 826 | {
|
||
| 827 | // if decoding works, use it. else assume data wasn't gzencoded |
||
| 828 | if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
|
||
| 829 | {
|
||
| 830 | if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data)) |
||
| 831 | {
|
||
| 832 | $data = $degzdata; |
||
| 833 | if($this->debug > 1) |
||
| 834 | {
|
||
| 835 | $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
|
||
| 836 | } |
||
| 837 | } |
||
| 838 | elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) |
||
| 839 | {
|
||
| 840 | $data = $degzdata; |
||
| 841 | if($this->debug > 1) |
||
| 842 | $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
|
||
| 843 | } |
||
| 844 | else |
||
| 845 | {
|
||
| 846 | $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']); |
||
| 847 | return $r; |
||
| 848 | } |
||
| 849 | } |
||
| 850 | else |
||
| 851 | {
|
||
| 852 | //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
|
||
| 853 | $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']); |
||
| 854 | return $r; |
||
| 855 | } |
||
| 856 | } |
||
| 857 | } |
||
| 858 | |||
| 859 | // check if client specified accepted charsets, and if we know how to fulfill |
||
| 860 | // the request |
||
| 861 | if ($this->response_charset_encoding == 'auto') |
||
| 862 | {
|
||
| 863 | $resp_encoding = ''; |
||
| 864 | if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) |
||
| 865 | {
|
||
| 866 | // here we should check if we can match the client-requested encoding |
||
| 867 | // with the encodings we know we can generate. |
||
| 868 | /// @todo we should parse q=0.x preferences instead of getting first charset specified... |
||
| 869 | $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
|
||
| 870 | // Give preference to internal encoding |
||
| 871 | $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII'); |
||
| 872 | foreach ($known_charsets as $charset) |
||
| 873 | {
|
||
| 874 | foreach ($client_accepted_charsets as $accepted) |
||
| 875 | if (strpos($accepted, $charset) === 0) |
||
| 876 | {
|
||
| 877 | $resp_encoding = $charset; |
||
| 878 | break; |
||
| 879 | } |
||
| 880 | if ($resp_encoding) |
||
| 881 | break; |
||
| 882 | } |
||
| 883 | } |
||
| 884 | } |
||
| 885 | else |
||
| 886 | {
|
||
| 887 | $resp_encoding = $this->response_charset_encoding; |
||
| 888 | } |
||
| 889 | |||
| 890 | if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) |
||
| 891 | {
|
||
| 892 | $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING']; |
||
| 893 | } |
||
| 894 | else |
||
| 895 | {
|
||
| 896 | $resp_compression = ''; |
||
| 897 | } |
||
| 898 | |||
| 899 | // 'guestimate' request encoding |
||
| 900 | /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check??? |
||
| 901 | $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '', |
||
| 902 | $data); |
||
| 903 | |||
| 904 | return null; |
||
| 905 | } |
||
| 906 | |||
| 907 | /** |
||
| 908 | * Parse an xml chunk containing an xmlrpc request and execute the corresponding |
||
| 909 | * php function registered with the server |
||
| 910 | * @param string $data the xml request |
||
| 911 | * @param string $req_encoding (optional) the charset encoding of the xml request |
||
| 912 | * @return xmlrpcresp |
||
| 913 | * @access private |
||
| 914 | */ |
||
| 915 | function parseRequest($data, $req_encoding='') |
||
| 916 | {
|
||
| 917 | // 2005/05/07 commented and moved into caller function code |
||
| 918 | //if($data=='') |
||
| 919 | //{
|
||
| 920 | // $data=$GLOBALS['HTTP_RAW_POST_DATA']; |
||
| 921 | //} |
||
| 922 | |||
| 923 | // G. Giunta 2005/02/13: we do NOT expect to receive html entities |
||
| 924 | // so we do not try to convert them into xml character entities |
||
| 925 | //$data = xmlrpc_html_entity_xlate($data); |
||
| 926 | |||
| 927 | $GLOBALS['_xh']=array(); |
||
| 928 | $GLOBALS['_xh']['ac']=''; |
||
| 929 | $GLOBALS['_xh']['stack']=array(); |
||
| 930 | $GLOBALS['_xh']['valuestack'] = array(); |
||
| 931 | $GLOBALS['_xh']['params']=array(); |
||
| 932 | $GLOBALS['_xh']['pt']=array(); |
||
| 933 | $GLOBALS['_xh']['isf']=0; |
||
| 934 | $GLOBALS['_xh']['isf_reason']=''; |
||
| 935 | $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not |
||
| 936 | $GLOBALS['_xh']['rt']=''; |
||
| 937 | |||
| 938 | // decompose incoming XML into request structure |
||
| 939 | |||
| 940 | if ($req_encoding != '') |
||
| 941 | {
|
||
| 942 | // Since parsing will fail if charset is not specified in the xml prologue, |
||
| 943 | // the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that... |
||
| 944 | // The following code might be better for mb_string enabled installs, but |
||
| 945 | // makes the lib about 200% slower... |
||
| 946 | //if (!is_valid_charset($req_encoding, array('UTF-8')))
|
||
| 947 | if (!in_array($req_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($data)) {
|
||
| 948 | if ($req_encoding == 'ISO-8859-1') {
|
||
| 949 | $data = utf8_encode($data); |
||
| 950 | } else {
|
||
| 951 | if (extension_loaded('mbstring')) {
|
||
| 952 | $data = mb_convert_encoding($data, 'UTF-8', $req_encoding); |
||
| 953 | } else {
|
||
| 954 | error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $req_encoding);
|
||
| 955 | } |
||
| 956 | } |
||
| 957 | } |
||
| 958 | } |
||
| 959 | |||
| 960 | $parser = xml_parser_create(); |
||
| 961 | xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); |
||
| 962 | // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell |
||
| 963 | // the xml parser to give us back data in the expected charset |
||
| 964 | // What if internal encoding is not in one of the 3 allowed? |
||
| 965 | // we use the broadest one, ie. utf8 |
||
| 966 | // This allows to send data which is native in various charset, |
||
| 967 | // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding |
||
| 968 | if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
|
||
| 969 | {
|
||
| 970 | xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8'); |
||
| 971 | } |
||
| 972 | else |
||
| 973 | {
|
||
| 974 | xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']); |
||
| 975 | } |
||
| 976 | |||
| 977 | if ($this->functions_parameters_type != 'xmlrpcvals') |
||
| 978 | xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast'); |
||
| 979 | else |
||
| 980 | xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee'); |
||
| 981 | xml_set_character_data_handler($parser, 'xmlrpc_cd'); |
||
| 982 | xml_set_default_handler($parser, 'xmlrpc_dh'); |
||
| 983 | if(!xml_parse($parser, $data, 1)) |
||
| 984 | {
|
||
| 985 | // return XML error as a faultCode |
||
| 986 | $r=new xmlrpcresp(0, |
||
| 987 | $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser), |
||
| 988 | sprintf('XML error: %s at line %d, column %d',
|
||
| 989 | xml_error_string(xml_get_error_code($parser)), |
||
| 990 | xml_get_current_line_number($parser), xml_get_current_column_number($parser))); |
||
| 991 | xml_parser_free($parser); |
||
| 992 | } |
||
| 993 | elseif ($GLOBALS['_xh']['isf']) |
||
| 994 | {
|
||
| 995 | xml_parser_free($parser); |
||
| 996 | $r=new xmlrpcresp(0, |
||
| 997 | $GLOBALS['xmlrpcerr']['invalid_request'], |
||
| 998 | $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']); |
||
| 999 | } |
||
| 1000 | else |
||
| 1001 | {
|
||
| 1002 | xml_parser_free($parser); |
||
| 1003 | // small layering violation in favor of speed and memory usage: |
||
| 1004 | // we should allow the 'execute' method handle this, but in the |
||
| 1005 | // most common scenario (xmlrpcvals type server with some methods |
||
| 1006 | // registered as phpvals) that would mean a useless encode+decode pass |
||
| 1007 | if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals'))) |
||
| 1008 | {
|
||
| 1009 | if($this->debug > 1) |
||
| 1010 | {
|
||
| 1011 | $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
|
||
| 1012 | } |
||
| 1013 | $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']); |
||
| 1014 | } |
||
| 1015 | else |
||
| 1016 | {
|
||
| 1017 | // build an xmlrpcmsg object with data parsed from xml |
||
| 1018 | $m=new xmlrpcmsg($GLOBALS['_xh']['method']); |
||
| 1019 | // now add parameters in |
||
| 1020 | for($i=0; $i<count($GLOBALS['_xh']['params']); $i++) |
||
| 1021 | {
|
||
| 1022 | $m->addParam($GLOBALS['_xh']['params'][$i]); |
||
| 1023 | } |
||
| 1024 | |||
| 1025 | if($this->debug > 1) |
||
| 1026 | {
|
||
| 1027 | $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
|
||
| 1028 | } |
||
| 1029 | $r = $this->execute($m); |
||
| 1030 | } |
||
| 1031 | } |
||
| 1032 | return $r; |
||
| 1033 | } |
||
| 1034 | |||
| 1035 | /** |
||
| 1036 | * Execute a method invoked by the client, checking parameters used |
||
| 1037 | * @param mixed $m either an xmlrpcmsg obj or a method name |
||
| 1038 | * @param array $params array with method parameters as php types (if m is method name only) |
||
| 1039 | * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only) |
||
| 1040 | * @return xmlrpcresp |
||
| 1041 | * @access private |
||
| 1042 | */ |
||
| 1043 | function execute($m, $params=null, $paramtypes=null) |
||
| 1211 | } |
||
| 1212 | |||
| 1213 | /** |
||
| 1214 | * add a string to the 'internal debug message' (separate from 'user debug message') |
||
| 1215 | * @param string $string |
||
| 1216 | * @access private |
||
| 1217 | */ |
||
| 1218 | function debugmsg($string) |
||
| 1221 | } |
||
| 1222 | |||
| 1223 | /** |
||
| 1224 | * @access private |
||
| 1225 | */ |
||
| 1226 | function xml_header($charset_encoding='') |
||
| 1227 | {
|
||
| 1228 | if ($charset_encoding != '') |
||
| 1229 | {
|
||
| 1230 | return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n"; |
||
| 1231 | } |
||
| 1232 | else |
||
| 1233 | {
|
||
| 1234 | return "<?xml version=\"1.0\"?" . ">\n"; |
||
| 1235 | } |
||
| 1236 | } |
||
| 1237 | |||
| 1238 | /** |
||
| 1239 | * A debugging routine: just echoes back the input packet as a string value |
||
| 1240 | * @deprecated |
||
| 1241 | */ |
||
| 1242 | function echoInput() |
||
| 1246 | } |
||
| 1247 | } |
||
| 1248 | ?> |
||
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.