| Viewing file:  Server.php (21.92 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
<?php
 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
 /**
 * Server commands for our PHP implementation of the XML-RPC protocol
 *
 * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
 * It has support for HTTP transport, proxies and authentication.
 *
 * PHP versions 4 and 5
 *
 * @category   Web Services
 * @package    XML_RPC
 * @author     Edd Dumbill <edd@usefulinc.com>
 * @author     Stig Bakken <stig@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Daniel Convissor <danielc@php.net>
 * @copyright  1999-2001 Edd Dumbill, 2001-2010 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License
 * @version    SVN: $Id: Server.php 315558 2011-08-26 14:42:51Z danielc $
 * @link       http://pear.php.net/package/XML_RPC
 */
 
 
 /**
 * Pull in the XML_RPC class
 */
 require_once 'XML/RPC.php';
 
 
 /**
 * signature for system.listMethods: return = array,
 * parameters = a string or nothing
 * @global array $GLOBALS['XML_RPC_Server_listMethods_sig']
 */
 $GLOBALS['XML_RPC_Server_listMethods_sig'] = array(
 array($GLOBALS['XML_RPC_Array'],
 $GLOBALS['XML_RPC_String']
 ),
 array($GLOBALS['XML_RPC_Array'])
 );
 
 /**
 * docstring for system.listMethods
 * @global string $GLOBALS['XML_RPC_Server_listMethods_doc']
 */
 $GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the'
 . ' methods that the XML-RPC server knows how to dispatch';
 
 /**
 * signature for system.methodSignature: return = array,
 * parameters = string
 * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig']
 */
 $GLOBALS['XML_RPC_Server_methodSignature_sig'] = array(
 array($GLOBALS['XML_RPC_Array'],
 $GLOBALS['XML_RPC_String']
 )
 );
 
 /**
 * docstring for system.methodSignature
 * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc']
 */
 $GLOBALS['XML_RPC_Server_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)';
 
 /**
 * signature for system.methodHelp: return = string,
 * parameters = string
 * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig']
 */
 $GLOBALS['XML_RPC_Server_methodHelp_sig'] = array(
 array($GLOBALS['XML_RPC_String'],
 $GLOBALS['XML_RPC_String']
 )
 );
 
 /**
 * docstring for methodHelp
 * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc']
 */
 $GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined'
 . ' for the method passed, otherwise returns an empty string';
 
 /**
 * dispatch map for the automatically declared XML-RPC methods.
 * @global array $GLOBALS['XML_RPC_Server_dmap']
 */
 $GLOBALS['XML_RPC_Server_dmap'] = array(
 'system.listMethods' => array(
 'function'  => 'XML_RPC_Server_listMethods',
 'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'],
 'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc']
 ),
 'system.methodHelp' => array(
 'function'  => 'XML_RPC_Server_methodHelp',
 'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
 'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc']
 ),
 'system.methodSignature' => array(
 'function'  => 'XML_RPC_Server_methodSignature',
 'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
 'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc']
 )
 );
 
 /**
 * @global string $GLOBALS['XML_RPC_Server_debuginfo']
 */
 $GLOBALS['XML_RPC_Server_debuginfo'] = '';
 
 
 /**
 * Lists all the methods that the XML-RPC server knows how to dispatch
 *
 * @return object  a new XML_RPC_Response object
 */
 function XML_RPC_Server_listMethods($server, $m)
 {
 global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
 
 $v = new XML_RPC_Value();
 $outAr = array();
 foreach ($server->dmap as $key => $val) {
 $outAr[] = new XML_RPC_Value($key, 'string');
 }
 foreach ($XML_RPC_Server_dmap as $key => $val) {
 $outAr[] = new XML_RPC_Value($key, 'string');
 }
 $v->addArray($outAr);
 return new XML_RPC_Response($v);
 }
 
 /**
 * Returns an array of known signatures (an array of arrays)
 * for the given method
 *
 * If no signatures are known, returns a none-array
 * (test for type != array to detect missing signature)
 *
 * @return object  a new XML_RPC_Response object
 */
 function XML_RPC_Server_methodSignature($server, $m)
 {
 global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
 
 $methName = $m->getParam(0);
 $methName = $methName->scalarval();
 if (strpos($methName, 'system.') === 0) {
 $dmap = $XML_RPC_Server_dmap;
 $sysCall = 1;
 } else {
 $dmap = $server->dmap;
 $sysCall = 0;
 }
 //  print "<!-- ${methName} -->\n";
 if (isset($dmap[$methName])) {
 if ($dmap[$methName]['signature']) {
 $sigs = array();
 $thesigs = $dmap[$methName]['signature'];
 for ($i = 0; $i < sizeof($thesigs); $i++) {
 $cursig = array();
 $inSig = $thesigs[$i];
 for ($j = 0; $j < sizeof($inSig); $j++) {
 $cursig[] = new XML_RPC_Value($inSig[$j], 'string');
 }
 $sigs[] = new XML_RPC_Value($cursig, 'array');
 }
 $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array'));
 } else {
 $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string'));
 }
 } else {
 $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
 $XML_RPC_str['introspect_unknown']);
 }
 return $r;
 }
 
 /**
 * Returns help text if defined for the method passed, otherwise returns
 * an empty string
 *
 * @return object  a new XML_RPC_Response object
 */
 function XML_RPC_Server_methodHelp($server, $m)
 {
 global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
 
 $methName = $m->getParam(0);
 $methName = $methName->scalarval();
 if (strpos($methName, 'system.') === 0) {
 $dmap = $XML_RPC_Server_dmap;
 $sysCall = 1;
 } else {
 $dmap = $server->dmap;
 $sysCall = 0;
 }
 
 if (isset($dmap[$methName])) {
 if ($dmap[$methName]['docstring']) {
 $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
 'string');
 } else {
 $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
 }
 } else {
 $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
 $XML_RPC_str['introspect_unknown']);
 }
 return $r;
 }
 
 /**
 * @return void
 */
 function XML_RPC_Server_debugmsg($m)
 {
 global $XML_RPC_Server_debuginfo;
 $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
 }
 
 
 /**
 * A server for receiving and replying to XML RPC requests
 *
 * <code>
 * $server = new XML_RPC_Server(
 *     array(
 *         'isan8' =>
 *             array(
 *                 'function' => 'is_8',
 *                 'signature' =>
 *                      array(
 *                          array('boolean', 'int'),
 *                          array('boolean', 'int', 'boolean'),
 *                          array('boolean', 'string'),
 *                          array('boolean', 'string', 'boolean'),
 *                      ),
 *                 'docstring' => 'Is the value an 8?'
 *             ),
 *     ),
 *     1,
 *     0
 * );
 * </code>
 *
 * @category   Web Services
 * @package    XML_RPC
 * @author     Edd Dumbill <edd@usefulinc.com>
 * @author     Stig Bakken <stig@php.net>
 * @author     Martin Jansen <mj@php.net>
 * @author     Daniel Convissor <danielc@php.net>
 * @copyright  1999-2001 Edd Dumbill, 2001-2010 The PHP Group
 * @license    http://www.php.net/license/3_01.txt  PHP License
 * @version    Release: @package_version@
 * @link       http://pear.php.net/package/XML_RPC
 */
 class XML_RPC_Server
 {
 /**
 * Should the payload's content be passed through mb_convert_encoding()?
 *
 * @see XML_RPC_Server::setConvertPayloadEncoding()
 * @since Property available since Release 1.5.1
 * @var boolean
 */
 var $convert_payload_encoding = false;
 
 /**
 * The dispatch map, listing the methods this server provides.
 * @var array
 */
 var $dmap = array();
 
 /**
 * The present response's encoding
 * @var string
 * @see XML_RPC_Message::getEncoding()
 */
 var $encoding = '';
 
 /**
 * Debug mode (0 = off, 1 = on)
 * @var integer
 */
 var $debug = 0;
 
 /**
 * The response's HTTP headers
 * @var string
 */
 var $server_headers = '';
 
 /**
 * The response's XML payload
 * @var string
 */
 var $server_payload = '';
 
 
 /**
 * Constructor for the XML_RPC_Server class
 *
 * @param array $dispMap   the dispatch map. An associative array
 *                          explaining each function. The keys of the main
 *                          array are the procedure names used by the
 *                          clients. The value is another associative array
 *                          that contains up to three elements:
 *                            + The 'function' element's value is the name
 *                              of the function or method that gets called.
 *                              To define a class' method: 'class::method'.
 *                            + The 'signature' element (optional) is an
 *                              array describing the return values and
 *                              parameters
 *                            + The 'docstring' element (optional) is a
 *                              string describing what the method does
 * @param int $serviceNow  should the HTTP response be sent now?
 *                          (1 = yes, 0 = no)
 * @param int $debug       should debug output be displayed?
 *                          (1 = yes, 0 = no)
 *
 * @return void
 */
 function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0)
 {
 global $HTTP_RAW_POST_DATA;
 
 if ($debug) {
 $this->debug = 1;
 } else {
 $this->debug = 0;
 }
 
 $this->dmap = $dispMap;
 
 if ($serviceNow) {
 $this->service();
 } else {
 $this->createServerPayload();
 $this->createServerHeaders();
 }
 }
 
 /**
 * @return string  the debug information if debug debug mode is on
 */
 function serializeDebug()
 {
 global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA;
 
 if ($this->debug) {
 XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n"
 . $HTTP_RAW_POST_DATA
 . "\n" . '^^^ END POST DATA ^^^');
 }
 
 if ($XML_RPC_Server_debuginfo != '') {
 return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n"
 . str_replace('--', '- - ', $XML_RPC_Server_debuginfo)
 . "-->\n";
 } else {
 return '';
 }
 }
 
 /**
 * Sets whether the payload's content gets passed through
 * mb_convert_encoding()
 *
 * Returns PEAR_ERROR object if mb_convert_encoding() isn't available.
 *
 * @param int $in  where 1 = on, 0 = off
 *
 * @return void
 *
 * @see XML_RPC_Message::getEncoding()
 * @since Method available since Release 1.5.1
 */
 function setConvertPayloadEncoding($in)
 {
 if ($in && !function_exists('mb_convert_encoding')) {
 return $this->raiseError('mb_convert_encoding() is not available',
 XML_RPC_ERROR_PROGRAMMING);
 }
 $this->convert_payload_encoding = $in;
 }
 
 /**
 * Sends the response
 *
 * The encoding and content-type are determined by
 * XML_RPC_Message::getEncoding()
 *
 * @return void
 *
 * @uses XML_RPC_Server::createServerPayload(),
 *       XML_RPC_Server::createServerHeaders()
 */
 function service()
 {
 if (!$this->server_payload) {
 $this->createServerPayload();
 }
 if (!$this->server_headers) {
 $this->createServerHeaders();
 }
 
 /*
 * $server_headers needs to remain a string for compatibility with
 * old scripts using this package, but PHP 4.4.2 no longer allows
 * line breaks in header() calls.  So, we split each header into
 * an individual call.  The initial replace handles the off chance
 * that someone composed a single header with multiple lines, which
 * the RFCs allow.
 */
 $this->server_headers = preg_replace("@[\r\n]+[ \t]+@",
 ' ', trim($this->server_headers));
 $headers = preg_split("@[\r\n]+@", $this->server_headers);
 foreach ($headers as $header)
 {
 header($header);
 }
 
 print $this->server_payload;
 }
 
 /**
 * Generates the payload and puts it in the $server_payload property
 *
 * If XML_RPC_Server::setConvertPayloadEncoding() was set to true,
 * the payload gets passed through mb_convert_encoding()
 * to ensure the payload matches the encoding set in the
 * XML declaration.  The encoding type can be manually set via
 * XML_RPC_Message::setSendEncoding().
 *
 * @return void
 *
 * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding,
 *       XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug()
 * @see  XML_RPC_Server::setConvertPayloadEncoding()
 */
 function createServerPayload()
 {
 $r = $this->parseRequest();
 $this->server_payload = '<?xml version="1.0" encoding="'
 . $this->encoding . '"?>' . "\n"
 . $this->serializeDebug()
 . $r->serialize();
 if ($this->convert_payload_encoding) {
 $this->server_payload = mb_convert_encoding($this->server_payload,
 $this->encoding);
 }
 }
 
 /**
 * Determines the HTTP headers and puts them in the $server_headers
 * property
 *
 * @return boolean  TRUE if okay, FALSE if $server_payload isn't set.
 *
 * @uses XML_RPC_Server::createServerPayload(),
 *       XML_RPC_Server::$server_headers
 */
 function createServerHeaders()
 {
 if (!$this->server_payload) {
 return false;
 }
 $this->server_headers = 'Content-Length: '
 . strlen($this->server_payload) . "\r\n"
 . 'Content-Type: text/xml;'
 . ' charset=' . $this->encoding;
 return true;
 }
 
 /**
 * @return array
 */
 function verifySignature($in, $sig)
 {
 for ($i = 0; $i < sizeof($sig); $i++) {
 // check each possible signature in turn
 $cursig = $sig[$i];
 if (sizeof($cursig) == $in->getNumParams() + 1) {
 $itsOK = 1;
 for ($n = 0; $n < $in->getNumParams(); $n++) {
 $p = $in->getParam($n);
 // print "<!-- $p -->\n";
 if ($p->kindOf() == 'scalar') {
 $pt = $p->scalartyp();
 } else {
 $pt = $p->kindOf();
 }
 // $n+1 as first type of sig is return type
 if ($pt != $cursig[$n+1]) {
 $itsOK = 0;
 $pno = $n+1;
 $wanted = $cursig[$n+1];
 $got = $pt;
 break;
 }
 }
 if ($itsOK) {
 return array(1);
 }
 }
 }
 if (isset($wanted)) {
 return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
 } else {
 $allowed = array();
 foreach ($sig as $val) {
 end($val);
 $allowed[] = key($val);
 }
 $allowed = array_unique($allowed);
 $last = count($allowed) - 1;
 if ($last > 0) {
 $allowed[$last] = 'or ' . $allowed[$last];
 }
 return array(0,
 'Signature permits ' . implode(', ', $allowed)
 . ' parameters but the request had '
 . $in->getNumParams());
 }
 }
 
 /**
 * @return object  a new XML_RPC_Response object
 *
 * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding
 */
 function parseRequest($data = '')
 {
 global $XML_RPC_xh, $HTTP_RAW_POST_DATA,
 $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
 $XML_RPC_defencoding, $XML_RPC_Server_dmap;
 
 if ($data == '') {
 $data = $HTTP_RAW_POST_DATA;
 }
 
 $this->encoding = XML_RPC_Message::getEncoding($data);
 $parser_resource = xml_parser_create($this->encoding);
 $parser = (int) $parser_resource;
 
 $XML_RPC_xh[$parser] = array();
 $XML_RPC_xh[$parser]['cm']     = 0;
 $XML_RPC_xh[$parser]['isf']    = 0;
 $XML_RPC_xh[$parser]['params'] = array();
 $XML_RPC_xh[$parser]['method'] = '';
 $XML_RPC_xh[$parser]['stack'] = array();
 $XML_RPC_xh[$parser]['valuestack'] = array();
 
 $plist = '';
 
 // decompose incoming XML into request structure
 
 xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
 xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
 xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
 if (!xml_parse($parser_resource, $data, 1)) {
 // return XML error as a faultCode
 $r = new XML_RPC_Response(0,
 $XML_RPC_errxml+xml_get_error_code($parser_resource),
 sprintf('XML error: %s at line %d',
 xml_error_string(xml_get_error_code($parser_resource)),
 xml_get_current_line_number($parser_resource)));
 xml_parser_free($parser_resource);
 } elseif ($XML_RPC_xh[$parser]['isf']>1) {
 $r = new XML_RPC_Response(0,
 $XML_RPC_err['invalid_request'],
 $XML_RPC_str['invalid_request']
 . ': '
 . $XML_RPC_xh[$parser]['isf_reason']);
 xml_parser_free($parser_resource);
 } else {
 xml_parser_free($parser_resource);
 $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
 // now add parameters in
 for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
 // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
 $plist .= "$i - " . var_export($XML_RPC_xh[$parser]['params'][$i], true) . " \n";
 $m->addParam($XML_RPC_xh[$parser]['params'][$i]);
 }
 
 if ($this->debug) {
 XML_RPC_Server_debugmsg($plist);
 }
 
 // now to deal with the method
 $methName = $XML_RPC_xh[$parser]['method'];
 if (strpos($methName, 'system.') === 0) {
 $dmap = $XML_RPC_Server_dmap;
 $sysCall = 1;
 } else {
 $dmap = $this->dmap;
 $sysCall = 0;
 }
 
 if (isset($dmap[$methName]['function'])
 && is_string($dmap[$methName]['function'])
 && strpos($dmap[$methName]['function'], '::') !== false)
 {
 $dmap[$methName]['function'] =
 explode('::', $dmap[$methName]['function']);
 }
 
 if (isset($dmap[$methName]['function'])
 && is_callable($dmap[$methName]['function']))
 {
 // dispatch if exists
 if (isset($dmap[$methName]['signature'])) {
 $sr = $this->verifySignature($m,
 $dmap[$methName]['signature'] );
 }
 if (!isset($dmap[$methName]['signature']) || $sr[0]) {
 // if no signature or correct signature
 if ($sysCall) {
 $r = call_user_func($dmap[$methName]['function'], $this, $m);
 } else {
 $r = call_user_func($dmap[$methName]['function'], $m);
 }
 if (!is_object($r) || !is_a($r, 'XML_RPC_Response')) {
 $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'],
 $XML_RPC_str['not_response_object']);
 }
 } else {
 $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
 $XML_RPC_str['incorrect_params']
 . ': ' . $sr[1]);
 }
 } else {
 // else prepare error response
 $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
 $XML_RPC_str['unknown_method']);
 }
 }
 return $r;
 }
 
 /**
 * Echos back the input packet as a string value
 *
 * @return void
 *
 * Useful for debugging.
 */
 function echoInput()
 {
 global $HTTP_RAW_POST_DATA;
 
 $r = new XML_RPC_Response(0);
 $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
 print $r->serialize();
 }
 }
 
 /*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * c-hanging-comment-ender-p: nil
 * End:
 */
 
 ?>
 
 |