Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Unknown

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /phpxmlrpc/xmlrpcs.php

Documentation is available at xmlrpcs.php

  1. <?php
  2. // by Edd Dumbill (C) 1999-2002
  3. // $Id: xmlrpcs.inc,v 1.57 2006/03/20 13:48:25 ggiunta Exp $
  4.  
  5. // Copyright (c) 1999,2000,2002 Edd Dumbill.
  6. // All rights reserved.
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions
  10. // are met:
  11. //
  12. //        * Redistributions of source code must retain the above copyright
  13. //            notice, this list of conditions and the following disclaimer.
  14. //
  15. //        * Redistributions in binary form must reproduce the above
  16. //            copyright notice, this list of conditions and the following
  17. //            disclaimer in the documentation and/or other materials provided
  18. //            with the distribution.
  19. //
  20. //        * Neither the name of the "XML-RPC for PHP" nor the names of its
  21. //            contributors may be used to endorse or promote products derived
  22. //            from this software without specific prior written permission.
  23. //
  24. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  35. // OF THE POSSIBILITY OF SUCH DAMAGE.
  36.  
  37.     // XML RPC Server class
  38.     // requires: xmlrpc.inc
  39.  
  40.     // listMethods: signature was either a string, or nothing.
  41.     // The useless string variant has been removed
  42.         $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
  43.     $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
  44.     function _xmlrpcs_listMethods($server$m=null// if called in plain php values mode, second param is missing
  45.         {
  46.  
  47.         $outAr=array();
  48.         foreach($server->dmap as $key => $val)
  49.         {
  50.             $outAr[]=&new xmlrpcval($key'string');
  51.         }
  52.         if($server->allow_system_funcs)
  53.         {
  54.             foreach($GLOBALS['_xmlrpcs_dmap'as $key => $val)
  55.             {
  56.                 $outAr[]=&new xmlrpcval($key'string');
  57.             }
  58.         }
  59.         $v=&new xmlrpcval($outAr'array');
  60.         return new xmlrpcresp($v);
  61.     }
  62.  
  63.     $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray']$GLOBALS['xmlrpcString']));
  64.     $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
  65.     function _xmlrpcs_methodSignature($server$m)
  66.     {
  67.         // let accept as parameter both an xmlrpcval or string
  68.         if (is_object($m))
  69.         {
  70.             $methName=$m->getParam(0);
  71.             $methName=$methName->scalarval();
  72.         }
  73.         else
  74.         {
  75.             $methName=$m;
  76.         }
  77.         if(ereg("^system\."$methName))
  78.         {
  79.             $dmap=$GLOBALS['_xmlrpcs_dmap']$sysCall=1;
  80.         }
  81.         else
  82.         {
  83.             $dmap=$server->dmap$sysCall=0;
  84.         }
  85.         //    print "<!-- ${methName} -->\n";
  86.         if(isset($dmap[$methName]))
  87.         {
  88.             if(isset($dmap[$methName]['signature']))
  89.             {
  90.                 $sigs=array();
  91.                 foreach($dmap[$methName]['signature'as $inSig)
  92.                 {
  93.                     $cursig=array();
  94.                     foreach($inSig as $sig)
  95.                     {
  96.                         $cursig[]=&new xmlrpcval($sig'string');
  97.                     }
  98.                     $sigs[]=&new xmlrpcval($cursig'array');
  99.                 }
  100.                 $r=&new xmlrpcresp(new xmlrpcval($sigs'array'));
  101.             }
  102.             else
  103.             {
  104.                 // NB: according to the official docs, we should be returning a
  105.                 // "none-array" here, which means not-an-array
  106.                 $r=&new xmlrpcresp(new xmlrpcval('undef''string'));
  107.             }
  108.         }
  109.         else
  110.         {
  111.             $r=&new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown']$GLOBALS['xmlrpcstr']['introspect_unknown']);
  112.         }
  113.         return $r;
  114.     }
  115.  
  116.     $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString']$GLOBALS['xmlrpcString']));
  117.     $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
  118.     function _xmlrpcs_methodHelp($server$m)
  119.     {
  120.         // let accept as parameter both an xmlrpcval or string
  121.         if (is_object($m))
  122.         {
  123.             $methName=$m->getParam(0);
  124.             $methName=$methName->scalarval();
  125.         }
  126.         else
  127.         {
  128.             $methName=$m;
  129.         }
  130.         if(ereg("^system\."$methName))
  131.         {
  132.             $dmap=$GLOBALS['_xmlrpcs_dmap']$sysCall=1;
  133.         }
  134.         else
  135.         {
  136.             $dmap=$server->dmap$sysCall=0;
  137.         }
  138.         // print "<!-- ${methName} -->\n";
  139.         if(isset($dmap[$methName]))
  140.         {
  141.             if(isset($dmap[$methName]['docstring']))
  142.             {
  143.                 $r=&new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring'])'string');
  144.             }
  145.             else
  146.             {
  147.                 $r=&new xmlrpcresp(new xmlrpcval('''string'));
  148.             }
  149.         }
  150.         else
  151.         {
  152.             $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['introspect_unknown']$GLOBALS['xmlrpcstr']['introspect_unknown']);
  153.         }
  154.         return $r;
  155.     }
  156.  
  157.     $_xmlrpcs_multicall_sig array(array($GLOBALS['xmlrpcArray']$GLOBALS['xmlrpcArray']));
  158.     $_xmlrpcs_multicall_doc 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
  159.  
  160.     function _xmlrpcs_multicall_error($err)
  161.     {
  162.         if(is_string($err))
  163.         {
  164.             $str $GLOBALS['xmlrpcstr']["multicall_${err}"];
  165.             $code $GLOBALS['xmlrpcerr']["multicall_${err}"];
  166.         }
  167.         else
  168.         {
  169.             $code $err->faultCode();
  170.             $str $err->faultString();
  171.         }
  172.         $struct array();
  173.         $struct['faultCode'new xmlrpcval($code'int');
  174.         $struct['faultString'new xmlrpcval($str'string');
  175.         return new xmlrpcval($struct'struct');
  176.     }
  177.  
  178.     function _xmlrpcs_multicall_do_call($server$call)
  179.     {
  180.         if($call->kindOf(!= 'struct')
  181.         {
  182.             return _xmlrpcs_multicall_error('notstruct');
  183.         }
  184.         $methName @$call->structmem('methodName');
  185.         if(!$methName)
  186.         {
  187.             return _xmlrpcs_multicall_error('nomethod');
  188.         }
  189.         if($methName->kindOf(!= 'scalar' || $methName->scalartyp(!= 'string')
  190.         {
  191.             return _xmlrpcs_multicall_error('notstring');
  192.         }
  193.         if($methName->scalarval(== 'system.multicall')
  194.         {
  195.             return _xmlrpcs_multicall_error('recursion');
  196.         }
  197.  
  198.         $params @$call->structmem('params');
  199.         if(!$params)
  200.         {
  201.             return _xmlrpcs_multicall_error('noparams');
  202.         }
  203.         if($params->kindOf(!= 'array')
  204.         {
  205.             return _xmlrpcs_multicall_error('notarray');
  206.         }
  207.         $numParams $params->arraysize();
  208.  
  209.         $msg new xmlrpcmsg($methName->scalarval());
  210.         for($i 0$i $numParams$i++)
  211.         {
  212.             if(!$msg->addParam($params->arraymem($i)))
  213.             {
  214.                 $i++;
  215.                 return _xmlrpcs_multicall_error(new xmlrpcresp(0,
  216.                     $GLOBALS['xmlrpcerr']['incorrect_params'],
  217.                     $GLOBALS['xmlrpcstr']['incorrect_params'": probable xml error in param " $i));
  218.             }
  219.         }
  220.  
  221.         $result $server->execute($msg);
  222.  
  223.         if($result->faultCode(!= 0)
  224.         {
  225.             return _xmlrpcs_multicall_error($result);        // Method returned fault.
  226.         }
  227.  
  228.         return new xmlrpcval(array($result->value())'array');
  229.     }
  230.  
  231.     function _xmlrpcs_multicall_do_call_phpvals($server$call)
  232.     {
  233.         if(!is_array($call))
  234.         {
  235.             return _xmlrpcs_multicall_error('notstruct');
  236.         }
  237.         if(!array_key_exists('methodName'$call))
  238.         {
  239.             return _xmlrpcs_multicall_error('nomethod');
  240.         }
  241.         if (!is_string($call['methodName']))
  242.         {
  243.             return _xmlrpcs_multicall_error('notstring');
  244.         }
  245.         if($call['methodName'== 'system.multicall')
  246.         {
  247.             return _xmlrpcs_multicall_error('recursion');
  248.         }
  249.         if(!array_key_exists('params'$call))
  250.         {
  251.             return _xmlrpcs_multicall_error('noparams');
  252.         }
  253.         if(!is_array($call['params']))
  254.         {
  255.             return _xmlrpcs_multicall_error('notarray');
  256.         }
  257.  
  258.         // this is a real dirty and simplistic hack, since we might have received a
  259.         // base64 or datetime values, but they will be listed as strings here...
  260.         $numParams count($call['params']);
  261.         $pt array();
  262.         foreach($call['params'as $val)
  263.             $pt[php_2_xmlrpc_type(gettype($val));
  264.  
  265.         $result $server->execute($call['methodName']$call['params']$pt);
  266.  
  267.         if($result->faultCode(!= 0)
  268.         {
  269.             return _xmlrpcs_multicall_error($result);        // Method returned fault.
  270.         }
  271.  
  272.         return new xmlrpcval(array($result->value())'array');
  273.     }
  274.  
  275.     function _xmlrpcs_multicall($server$m)
  276.     {
  277.         $result array();
  278.         // let accept a plain list of php parameters, beside a single xmlrpc msg object
  279.         if (is_object($m))
  280.         {
  281.             $calls $m->getParam(0);
  282.             $numCalls $calls->arraysize();
  283.             for($i 0$i $numCalls$i++)
  284.             {
  285.                 $call $calls->arraymem($i);
  286.                 $result[$i_xmlrpcs_multicall_do_call($server$call);
  287.             }
  288.         }
  289.         else
  290.         {
  291.             //$calls = func_get_args();
  292.             $numCalls=count($m);
  293.             for($i 0$i $numCalls$i++)
  294.             {
  295.                 $result[$i_xmlrpcs_multicall_do_call_phpvals($server$m[$i]);
  296.             }
  297.         }
  298.  
  299.         return new xmlrpcresp(new xmlrpcval($result'array'));
  300.     }
  301.  
  302.     $GLOBALS['_xmlrpcs_dmap']=array(
  303.         'system.listMethods' => array(
  304.             'function' => '_xmlrpcs_listMethods',
  305.             'signature' => $_xmlrpcs_listMethods_sig,
  306.             'docstring' => $_xmlrpcs_listMethods_doc),
  307.         'system.methodHelp' => array(
  308.             'function' => '_xmlrpcs_methodHelp',
  309.             'signature' => $_xmlrpcs_methodHelp_sig,
  310.             'docstring' => $_xmlrpcs_methodHelp_doc),
  311.         'system.methodSignature' => array(
  312.             'function' => '_xmlrpcs_methodSignature',
  313.             'signature' => $_xmlrpcs_methodSignature_sig,
  314.             'docstring' => $_xmlrpcs_methodSignature_doc),
  315.         'system.multicall' => array(
  316.             'function' => '_xmlrpcs_multicall',
  317.             'signature' => $_xmlrpcs_multicall_sig,
  318.             'docstring' => $_xmlrpcs_multicall_doc
  319.         )
  320.     );
  321.  
  322.     $GLOBALS['_xmlrpcs_occurred_errors''';
  323.     $GLOBALS['_xmlrpcs_prev_ehandler''';
  324.     /**
  325.     * Error handler used to track errors that occur during server-side execution of PHP code.
  326.     * This allows to report back to the client whether an internal error has occurred or not
  327.     * using an xmlrpc response object, instead of letting the client deal with the html junk
  328.     * that a PHP execution error on the server generally entails.
  329.     *
  330.     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
  331.     *
  332.     */
  333.     function _xmlrpcs_errorHandler($errcode$errstring$filename=null$lineno=null$context=null)
  334.     {
  335.         //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
  336.         if($errcode != 2048// do not use E_STRICT by name, since on PHP 4 it will not be defined
  337.         {
  338.             $GLOBALS['_xmlrpcs_occurred_errors'$GLOBALS['_xmlrpcs_occurred_errors'$errstring "\n";
  339.         }
  340.         // Try to avoid as much as possible disruption to the previous error handling
  341.         // mechanism in place
  342.         if($GLOBALS['_xmlrpcs_prev_ehandler'== '')
  343.         {
  344.             // The previous error handler was the default: all we should do is log error
  345.             // to the default error log (if level high enough)
  346.             if(ini_get('log_errors'&& (intval(ini_get('error_reporting')) $errcode))
  347.             {
  348.                 error_log($errstring);
  349.             }
  350.         }
  351.         else
  352.         {
  353.             // Pass control on to previous error handler, trying to avoid loops...
  354.             if($GLOBALS['_xmlrpcs_prev_ehandler'!= '_xmlrpcs_errorHandler')
  355.             {
  356.                 // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
  357.                 if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
  358.                 {
  359.                     $GLOBALS['_xmlrpcs_prev_ehandler'][0]->$GLOBALS['_xmlrpcs_prev_ehandler'][1]($errcode$errstring$filename$lineno$context);
  360.                 }
  361.                 else
  362.                 {
  363.                     $GLOBALS['_xmlrpcs_prev_ehandler']($errcode$errstring$filename$lineno$context);
  364.                 }
  365.             }
  366.         }
  367.     }
  368.  
  369.     $GLOBALS['_xmlrpc_debuginfo']='';
  370.  
  371.     /**
  372.     * Add a string to the debug info that can be later seralized by the server
  373.     * as part of the response message.
  374.     * Note that for best compatbility, the debug string should be encoded using
  375.     * the $GLOBALS['xmlrpc_internalencoding'] character set.
  376.     * @param string $m 
  377.     * @access public
  378.     */
  379.     function xmlrpc_debugmsg($m)
  380.     {
  381.         $GLOBALS['_xmlrpc_debuginfo'.= $m "\n";
  382.     }
  383.  
  384.     class xmlrpc_server
  385.     {
  386.         /// array defining php functions exposed as xmlrpc methods by this server
  387.                 var $dmap=array();
  388.         /**
  389.         * Defines how functions in dmap will be invokde: either using an xmlrpc msg object
  390.         * or plain php values.
  391.         * valid strings are 'xmlrpcvals' or 'phpvals'
  392.         */
  393.         var $functions_parameters_type='xmlrpcvals';
  394.         /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
  395.                 var $debug = 1;
  396.         /**
  397.         * When set to true, it will enable HTTP compression of the response, in case
  398.         * the client has declared its support for compression in the request.
  399.         */
  400.         var $compress_response = false;
  401.         /**
  402.         * List of http compression methods accepted by the server for requests.
  403.         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
  404.         */
  405.         var $accepted_compression = array();
  406.         /// shall we serve calls to system.* methods?
  407.                 var $allow_system_funcs = true;
  408.         /// list of charset encodings natively accepted for requests
  409.                 var $accepted_charset_encodings = array();
  410.         /**
  411.         * charset encoding to be used for response.
  412.         * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
  413.         * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
  414.         * null (leave unspecified in response, convert output stream to US_ASCII),
  415.         * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
  416.         * 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).
  417.         * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
  418.         */
  419.         var $response_charset_encoding = 'UTF-8';
  420.         /// storage for internal debug info
  421.                 var $debug_info = '';
  422.  
  423.         /**
  424.         * @param array $dispmap the dispatch map withd efinition of exposed services
  425.         * @param boolean $servicenow set to false to prevent the server from runnung upon construction
  426.         */
  427.         function xmlrpc_server($dispMap=null$serviceNow=true)
  428.         {
  429.             // if ZLIB is enabled, let the server by default accept compressed requests,
  430.             // and compress responses sent to clients that support them
  431.             if(function_exists('gzinflate'))
  432.             {
  433.                 $this->accepted_compression = array('gzip''deflate');
  434.                 $this->compress_response = true;
  435.             }
  436.  
  437.             // by default the xml parser can support these 3 charset encodings
  438.             $this->accepted_charset_encodings = array('UTF-8''ISO-8859-1''US-ASCII');
  439.  
  440.             // dispMap is a dispatch array of methods
  441.             // mapped to function names and signatures
  442.             // if a method
  443.             // doesn't appear in the map then an unknown
  444.             // method error is generated
  445.             /* milosch - changed to make passing dispMap optional.
  446.              * instead, you can use the class add_to_map() function
  447.              * to add functions manually (borrowed from SOAPX4)
  448.              */
  449.             if($dispMap)
  450.             {
  451.                 $this->dmap = $dispMap;
  452.                 if($serviceNow)
  453.                 {
  454.                     $this->service();
  455.                 }
  456.             }
  457.         }
  458.  
  459.         /**
  460.         * Set debug level of server.
  461.         * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
  462.         *  0 = no debug info,
  463.         *  1 = msgs set from user with debugmsg(),
  464.         *  2 = add complete xmlrpc request (headers and body),
  465.         *  3 = add also all processing warnings happened during method processing
  466.         *  (NB: this involves setting a custom error handler, and might interfere
  467.         *  with the standard processing of the php function exposed as method. In
  468.         *  particular, triggering an USER_ERROR level error will not halt script
  469.         *  execution anymore, but just end up logged in the xmlrpc response)
  470.         *  Note that info added at elevel 2 and 3 will be base64 encoded
  471.         */
  472.         function setDebug($in)
  473.         {
  474.             $this->debug=$in;
  475.         }
  476.  
  477.         /**
  478.         * Return a string with the serialized representation of all debug info
  479.         * @param string $charset_encoding the target charset encoding for the serialization
  480.         * @return string an XML comment (or two)
  481.         */
  482.         function serializeDebug($charset_encoding='')
  483.         {
  484.             // Tough encoding problem: which internal charset should we assume for debug info?
  485.             // It might contain a copy of raw data received from client, ie with unknown encoding,
  486.             // intermixed with php generated data and user generated data...
  487.             // so we split it: system debug is base 64 encoded,
  488.             // user debug info should be encoded by the end user using the INTERNAL_ENCODING
  489.             $out '';
  490.             if ($this->debug_info != ''{
  491.                 $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
  492.             }
  493.             if($GLOBALS['_xmlrpc_debuginfo']!='')
  494.             {
  495.  
  496.                 $out .= "<!-- DEBUG INFO:\n" xmlrpc_encode_entitites(str_replace('--''_-'$GLOBALS['_xmlrpc_debuginfo'])$GLOBALS['xmlrpc_internalencoding']$charset_encoding"\n-->\n";
  497.                 // NB: a better solution MIGHT be to use CDATA, but we need to insert it
  498.                 // into return payload AFTER the beginning tag
  499.                 //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
  500.             }
  501.             return $out;
  502.         }
  503.  
  504.         /**
  505.         * Execute the xmlrpc request, printing the response
  506.         * @param string $data the request body. If null, the http POST request will be examined
  507.         */
  508.         function service($data=null)
  509.         {
  510.             if ($data === null{
  511.                 $data = isset($GLOBALS['HTTP_RAW_POST_DATA']$GLOBALS['HTTP_RAW_POST_DATA''';
  512.             }
  513.  
  514.             // reset internal debug info
  515.             $this->debug_info = '';
  516.  
  517.             // Echo back what we received, before parsing it
  518.             if($this->debug > 1)
  519.             {
  520.                 $this->debugmsg("+++GOT+++\n" $data "\n+++END+++");
  521.             }
  522.  
  523.             $r $this->parseRequestHeaders($data$req_charset$resp_charset$resp_encoding);
  524.             if (!$r)
  525.             {
  526.                 $r=$this->parseRequest($data$req_charset);
  527.             }
  528.  
  529.             if($this->debug > && $GLOBALS['_xmlrpcs_occurred_errors'])
  530.             {
  531.                 $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
  532.                     $GLOBALS['_xmlrpcs_occurred_errors'"+++END+++");
  533.             }
  534.  
  535.             //$payload='<?xml version="1.0" encoding="' . $GLOBALS['xmlrpc_defencoding'] . '"?' . '>' . "\n"
  536.  
  537.             $payload=$this->xml_header($resp_charset);
  538.             if($this->debug > 0)
  539.             {
  540.  
  541.                 //$payload = $payload . "<methodResponse>\n" . $this->serializeDebug();
  542.                 //$payload = $payload . substr($r->serialize(), 17);
  543.                 $payload $payload $this->serializeDebug($resp_charset);
  544.             }
  545.             //else
  546.             //{
  547.             // G. Giunta 2006-01-27: do not create response serialization if it has
  548.             // already happened. Helps building json magic
  549.             if (empty($r->payload))
  550.             {
  551.                 $r->serialize($resp_charset);
  552.             }
  553.             $payload $payload $r->payload;
  554.             //}
  555.  
  556.  
  557.             // if we get a warning/error that has output some text before here, then we cannot
  558.             // add a new header. We cannot say we are sending xml, either...
  559.             if(!headers_sent())
  560.             {
  561.                 header('Content-Type: '.$r->content_type);
  562.                 // we do not know if client actually told us an accepted charset, but if he did
  563.                 // we have to tell him what we did
  564.                 header("Vary: Accept-Charset");
  565.  
  566.                 // http compression of output
  567.                 if($this->compress_response && function_exists('gzencode'&& $resp_encoding != '')
  568.                 {
  569.                     if(strstr($resp_encoding'gzip'))
  570.                     {
  571.                         $payload gzencode($payload);
  572.                         header("Content-Encoding: gzip");
  573.                         header("Vary: Accept-Encoding");
  574.                     }
  575.                     elseif (strstr($resp_encoding'deflate'))
  576.                     {
  577.                         $payload gzdeflate($payload);
  578.                         header("Content-Encoding: deflate");
  579.                         header("Vary: Accept-Encoding");
  580.                     }
  581.                 }
  582.  
  583.                 header('Content-Length: ' . (int)strlen($payload));
  584.             }
  585.             else
  586.             {
  587.                 //print "Internal server error: headers sent before PHP response"
  588.                 error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages');
  589.             }
  590.  
  591.             print $payload;
  592.         }
  593.  
  594.         /**
  595.         * Add a method to the dispatch map
  596.         * @param string $methodname the name with which the method will be made available
  597.         * @param string $function the php function that will get invoked
  598.         * @param array $sig the array of valid method signatures
  599.         * @param string $doc method documentation
  600.         */
  601.         function add_to_map($methodname,$function,$sig,$doc='')
  602.         {
  603.             $this->dmap[$methodnamearray(
  604.                 'function'    => $function,
  605.                 'signature' => $sig,
  606.                 'docstring' => $doc
  607.             );
  608.         }
  609.  
  610.         /**
  611.         * Verify type and number of parameters received against a list of known signatures
  612.         * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
  613.         * @param array $sig array of known signatures to match against
  614.         * @access private
  615.         */
  616.         function verifySignature($in$sig)
  617.         {
  618.             // check each possible signature in turn
  619.             if (is_object($in))
  620.             {
  621.                 $numParams $in->getNumParams();
  622.             }
  623.             else
  624.             {
  625.                 $numParams sizeof($in);
  626.             }
  627.             foreach($sig as $cursig)
  628.             {
  629.                 if(sizeof($cursig)==$numParams+1)
  630.                 {
  631.                     $itsOK=1;
  632.                     for($n=0$n<$numParams$n++)
  633.                     {
  634.                         if (is_object($in))
  635.                         {
  636.                             $p=$in->getParam($n);
  637.                             if($p->kindOf(== 'scalar')
  638.                             {
  639.                                 $pt=$p->scalartyp();
  640.                             }
  641.                             else
  642.                             {
  643.                                 $pt=$p->kindOf();
  644.                             }
  645.                         }
  646.                         else
  647.                         {
  648.                             $pt$in[$n== 'i4' 'int' $in[$n]// dispatch maps never use i4...
  649.                         }
  650.  
  651.                         // param index is $n+1, as first member of sig is return type
  652.                         if($pt != $cursig[$n+1&& $cursig[$n+1!= $GLOBALS['xmlrpcValue'])
  653.                         {
  654.                             $itsOK=0;
  655.                             $pno=$n+1;
  656.                             $wanted=$cursig[$n+1];
  657.                             $got=$pt;
  658.                             break;
  659.                         }
  660.                     }
  661.                     if($itsOK)
  662.                     {
  663.                         return array(1,'');
  664.                     }
  665.                 }
  666.             }
  667.             if(isset($wanted))
  668.             {
  669.                 return array(0"Wanted ${wanted}got ${got} at param ${pno}");
  670.             }
  671.             else
  672.             {
  673.                 return array(0"No method signature matches number of parameters");
  674.             }
  675.         }
  676.  
  677.         /**
  678.         * Parse http headers received along with xmlrpc request. If needed, inflate request
  679.         * @return null on success or an xmlrpcresp
  680.         * @access private
  681.         */
  682.         function parseRequestHeaders(&$data&$req_encoding&$resp_encoding&$resp_compression)
  683.         {
  684.             // Play nice to PHP 4.0.x: superglobals were not yet invented...
  685.             if(!isset($_SERVER))
  686.             {
  687.                 $_SERVER $GLOBALS['HTTP_SERVER_VARS'];
  688.             }
  689.  
  690.             if($this->debug > 1)
  691.             {
  692.                 if(function_exists('getallheaders'))
  693.                 {
  694.                     $this->debugmsg('')// empty line
  695.                     foreach(getallheaders(as $name => $val)
  696.                     {
  697.                         $this->debugmsg("HEADER$name$val");
  698.                     }
  699.                 }
  700.  
  701.             }
  702.  
  703.             if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
  704.             {
  705.                 $content_encoding $_SERVER['HTTP_CONTENT_ENCODING'];
  706.             }
  707.             else
  708.             {
  709.                 $content_encoding '';
  710.             }
  711.  
  712.             // check if request body has been compressed and decompress it
  713.             if($content_encoding != '' && strlen($data))
  714.             {
  715.                 if($content_encoding == 'deflate' || $content_encoding == 'gzip')
  716.                 {
  717.                     // if decoding works, use it. else assume data wasn't gzencoded
  718.                     if(function_exists('gzinflate'&& in_array($content_encoding$this->accepted_compression))
  719.                     {
  720.                         if($content_encoding == 'deflate' && $degzdata @gzinflate($data))
  721.                         {
  722.                             $data $degzdata;
  723.                             if($this->debug > 1)
  724.                             {
  725.                                 $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" $data "\n+++END+++");
  726.                             }
  727.                         }
  728.                         elseif($content_encoding == 'gzip' && $degzdata @gzinflate(substr($data10)))
  729.                         {
  730.                             $data $degzdata;
  731.                             if($this->debug > 1)
  732.                                 $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" $data "\n+++END+++");
  733.                         }
  734.                         else
  735.                         {
  736.                             $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['server_decompress_fail']$GLOBALS['xmlrpcstr']['server_decompress_fail']);
  737.                             return $r;
  738.                         }
  739.                     }
  740.                     else
  741.                     {
  742.                         //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
  743.                         $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['server_cannot_decompress']$GLOBALS['xmlrpcstr']['server_cannot_decompress']);
  744.                         return $r;
  745.                     }
  746.                 }
  747.             }
  748.  
  749.             // check if client specified accepted charsets, and if we know how to fulfill
  750.             // the request
  751.             if ($this->response_charset_encoding == 'auto')
  752.             {
  753.                 $resp_encoding '';
  754.                 if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
  755.                 {
  756.                     // here we should check if we can match the client-requested encoding
  757.                     // with the encodings we know we can generate.
  758.                     /// @todo we should parse q=0.x preferences instead of getting first charset specified...
  759.                     $client_accepted_charsets explode(','strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
  760.                     // Give preference to internal encoding
  761.                     $known_charsets array($this->internal_encoding'UTF-8''ISO-8859-1''US-ASCII');
  762.                     foreach ($known_charsets as $charset)
  763.                     {
  764.                         foreach ($client_accepted_charsets as $accepted)
  765.                             if (strpos($accepted$charset=== 0)
  766.                             {
  767.                                 $resp_encoding $charset;
  768.                                 break;
  769.                             }
  770.                         if ($resp_encoding)
  771.                             break;
  772.                     }
  773.                 }
  774.             }
  775.             else
  776.             {
  777.                 $resp_encoding $this->response_charset_encoding;
  778.             }
  779.  
  780.             if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
  781.             {
  782.                 $resp_compression $_SERVER['HTTP_ACCEPT_ENCODING'];
  783.             }
  784.             else
  785.             {
  786.                 $resp_compression '';
  787.             }
  788.  
  789.             // 'guestimate' request encoding
  790.             /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
  791.             $req_encoding guess_encoding(isset($_SERVER['CONTENT_TYPE']$_SERVER['CONTENT_TYPE''',
  792.                 $data);
  793.  
  794.             return null;
  795.         }
  796.  
  797.         /**
  798.         * Parse an xml chunk containing an xmlrpc request and execute the corresponding
  799.         * php function registered with the server
  800.         * @param string $data the xml request
  801.         * @param string $req_encoding (optional) the charset encoding of the xml request
  802.         * @return xmlrpcresp 
  803.         * @access private
  804.         */
  805.         function parseRequest($data$req_encoding='')
  806.         {
  807.             // 2005/05/07 commented and moved into caller function code
  808.             //if($data=='')
  809.             //{
  810.             //    $data=$GLOBALS['HTTP_RAW_POST_DATA'];
  811.             //}
  812.  
  813.  
  814.             // G. Giunta 2005/02/13: we do NOT expect to receive html entities
  815.             // so we do not try to convert them into xml character entities
  816.             //$data = xmlrpc_html_entity_xlate($data);
  817.  
  818.             $GLOBALS['_xh']=array();
  819.             $GLOBALS['_xh']['isf']=0;
  820.             $GLOBALS['_xh']['isf_reason']='';
  821.             $GLOBALS['_xh']['params']=array();
  822.             $GLOBALS['_xh']['pt']=array();
  823.             $GLOBALS['_xh']['stack']=array();
  824.             $GLOBALS['_xh']['valuestack'array();
  825.             $GLOBALS['_xh']['method']='';
  826.  
  827.             // decompose incoming XML into request structure
  828.             if ($req_encoding != '')
  829.             {
  830.                 if (!in_array($req_encodingarray('UTF-8''ISO-8859-1''US-ASCII')))
  831.                 // the following code might be better for mb_string enabled installs, but
  832.                 // makes the lib about 200% slower...
  833.                 //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
  834.                 {
  835.                     error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding);
  836.                     $req_encoding $GLOBALS['xmlrpc_defencoding'];
  837.                 }
  838.                 /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
  839.                 // the encoding is not UTF8 and there are non-ascii chars in the text...
  840.                 $parser xml_parser_create($req_encoding);
  841.             }
  842.             else
  843.             {
  844.                 $parser xml_parser_create();
  845.             }
  846.  
  847.             xml_parser_set_option($parserXML_OPTION_CASE_FOLDINGtrue);
  848.             // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
  849.             // the xml parser to give us back data in the expected charset
  850.             xml_parser_set_option($parserXML_OPTION_TARGET_ENCODING$GLOBALS['xmlrpc_internalencoding']);
  851.  
  852.             if ($this->functions_parameters_type == 'phpvals')
  853.                 xml_set_element_handler($parser'xmlrpc_se''xmlrpc_ee_fast');
  854.             else
  855.                 xml_set_element_handler($parser'xmlrpc_se''xmlrpc_ee');
  856.             xml_set_character_data_handler($parser'xmlrpc_cd');
  857.             xml_set_default_handler($parser'xmlrpc_dh');
  858.             if(!xml_parse($parser$data1))
  859.             {
  860.                 // return XML error as a faultCode
  861.                 $r=&new xmlrpcresp(0,
  862.                 $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
  863.                 sprintf('XML error: %s at line %d, column %d',
  864.                     xml_error_string(xml_get_error_code($parser)),
  865.                     xml_get_current_line_number($parser)xml_get_current_column_number($parser)));
  866.                 xml_parser_free($parser);
  867.             }
  868.             elseif ($GLOBALS['_xh']['isf'])
  869.             {
  870.                 xml_parser_free($parser);
  871.                 $r=&new xmlrpcresp(0,
  872.                     $GLOBALS['xmlrpcerr']['invalid_request'],
  873.                     $GLOBALS['xmlrpcstr']['invalid_request'' ' $GLOBALS['_xh']['isf_reason']);
  874.             }
  875.             else
  876.             {
  877.                 xml_parser_free($parser);
  878.                 if ($this->functions_parameters_type == 'phpvals')
  879.                 {
  880.                     if($this->debug > 1)
  881.                     {
  882.                         $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params']true)."\n+++END+++");
  883.                     }
  884.                     $r $this->execute($GLOBALS['_xh']['method']$GLOBALS['_xh']['params']$GLOBALS['_xh']['pt']);
  885.                 }
  886.                 else
  887.                 {
  888.                     // build an xmlrpcmsg object with data parsed from xml
  889.                     $m=&new xmlrpcmsg($GLOBALS['_xh']['method']);
  890.                     // now add parameters in
  891.                     for($i=0$i<sizeof($GLOBALS['_xh']['params'])$i++)
  892.                     {
  893.                         $m->addParam($GLOBALS['_xh']['params'][$i]);
  894.                     }
  895.  
  896.                     if($this->debug > 1)
  897.                     {
  898.                         $this->debugmsg("\n+++PARSED+++\n".var_export($mtrue)."\n+++END+++");
  899.                     }
  900.  
  901.                     $r $this->execute($m);
  902.                 }
  903.             }
  904.             return $r;
  905.         }
  906.  
  907.         /**
  908.         * Execute a method invoked by the client, checking parameters used
  909.         * @param mixed $m either an xmlrpcmsg obj or a method name
  910.         * @param array $params array with method parameters as php types (if m is method name only)
  911.         * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
  912.         * @return xmlrpcresp 
  913.         */
  914.         function execute($m$params=null$paramtypes=null)
  915.         {
  916.             if (is_object($m))
  917.             {
  918.                 $methName $m->method();
  919.             }
  920.             else
  921.             {
  922.                 $methName $m;
  923.             }
  924.             $sysCall $this->allow_system_funcs && ereg("^system\."$methName);
  925.             $dmap $sysCall $GLOBALS['_xmlrpcs_dmap'$this->dmap;
  926.  
  927.             if(!isset($dmap[$methName]['function']))
  928.             {
  929.                 // No such method
  930.                 return new xmlrpcresp(0,
  931.                     $GLOBALS['xmlrpcerr']['unknown_method'],
  932.                     $GLOBALS['xmlrpcstr']['unknown_method']);
  933.             }
  934.  
  935.             // Check signature
  936.             if(isset($dmap[$methName]['signature']))
  937.             {
  938.                 $sig $dmap[$methName]['signature'];
  939.                 if (is_object($m))
  940.                 {
  941.                     list($ok$errstr$this->verifySignature($m$sig);
  942.                 }
  943.                 else
  944.                 {
  945.                 list($ok$errstr$this->verifySignature($paramtypes$sig);
  946.                 }
  947.                 if(!$ok)
  948.                 {
  949.                     // Didn't match.
  950.                     return new xmlrpcresp(
  951.                         0,
  952.                         $GLOBALS['xmlrpcerr']['incorrect_params'],
  953.                         $GLOBALS['xmlrpcstr']['incorrect_params'": ${errstr}"
  954.                     );
  955.                 }
  956.             }
  957.  
  958.             $func $dmap[$methName]['function'];
  959.             // let the 'class::function' syntax be accepted in dispatch maps
  960.             if(is_string($func&& strpos($func'::'))
  961.             {
  962.                 $func explode('::'$func);
  963.             }
  964.             // verify that function to be invoked is in fact callable
  965.             if(!is_callable($func))
  966.             {
  967.                 error_log("XML-RPCxmlrpc_server::executefunction $func registered as method handler is not callable");
  968.                 return new xmlrpcresp(
  969.                     0,
  970.                     $GLOBALS['xmlrpcerr']['server_error'],
  971.                     $GLOBALS['xmlrpcstr']['server_error'": no function matches method"
  972.                 );
  973.             }
  974.  
  975.             // If debug level is 3, we should catch all errors generated during
  976.             // processing of user function, and log them as part of response
  977.             if($this->debug > 2)
  978.             {
  979.                 $GLOBALS['_xmlrpcs_prev_ehandler'set_error_handler('_xmlrpcs_errorHandler');
  980.             }
  981.             if (is_object($m))
  982.             {
  983.                 if($sysCall)
  984.                 {
  985.                     $r call_user_func($func$this$m);
  986.                 }
  987.                 else
  988.                 {
  989.                     $r call_user_func($func$m);
  990.                 }
  991.                 if (!is_a($r'xmlrpcresp'))
  992.                 {
  993.                     error_log("XML-RPCxmlrpc_server::executefunction $func registered as method handler does not return an xmlrpcresp object");
  994.                     if (is_a($r'xmlrpcval'))
  995.                     {
  996.                         $r new xmlrpcresp($r);
  997.                     }
  998.                     else
  999.                     {
  1000.                         $r new xmlrpcresp(
  1001.                             0,
  1002.                             $GLOBALS['xmlrpcerr']['server_error'],
  1003.                             $GLOBALS['xmlrpcstr']['server_error'": function does not return xmlrpcresp object"
  1004.                         );
  1005.                     }
  1006.                 }
  1007.             }
  1008.             else
  1009.             {
  1010.                 // call a 'plain php' function
  1011.                 if($sysCall)
  1012.                 {
  1013.                     array_unshift($params$this);
  1014.                     $r call_user_func_array($func,$params);
  1015.                 }
  1016.                 else
  1017.                 {
  1018.                     $r call_user_func_array($func$params);
  1019.                 }
  1020.                 // the return type can be either an xmlrpcresp object or a plain php value...
  1021.                 if (!is_a($r'xmlrpcresp'))
  1022.                 {
  1023.                     // what should we assume here about automatic encoding of datetimes
  1024.                     // and php classes instances???
  1025.                     $r new xmlrpcresp(php_xmlrpc_encode($rarray('auto_dates')));
  1026.                 }
  1027.             }
  1028.             if($this->debug > 2)
  1029.             {
  1030.                 // note: restore the error handler we found before calling the
  1031.                 // user func, even if it has been changed inside the func itself
  1032.                 if($GLOBALS['_xmlrpcs_prev_ehandler'])
  1033.                 {
  1034.                     set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
  1035.                 }
  1036.                 else
  1037.                 {
  1038.                     restore_error_handler();
  1039.                 }
  1040.             }
  1041.             return $r;
  1042.         }
  1043.  
  1044.         /**
  1045.         * add a string to the 'internal debug message' (separate from 'user debug message')
  1046.         * @param string $strings 
  1047.         * @access private
  1048.         */
  1049.         function debugmsg($string)
  1050.         {
  1051.             $this->debug_info .= $string."\n";
  1052.         }
  1053.  
  1054.         function xml_header($charset_encoding='')
  1055.         {
  1056.             if ($charset_encoding != '')
  1057.             {
  1058.                 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?">\n";
  1059.             }
  1060.             else
  1061.             {
  1062.                 return "<?xml version=\"1.0\"?" ">\n";
  1063.             }
  1064.         }
  1065.  
  1066.         /**
  1067.         * A debugging routine: just echoes back the input packet as a string value
  1068.         * DEPRECATED!
  1069.         */
  1070.         function echoInput()
  1071.         {
  1072.             $r=&new xmlrpcresp(new xmlrpcval"'Aha said I: '" $GLOBALS['HTTP_RAW_POST_DATA']'string'));
  1073.             print $r->serialize();
  1074.         }
  1075.     }
  1076. ?>

Documentation generated on Mon, 05 Mar 2007 21:32:58 +0000 by phpDocumentor 1.3.1