<?php
// by Edd Dumbill (C) 1999,2000
// <edd@usefulinc.com>
// $Id: xmlrpcs.inc,v 1.5 2000/06/03 13:44:33 edmundd Exp $

// License is granted to use or modify this software ("XML-RPC for PHP")
// for commercial or non-commercial use provided the copyright of the author
// is preserved in any distributed or derivative work.

// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// XML RPC Server class
// requires: xmlrpc.inc

// listMethods: either a string, or nothing
$_xmlrpcs_listMethods_sig=array(array($xmlrpcArray, $xmlrpcString), 
																array($xmlrpcArray));
$_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
function _xmlrpcs_listMethods($server, $m) {
	global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
	$v=new xmlrpcval();
	$dmap=$server->dmap;
	$outAr=array();
	for(reset($dmap); list($key, $val)=each($dmap); ) {
		$outAr[]=new xmlrpcval($key, "string");
	}
	$dmap=$_xmlrpcs_dmap;
	for(reset($dmap); list($key, $val)=each($dmap); ) {
		$outAr[]=new xmlrpcval($key, "string");
	}
	$v->addArray($outAr);
	return new xmlrpcresp($v);
}

$_xmlrpcs_methodSignature_sig=array(array($xmlrpcArray, $xmlrpcString));
$_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)';
function _xmlrpcs_methodSignature($server, $m) {
	global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;

	$methName=$m->getParam(0);
	$methName=$methName->scalarval();
	if (ereg("^system\.", $methName)) {
		$dmap=$_xmlrpcs_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 xmlrpcval($inSig[$j], "string");
				}
				$sigs[]=new xmlrpcval($cursig, "array");
			}
			$r=new xmlrpcresp(new xmlrpcval($sigs, "array"));
		} else {
			$r=new xmlrpcresp(new xmlrpcval("undef", "string"));
		}
	} else {
			$r=new xmlrpcresp(0,
						  $xmlrpcerr["introspect_unknown"],
						  $xmlrpcstr["introspect_unknown"]);
	}
	return $r;
}

$_xmlrpcs_methodHelp_sig=array(array($xmlrpcString, $xmlrpcString));
$_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
function _xmlrpcs_methodHelp($server, $m) {
	global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;

	$methName=$m->getParam(0);
	$methName=$methName->scalarval();
	if (ereg("^system\.", $methName)) {
		$dmap=$_xmlrpcs_dmap; $sysCall=1;
	} else {
		$dmap=$server->dmap; $sysCall=0;
	}
	//	print "<!-- ${methName} -->\n";
	if (isset($dmap[$methName])) {
		if ($dmap[$methName]["docstring"]) {
			$r=new xmlrpcresp(new xmlrpcval($dmap[$methName]["docstring"]),
												"string");
		} else {
			$r=new xmlrpcresp(new xmlrpcval("", "string"));
		}
	} else {
			$r=new xmlrpcresp(0,
						  $xmlrpcerr["introspect_unknown"],
						  $xmlrpcstr["introspect_unknown"]);
	}
	return $r;
}

$_xmlrpcs_dmap=array(
										 "system.listMethods" =>
										 array("function" => "_xmlrpcs_listMethods",
													 "signature" => $_xmlrpcs_listMethods_sig,
													 "docstring" => $_xmlrpcs_listMethods_doc),
										 "system.methodHelp" =>
										 array("function" => "_xmlrpcs_methodHelp",
													 "signature" => $_xmlrpcs_methodHelp_sig,
													 "docstring" => $_xmlrpcs_methodHelp_doc),
										 "system.methodSignature" =>
										 array("function" => "_xmlrpcs_methodSignature",
													 "signature" => $_xmlrpcs_methodSignature_sig,
													 "docstring" => $_xmlrpcs_methodSignature_doc)
										 );

$_xmlrpc_debuginfo="";
function xmlrpc_debugmsg($m) {
	global $_xmlrpc_debuginfo;
	$_xmlrpc_debuginfo=$_xmlrpc_debuginfo . $m . "\n";
}

class xmlrpc_server {
    var $dmap=array();

    function xmlrpc_server($dispMap, $serviceNow=1) {
         global $HTTP_RAW_POST_DATA;
         // dispMap is a despatch array of methods
         // mapped to function names and signatures
         // if a method
         // doesn't appear in the map then an unknown
         // method error is generated
         $this->dmap=$dispMap;
         if ($serviceNow) {
             $this->service();
         }
    }

    function serializeDebug() {
        global $_xmlrpc_debuginfo;
        if ($_xmlrpc_debuginfo!="") 
            return "<!-- DEBUG INFO:\n\n" . $_xmlrpc_debuginfo . "\n-->\n";
        else
            return "";
    }

    function service() {
        $r=$this->parseRequest();
        $payload="<?xml version=\"1.0\"?>\n" . 
            $this->serializeDebug() .
            $r->serialize() .
            "\n\n\n\n\n";
        # Before 2014.10.25, the header value below was two fields --
        # a Content-type and a Content-length, separated by a newline.
        # Apparently, that used to be legal because this code used to work,
        # but now it just makes the Header statement get ignored.  Also,
        # The system appears to put in the right Content-length header field
        # regardless of any field you specify with Header().  So we just
        # dropped the Content-length off of the value below.
        Header("Content-type: text/xml");
        print $payload;
    }

    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);
                    }
            }
            return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
    }

    function parseRequest($data="") {
        global $_xh,$HTTP_RAW_POST_DATA;
        global $xmlrpcerr, $xmlrpcstr, $xmlrpcerrxml, $xmlrpc_defencoding,
                $_xmlrpcs_dmap;

        

        if ($data=="") {
          $data=$HTTP_RAW_POST_DATA;
        }
        $parser = xml_parser_create($xmlrpc_defencoding);

        $_xh[$parser]=array();
        $_xh[$parser]['st']="";
        $_xh[$parser]['cm']=0; 
        $_xh[$parser]['isf']=0; 
        $_xh[$parser]['params']=array();
        $_xh[$parser]['method']="";

        // decompose incoming XML into request structure

        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
        xml_set_element_handler($parser, "xmlrpc_se", "xmlrpc_ee");
        xml_set_character_data_handler($parser, "xmlrpc_cd");
        xml_set_default_handler($parser, "xmlrpc_dh");
        if (!xml_parse($parser, $data, 1)) {
          // return XML error as a faultCode
          $r=new xmlrpcresp(0,
                                                $xmlrpcerrxml+xml_get_error_code($parser),
                                                sprintf("XML error: %s at line %d",
                                        xml_error_string(xml_get_error_code($parser)),
                                  xml_get_current_line_number($parser)));
          xml_parser_free($parser);
        } else {
          xml_parser_free($parser);
          $m=new xmlrpcmsg($_xh[$parser]['method']);
          // now add parameters in
          for($i=0; $i<sizeof($_xh[$parser]['params']); $i++) {
                        // print "<!-- " . $_xh[$parser]['params'][$i]. "-->\n";
                        $plist.="$i - " .  $_xh[$parser]['params'][$i]. " \n";
                        eval('$m->addParam(' . $_xh[$parser]['params'][$i]. ");");
          }
                xmlrpc_debugmsg($plist);
          // now to deal with the method
                $methName=$_xh[$parser]['method'];
                if (ereg("^system\.", $methName)) {
                        $dmap=$_xmlrpcs_dmap; $sysCall=1;
                } else {
                        $dmap=$this->dmap; $sysCall=0;
                }
          if (isset($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) { 
                                        eval('$r=' . $dmap[$methName]['function'] . 
                                                         '($this, $m);');
                                } else {
                                        eval('$r=' . $dmap[$methName]['function'] . 
                                                         '($m);');
                                }
                        } else {
                                $r=new xmlrpcresp(0,
                                                  $xmlrpcerr["incorrect_params"],
                                                  $xmlrpcstr["incorrect_params"].": ". $sr[1]);
                        }
          } else {
                // else prepare error response
                $r=new xmlrpcresp(0,
                                                  $xmlrpcerr["unknown_method"],
                                                  $xmlrpcstr["unknown_method"]);
          }
        }
        return $r;
  }

    function echoInput() {
        global $HTTP_RAW_POST_DATA;

        // a debugging routine: just echos back the input
        // packet as a string value

        $r=new xmlrpcresp;
        $r->xv=new xmlrpcval( "'Aha said I: '" . $HTTP_RAW_POST_DATA, "string");
        print $r->serialize();
  }
}

?>
