+++ /dev/null
-<?php define("XMLRPC_VERSION", "0.3.10");
-#
-# Supports XML-RPC (text/xml) and XML+RPC (application/rpc+xml) compressed,
-# and can be used as client or server interface. Works without XMLRPC and
-# XML extensions, but utilizes them for optimal speed whenever available.
-#
-# XXXX XXXX MMM MMM LLL RRRRRRR PPPPPPP CCCCCCC
-# XXXX XXXX MMMM MMMM LLL +++ RRRRRRRR PPPPPPPP CCCCCCCCC
-# XXXXXXX MMMMM MMMMM LLL +++ RRR RRR PPP PPP CCC CCC
-# XXXXX MMMMMMMMMMM LLL +++++++++++ RRR RRR PPP PPP CCC
-# XXX MMM MMM MMM LLL +++++++++++ RRRRRRRR PPPPPPPP CCC
-# XXXXX MMM M MMM LLL +++ RRRRRRR PPPPPPP CCC
-# XXXXXXX MMM MMM LLL +++ RRR RRR PPP CCC CCC
-# XXXX XXXX MMM MMM LLLLLLL RRR RRR PPP CCCCCCCCC
-# XXXX XXXX MMM MMM LLLLLLL RRR RRR PPP CCCCCCC
-#
-# This is Public Domain. (c) 2004 WhoEver wants to. [milky*erphesfurt·de]
-
-
-#-- config
-define("XMLRPC_PLUS", 0); # use XML+RPC per default
-define("XMLRPC_AUTO_TYPES", 0); # detect base64+datetime strings and automatically generate the according xmlrpc object representations then
-define("XMLRPC_AUTO_UTF8", 1); # de/convert anything from and to UTF-8 automatically - if yourscripts use Latin1 natively, but the RPC server expects/sends UTF-8
-define("XMLRPC_CHARSET", "utf-8"); # used in responses and requests
-define("XMLRPC_AUTODISCOVERY", 0); # "connections" automatically create methods
-define("XMLRPC_FAST", 1); # use PHPs XML-RPC extension where possible
-define("XMLRPC_OO", 1); # return XML-RPC/HTTP errors as objects
-define("XMLRPC_DEBUG", 0); # output error hints, write /tmp dumps - set this to 1, 2 or 3
-
-#-- _server() settings
-define("XMLRPC_LOG", "/tmp/xmlrpc.".@$_SERVER["SERVER_NAME"].".log");
-
-#-- general data
-# (don't change the following, most are auto-configured values)
-define("XMLRPC_UA", "xml+rpc/".XMLRPC_VERSION." (PHP/".PHP_VERSION."; ".PHP_OS.")");
-define("XMLRPC_MIME_NEW", "application/rpc+xml");
-define("XMLRPC_MIME_OLD", "text/xml");
-define("XMLRPC_MIME", XMLRPC_MIME_OLD);
-define("XMLRPC_ACCEPT", XMLRPC_MIME_NEW.", ".XMLRPC_MIME_OLD."; q=0.5");
-define("XMLRPC_EPI", function_exists("xmlrpc_decode_request"));
-
-#-- init
-error_reporting(0);
-if (isset($_SERVER["HTTP_CONTENT_TYPE"]) && empty($_SERVER["CONTENT_TYPE"])) {
- $_SERVER["CONTENT_TYPE"] = $_SERVER["HTTP_CONTENT_TYPE"]; // older CGI implementations
-}
-
-
-
-
-############################################################################
-# #
-# client part #
-# #
-############################################################################
-
-
-#-- Issue a request, call can take any number of arguments.
-# $result = xmlrpc("http://example.com/RPC2/", "method1", $arg1 ...);
-# $result = xmlrpc("xml+rpc://here.org/RPC3/", "ns.function", ...);
-# Results automatically have <datetime> values converted into Unix
-# timestamps and <base64> unpacked into strings.
-#
-function xmlrpc($server, $method=NULL /*, ... */) {
- if ($method) {
- $params = func_get_args();
- shift($params); shift($params);
- return
- xmlrpc_request($server, $method, $params);
- }
- else {
- return
- new xmlrpc_connection($server);
- }
-}
-
-
-
-#-- Generate and send request, decode response.
-function xmlrpc_request($url, $method, $params=array(), $plus=XMLRPC_PLUS, $gzip=0) {
- global $xmlrpc_response_headers, $xmlrpc_error;
-
- #-- init whole lib for request (we are not-OO here)
- $xmlrpc_error = false;
- $xmlrpc_response_headers = array();
-
- #-- encapsulate req, transmit it
- $socket = xmlrpc_request_send($url, $method, $params, $plus, $gzip);
- if (!$socket) {
- return xmlrpc_error(-32768, "no connection", 0, "GLOBALVARS");
- }
-
- #-- wait for, read response
- $response = "";
- while (!feof($socket) && (strlen($DATA) <= 768<<10)) {
- $response .= fread($socket, 4<<10);
- }
- fclose($socket);
- if (XMLRPC_DEBUG >= 3) {
- echo "<code>$response</code>";
- }
-
- #-- decode answer and give results
- return xmlrpc_response_decode($response);
-}
-
-
-#-- an alias
-function xmlrpc_call($url, $method, $params=array(), $plus=XMLRPC_PLUS, $gzip=0) {
- return xmlrpc_request($url, $method, $params, $plus, $gzip);
-}
-
-
-
-#-- marshall request parameters into array, hash, xml string
-function xmlrpc_request_send($url, $method, &$params, $plus, $gzip, $blocking=true) {
-
- #-- get connection data
- $c = parse_url($url);
- ($host = $c["host"]);
- ($port = @$c["port"]) or ($port = 80);
- ($path = $c["path"]) or ($path = "/");
- if (strpos($c["scheme"], "+")) {
- $plus++;
- }
- if (strpos($c["scheme"], "gzip")) {
- $gzip++;
- }
- if (!$host) { return(NULL); }
- $inj = "";
- if ($str = $c["user"]) {
- if ($c["pass"]) { $str .= ":" . $c["pass"]; }
- $inj = "Authorization: Basic " . base64_encode($str) . "\n";
- }
-
- #-- mk request HTTP+XML block from params
- $request = xmlrpc_request_marshall($method, $params);
- $request = xmlrpc_request_http($request, $path, $host, $plus, $gzip, $inj);
-
- #-- connect, send request
- if ($socket = fsockopen($host, $port, $io_err, $io_err_s, 30)) {
- socket_set_blocking($socket, $blocking);
- socket_set_timeout($socket, 17, 555);
- }
- else {
- echo "Could not connect to '<b>$host</b>:$port$path' - error $io_err: $io_err_s.<br>\n";
- return(NULL);
- }
- fputs($socket, $request);
-
- #-- done here
- return($socket);
-}
-
-
-#-- marshall function call into XML+HTTP string
-function xmlrpc_request_marshall($method, &$params) {
-
- #-- use xmlrpc-epi
- if (XMLRPC_FAST && XMLRPC_EPI) {
- $query = xmlrpc_encode_request($method, $params);
- return($query);
- }
-
- #-- build query
- $query = array(
- "methodCall" => array(
- "methodName" => array( ",0"=>$method ),
- "params" => array()
- )
- );
- foreach ($params as $i=>$p) {
- $query["methodCall"]["params"]["param,$i"] = xmlrpc_compact_value($p);
- }
- $query = array2xml($query, 1, 'encoding="'.XMLRPC_CHARSET.'" ');
-
- #-- encode?
- if (XMLRPC_AUTO_UTF8) {
- $query = utf8_encode($query);
- }
-
- return($query);
-}
-
-
-#-- enclose body into HTTP request string
-function xmlrpc_request_http(&$query, $path, $host, $plus, $gzip, $inj_header="") {
-
- #-- build request
- $n = "\015\012";
- $request = "POST $path HTTP/1.0$n"
- . "Host: $host$n"
- . ($inj_header ? str_replace("\n", $n, $inj_header) : "")
- . "User-Agent: " . XMLRPC_UA . "$n"
- . "Accept: ".XMLRPC_ACCEPT."$n"
- . (!XMLRPC_DEBUG ? "Accept-Encoding: deflate$n" : "")
- . "Content-Type: ".($plus ? XMLRPC_MIME_NEW : XMLRPC_MIME_OLD)
- ."; charset=".XMLRPC_CHARSET."$n";
-
- #-- compress?
- if ($gzip) {
- $query = gzdeflate($query);
- $request .= "Content-Encoding: deflate$n";
- }
- $request .= "Content-Length: " . strlen($query) . "$n" . "$n";
- $request .= $query . "$n";
-
- return($request);
-}
-
-
-#-- unpack response from HTTP and XML representation
-function xmlrpc_response_decode(&$response) {
- global $xmlrpc_response_headers;
-
- #-- split into headers and content
- $l1 = strpos($response, "\n\n");
- $l2 = strpos($response, "\n\r\n");
- if ($l2 && (!$l1 || ($l2<$l1))) {
- $head = substr($response, 0, $l2);
- $response = substr($response, $l2+3);
- }
- else {
- $head = substr($response, 0, $l1);
- $response = substr($response, $l2+2);
- }
-
- #-- decode headers, decompress body
- foreach (explode("\n", $head) as $line) {
- $xmlrpc_response_headers[strtolower(trim(strtok($line, ":")))] = trim(strtok("\000"));
- }
- if ($enc = trim(@$xmlrpc_response_headers["content-encoding"])) {
- if (($enc == "gzip") || ($enc == "x-gzip")) {
- $response = gzinflate(substr($response, 10, strlen($response)-18));
- }
- elseif (($enc == "compress") || ($enc == "x-compress")) {
- $response = gzuncompress($response);
- }
- elseif (($enc == "deflate") || ($enc == "x-deflate")) {
- $response = gzinflate($response);
- }
- }
-
- $r = xmlrpc_response_unmarshall($response);
- if (XMLRPC_DEBUG) {var_dump($r);}
- return($r);
-}
-
-
-#-- decode XML-RPC from string into array and extract its actual meaning
-function xmlrpc_response_unmarshall(&$response) {
- global $xmlrpc_response_headers;
-
- #-- strip encoding
- if (XMLRPC_AUTO_UTF8) {
- xmlrpc_decode_utf8xml($response, @$xmlrpc_response_headers["content-type"].@$xmlrpc_response_headers["content-charset"]);
- }
-
- if (XMLRPC_DEBUG >= 4) { fwrite(fopen("/tmp/xmlrpc:resp_in_xml","w"), $response); }
-
- #-- use xmlrpc-epi
- if (XMLRPC_FAST && XMLRPC_EPI) {
- $r = xmlrpc_decode_request($response, $uu);
- xmlrpc_epi_decode_xtypes($r);
- if (is_array($r) && (count($r)==2) && isset($r["faultCode"]) && isset($r["faultString"])) {
- return xmlrpc_error($r["faultCode"], $r["faultString"], 1, "GLOBALVARS");
- }
- else {
- return($r);
- }
- }
-
-
- #-- unmarshall XML
- $response = xml2array($response);
-
- #-- fetch content (one returned element)
- if ($r = @$response["methodResponse,0"]["params,0"]["param,0"]["value,0"]) {
- $r = xmlrpc_decode_value($r);
- return($r);
- }
-
- #-- error cases
- # (we should rather return an error object here)
- if (($r = @$response["methodResponse,0"]["fault,0"]["value,0"]) && ($r = xmlrpc_decode_value($r))) {
- return xmlrpc_error($r["faultCode"], $r["faultString"], 1, "GLOBALVARS");
- }
- else {
- return xmlrpc_error(-32600, "xml+rpc: invalid response", 0, "GLBLVRS");
- }
- return(NULL);
-}
-
-
-
-#-- Establish a virtual XML+RPC or XML-RPC server connection (a pseudo
-# handshake is used to determine supported protocol / extensions).
-class xmlrpc_connection {
-
- #-- init
- function xmlrpc_connection($url, $autodiscovery=0) {
- global $xmlrpc_response_headers;
- $this->server = $url;
- $this->plus = 0;
- $this->gzip = 0;
-
- #-- handshake to check supported protocol
- $funcs = $this->call("system.getVersion");
- $this->plus = (strpos($xmlrpc_response_headers["accept"], XMLRPC_MIME_NEW) !== false);
- $this->gzip = (strpos($xmlrpc_response_headers["accept_encoding"], "deflate") !== false);
-
- #-- auto-discovery, create 'method' names
- if ($funcs && (XMLRPC_AUTODISCOVERY || $autodiscovery)) {
- foreach ($funcs as $fn) {
- $short = $fn;
- if ($l = strpos($fn, ".")) {
- $short = substr($fn, $l + 1);
- if (substr($fn, 0, $l) == "system") { continue; }
- }
- $this->short = create_function("", "return xmlrpc_request('{$this->server}','$fn',func_get_args(),{$this->plus},{$this->gzip});");
- }
- }
- }
-
- #-- generical call (needs func name)
- function call($method /*, ... */) {
- $params = func_get_args();
- shift($params);
- $r = xmlrpc_request($this->serverm, $method, $params, $this->plus, $this->gzip);
- return($r);
- }
-}
-
-#-- an alias
-class xmlrpc extends xmlrpc_connection {
-}
-
-
-
-
-############################################################################
-# #
-# server implementation #
-# #
-############################################################################
-
-
-#-- Check request and execute function if registered in $xmlrpc_methods[]
-# array.
-function xmlrpc_server() {
-
- global $xmlrpc_methods;
-
- #-- server is active
- define("XMLRPC_SERVER", getmypid());
- if (XMLRPC_DEBUG) { error_reporting(E_ALL^E_NOTICE); }
- ob_start();
-
- #-- standard reply headers
- header("Accept: ".XMLRPC_MIME_NEW.", ".XMLRPC_MIME_OLD."; q=0.5");
- header("Accept-Encoding: deflate");
- header("X-Server: " . XMLRPC_UA);
- header("Connection: close");
- header("Cache-Control: private");
-
- #-- fixes for PHP/Apache
- if (function_exists("getallheaders")) {
- foreach (getallheaders() as $i=>$v) {
- $_SERVER[strtoupper(strtr("HTTP_$i", "-", "_"))] = $v;
- }
- }
-
- #-- check and get call
- $allowed = array(
- "REQUEST_METHOD" => array("POST", "PUT", "CALL"),
- "CONTENT_TYPE" => array(XMLRPC_MIME_NEW, XMLRPC_MIME_OLD),
- );
- foreach ($allowed as $WHAT=>$WHICH) {
- if (!in_array(trim(strtok($WRONG=$_SERVER[$WHAT], ";,(")), $WHICH)) {
- header("Status: 400 Go Away, Stupid!");
- if (!$WRONG) {
- $WRONG = "undefined";
- }
- die("<h2>Error</h2>Your request was bogus, <b>$WHAT</b> must be <i>"
- . implode("</i> or <i>", $WHICH) . "</i>, but yours was '<tt>$WRONG</tt>'.\n");
- }
- }
- if (!($xml_request = xmlrpc_fetch_post_chunk())) {
- header("Status: 500 How Sad");
- die("<h2>Error</h2>Could not fetch POST data.\n");
- }
-
- #-- decipher incoming XML request string
- $method = "";
- if (XMLRPC_FAST && XMLRPC_EPI) {
- $params = xmlrpc_decode_request($xml_request, $method);
- xmlrpc_epi_decode_xtypes($params);
- }
- else {
- $params = xmlrpc_request_unmarshall($xml_request, $method);
- }
-
-
- #-- add the few system.methods()
- //if (empty($xmlrpc_methods)) {
- // $xmlrpc_methods = get_defined_functions();
- //}
- $xmlrpc_methods["system"] = "xmlrpc_system_methods"; # a class
-
- #-- call
- $result = xmlrpc_exec_method($method, $params);
-
- #-- send back result
- if (isset($result)) {
- if (isset($result)) {
- $resp["methodResponse"]["params"]["param"] = xmlrpc_compact_value($result);
- }
- else {
- $resp["methodResponse"]["params"] = array();
- }
-
- xmlrpc_send_response($resp);
- }
- else {
- $result = xmlrpc_error(0, "No Result");
- xmlrpc_send_response($result);
- }
-}
-
-
-
-#-- decode <methodCall> XML string into understandable chunks,
-# gives $params as return value and $method name via pass-by-ref
-function xmlrpc_request_unmarshall(&$xml_request, &$method) {
-
- #-- mangle charset
- if (XMLRPC_AUTO_UTF8) {
- xmlrpc_decode_utf8xml($xml_request, $_SERVER["CONTENT_TYPE"].$_SERVER["HTTP_CONTENT_CHARSET"]);
- }
-
- #-- decode XML string into PHP arrays
- $call = xml2array($xml_request, 1);
- $xml_request = NULL;
-
- $call = $call["methodCall,0"];
- if (!$call) {
- xmlrpc_send_response(xmlrpc_error(-32600, "Bad Request, <methodCall> missing"));
- }
- $method = $call["methodName,0"][",0"];
- if (!$method) {
- xmlrpc_send_response(xmlrpc_error(-32600, "Bad Request, <methodName> missing"));
- }
-
- $params = array();
- foreach ($call["params,1"] as $uu => $param) {
- $params[] = xmlrpc_decode_value($param["value,0"]);
- }
-
- return($params);
-}
-
-
-
-#-- Call the requested method (using the XML-method to PHP-function mapping
-# table and hints).
-function xmlrpc_exec_method($method, $params) {
-
- global $xmlrpc_methods;
- if (XMLRPC_DEBUG >= 2) { error_reporting(E_ALL^E_NOTICE); }
-
- #-- check if allowed call
- $rf = strtr($method, ".", "_");
- $cl = strtok($method, ".");
- if (!$xmlrpc_methods[$method] && !$xmlrpc_methods[$cl]
- && !in_array($method, $xmlrpc_methods)
- && !in_array($rf, $xmlrpc_methods) && !in_array($cl, $xmlrpc_methods) )
- {
- xmlrpc_send_response(xmlrpc_error(-32601));
- }
-
- #-- real function call
- if ($php_func_name = $xmlrpc_methods[$method]) {
- $rf = $method = $php_func_name;
- }
- if (function_exists($rf)) {
- $result = call_user_func_array($rf, $params);
- if (XMLRPC_DEBUG >= 4) { fwrite(fopen("/tmp/xmlrpc:func_call_res","w"),serialize(array($rf,$result,$params))); }
- return($result);
- }
- #-- PHP object method calls
- else {
- $class = strtok($method, ".");
- $method = strtok("\000");
- if ($uu = $xmlrpc_methods[$class]) {
- $class = $uu;
- }
- if ($class && class_exists($class) && $method) {
- $obj = new $class;
- if (method_exists($obj, $method)) {
- $result = call_user_method_array($method, $obj, $params); //<DEPRECATED>
- return($result);
- }
- }
- }
-
- #-- else error
- xmlrpc_send_response(xmlrpc_error(-32601));
-}
-
-
-
-#-- Get POST data from PHP (if it gives it to us).
-function xmlrpc_fetch_post_chunk() {
- global $HTTP_RAW_POST_DATA;
-
- $data = false;
- if ($f = fopen("php://input", "rb")) {
- $data = fread($f, 0x0100000);
- fclose($f);
- }
- if (empty($data)) {
- ini_set("always_populate_raw_post_data", "true"); // well, maybe(!?)
- $data = $HTTP_RAW_POST_DATA;
- $HTTP_RAW_POST_DATA = "";
- }
- $enc = trim(strtolower($_SERVER["HTTP_CONTENT_ENCODING"]));
- $funcs = array("deflate"=>"gzinflate", "gzip"=>"gzdecode", "compress"=>"gzuncompress", "x-gzip"=>"gzdecode", "x-bzip2"=>"bzuncompress");
- if ($enc && ($pf = $funcs[$enc]) && function_exists($pf)) {
- $data = $pf($data);
- }
- return($data);
-}
-
-
-#-- converts UTF-8 documents into Latin-1 ones
-function xmlrpc_decode_utf8xml(&$xml, $ct) {
- if (strpos(strtolower($ct), "utf-8") or preg_match('/<\?xml[^>]+encoding=["\']utf-8/i', $xml)) {
- $xml = utf8_decode($xml);
- $xml = preg_replace('/(<\?xml[^>]+encoding=["\'])utf-8(["\'])/i', '$1iso-8859-1$2', $xml, 1);
- }
-}
-
-
-
-#-- Creates an error object.
-function xmlrpc_error($no=-32500, $str="", $type=1, $into_vars=0) {
- global $xmlrpc_error, $xmlrpc_errorcode;
- $errors = array(
- 0 => "No Result",
- -32300 => "Transport error",
- -32400 => "Internal Server Error",
- -32500 => "Application error",
- -32600 => "Invalid message format / Bad request",
- -32601 => "Method does not exist",
- -32602 => "Parameter type mismatch",
- -32603 => "Internal XML-RPC error",
- -32604 => "Too many parameters",
- -32700 => "Not well-formed XML",
- -32701 => "Unsupported encoding - only ISO-8859-1 and UTF-8 capable",
- -32702 => "Invalid characters, encoding mismatch",
- );
- #-- build response xml/array
- if (!($str) && !($str = $errors[$no])) {
- $str = "Unknown Error";
- }
- if ($into_vars && !XMLRPC_OO) {
- $xmlrpc_error = $str;
- $xmlrpc_errorcode = $no;
- return(NULL);
- }
- else {
- return new xmlrpc_error($no, $str, $type);
- }
-}
-
-
-#-- error object
-class xmlrpc_error {
-
- var $type = 1; // else an HTTP error
- var $no;
- var $str;
-
- function xmlrpc_error($no, $str, $type=1) {
- $this->type = $type;
- $this->no = $no;
- $this->str = $str;
- }
-
- function send() {
- $error = xmlrpc_compact_value(array(
- "faultCode" => $no,
- "faultString" => $str,
- ));
- $resp = array(
- "methodResponse" => array(
- "fault" => $error
- )
- );
- xmlrpc_send_response($resp);
- }
-}
-
-
-#-- Sends a response.
-function xmlrpc_send_response($r) {
-
- #-- error objects send itself (by calling _send_response() again ;-)
- if (is_object($r)) {
- $r->send();
- }
-
- #-- answer XML-RPC and XML+RPC requests
- $ct = trim(strtok(strtolower($_SERVER["CONTENT_TYPE"]), ";,(")); // from original request
- $cs = XMLRPC_CHARSET;
- header("Content-Type: $ct; charset=\"$cs\"");
-
- #-- make XML document from it
- if (is_array($r)) {
- $r = array2xml($r, 1, 'encoding="'.$cs.'" ');
- }
-
- #-- compress answer?
- if (!headers_sent()) {
- $enc = trim(strtolower($_SERVER["HTTP_ACCEPT_ENCODING"]));
- $funcs = array("deflate"=>"gzdeflate", "gzip"=>"gzencode", "compress"=>"gzcompress", "x-gzip"=>"gzencode", "x-bzip2"=>"bzcompress");
- if ($enc && ($pf = $funcs[$enc]) && function_exists($pf)) {
- header("Content-Encoding: $enc");
- $r = $pf($r);
- }
- }
-
- #-- send
- if (ob_get_level()) {
- #-- this prevents that PHP errors appear as garbage in our response
- $add .= "<!--\n" . ob_get_contents() . "\n-->";
- ob_end_clean();
- }
- header("Content-Length: " . strlen($r));
- print $r . $add;
- die;
-}
-
-
-
-#-- Provides "system.*" method namespace.
-class xmlrpc_system_methods {
-
- function listMethods() {
- global $xmlrpc_methods;
- $r = array();
- foreach ($xmlrpc_methods as $i=>$i2) {
- $real = is_int($i) ? $i2 : $i;
- if (class_exists($real) && ($i2=$real) || class_exists($i2)) {
- foreach (get_class_methods($i2) as $add) {
- $r[] = $real.".".$add;
- }
- }
- else {
- $r[] = $real;
- }
- }
- return($r);
- }
-
- function time() {
- return new xmlrpc_datetime(time());
- }
-}
-
-
-############################################################################
-# #
-# misc functions #
-# #
-############################################################################
-
-
-function xmlrpc_log($message) {
-}
-
-function xmlrpc_die($error="", $str="") {
-}
-
-
-
-############################################################################
-# #
-# data representation mangling #
-# #
-############################################################################
-
-
-#-- Makes compact-array2xml datavar from a PHP variable.
-function xmlrpc_compact_value($var, $n=0) {
-
- #-- create compact-array2xml tree
- $root = array(
- "value,$n" => array(),
- );
- $r = &$root["value,$n"];
-
- #-- detect PHP values to be complex types in XML-RPC
- if (XMLRPC_AUTO_TYPES && is_string($var)) {
- if ((strlen($var) >= 64) && preg_match('/^[\w]+=*$/', $var)) {
- $var = new xmlrpc_base64($var);
- }
- elseif ((strlen($var)==17) && ($var[8]=="T") && preg_match('/^\d{8}T\d\d:\d\d:\d\d$/', $var)) {
- $var = new xmlrpc_datetime($var);
- }
- }
-
- #-- complex types
- if (is_object($var)) {
- $r = $var->out();
- }
- #-- arrays and hashes(structs)
- elseif (is_array($var)) {
- if (isset($var[0]) || empty($var)) {
- $r = array("array,$n" => array("data,0" => array()));
- $r = &$r["array,$n"]["data,0"];
- foreach ($var as $n=>$val) {
- $r = array_merge($r, xmlrpc_compact_value($val, $n));
- }
- }
- else {
- $r = array("struct,$n"=>array());
- $r = &$r["struct,$n"];
- $n = 0;
- foreach ($var as $i=>$val) {
- $r["member,$n"] = array_merge(array(
- "name,0" => array(",0" => "$i"),
- ), xmlrpc_compact_value($val, 1));
- $n++;
- }
- }
- }
- #-- simple types
- elseif (is_bool($var)) {
- $r = array(
- "boolean,$n" => array(",0" => ($var?1:0)),
- );
- }
- elseif (is_int($var)) {
- $r = array(
- "int,$n" => array(",0" => $var),
- );
- }
- elseif (is_float($var)) {
- $r = array(
- "double,$n" => array(",0" => $var),
- );
- }
- elseif (is_string($var)) {
- $r = array(
- "string,$n" => array(",0" => $var),
- );
- }
- return($root);
-}
-
-
-#-- Makes a PHP array from a compact-xml2array representation. $value must
-# always be the xml2array elements _below_ the ["value,0"] or ["data,0"]
-# or ["member,N"] entry.
-function xmlrpc_decode_value($value) {
- $val = NULL;
- foreach ($value as $i=>$d) {
-
- #-- use single (text) content xml2array entry as actual $d var
- if (is_array($d) && isset($d[",0"])) {
- $d = $d[",0"];
- }
-
- #-- convert into PHP var based on type
- $type = strtok($i, ",");
- switch ($type) {
-
- case "array":
- $val = array();
- foreach ($d["data,0"] as $i=>$value) {
- $val[] = xmlrpc_decode_value($value);
- }
- break;
-
- case "struct":
- $val = array();
- foreach ($d as $uu=>$d2) {
- if (($in=$d2["name,0"][",0"]) && ($pos2=1) || ($in=$d2["name,1"][",0"]) && ($pos2=0)) {
- $val[$in] = xmlrpc_decode_value($d2["value,$pos2"]);
- }
- }
- break;
-
- case "": # handles also '<value>s</value>' instead
- case "0": # of '<value><string>s</string></value>'
- case "string":
- $val = is_array($d) ? "" : (string)$d;
- break;
-
- case "base64":
- $val = (XMLRPC_AUTO_TYPES>=2) ? base64_decode($d) : (string)$d;
- if ((XMLRPC_AUTO_UTF8 >= 2) && ($uu = utf8_decode($val))) {
- $val = $uu;
- }
- break;
-
- // case "real": case "float": // neither is allowed
- case "double":
- $val = (double)$d;
- break;
- case "i4":
- case "int":
- $val = (int)$d;
- break;
-
- case "boolean":
- $val = (boolean)$d;
- break;
-
- case "dateTime.iso8601":
- $val = xmlrpc_strtotime($d);
- break;
-
- default:
- if (defined("XMLRPC_SERVER")) {
- xmlrpc_send_response(xmlrpc_error(-32600, "Unknown data type '$type'"));
- }
- else {
- echo $xmlrpc_error = "UNKNOWN XML-RPC DATA TYPE '$type'<br>\n";
- $xmlrpc_errorcode = -32207;
- }
-# echo "<!-- UNKNOWN TYPE $type -->\n";
-# xmlrpc_log("bad data type '$type' enountered");
- }
- }
- return($val);
-}
-
-
-#-- More complex XML-RPC data types need object representation to
-# distinguish them from ordinary string and integer vars.
-class xmlrpc_xtype {
- var $scalar = "";
- var $xmlrpc_type = "string";
- var $tag = "string";
- function xmlrpc_type($str) {
- $this->data = $str;
- }
- function out() {
- return array($this->tag.",0" => array(",0"=>$this->scalar));
- }
-}
-class xmlrpc_base64 extends xmlrpc_xtype {
- function xmlrpc_base64($str) {
- $this->tag = "base64";
- $this->xmlrpc_type = "base64";
- if (XMLRPC_AUTO_UTF8 >= 2) {
- $str = utf8_encode($str);
- }
- if (!preg_match("/^[=\w\s]+$/", $str)) {
- $this->encode=1;
- }
- $this->scalar = $str;
- }
- function out() {
- if (isset($this->encode)) {
- $this->scalar = chunk_split(base64_encode($this->scalar), 74, "\n");
- }
- return xmlrpc_xtype::out();
- }
-}
-class xmlrpc_datetime extends xmlrpc_xtype {
- function xmlrpc_datetime($t) {
- $this->tag = "dateTime.iso8601";
- $this->xmlrpc_type = "datetime";
- if (($t > 0) && ($t[8] != "T")) {
- $this->timestamp = $t;
- $t = xmlrpc_timetostr($t);
- }
- $this->scalar = $t;
- }
-}
-
-#-- Further simplify use of the above ones.
-function xmlrpc_base64($string) {
- return(new xmlrpc_base64($string));
-}
-function xmlrpc_datetime($timestr) {
- return(new xmlrpc_datetime($timestr));
-}
-
-
-#-- Deciphers ISO datetime string into UNIX timestamp.
-function xmlrpc_strtotime($str) {
- $tm = explode(":", substr($str, 9));
- $t = mktime($tm[0], $tm[1], $tm[2], substr($str, 4, 2), substr($str, 6, 2), substr($str, 0, 4));
- return($t);
-}
-function xmlrpc_timetostr($time) {
- return(gmstrftime("%Y%m%dT%T", $time));
-}
-
-
-#-- helping hand for the xmlrpc-epi extension of php
-function xmlrpc_epi_decode_xtypes(&$r) {
- if (is_object($r) && isset($r->xmlrpc_type)) {
- if (isset($r->timestamp)) {
- $r = $r->timestamp;
- }
- else {
- $r = $r->scalar;
- }
- }
- elseif (is_array($r)) {
- foreach ($r as $i=>$v) {
- xmlrpc_epi_decode_xtypes($r[$i]);
- }
- }
-}
-
-
-
-
-############################################################################
-# #
-# simplified XML parser #
-# #
-############################################################################
-
-
-#-- Encode the two chars & and < into htmlentities (there is nothing said
-# about the possible other entities in the XML-RPC spec).
-function xml_entities($str) {
- $e = array(
- "&" => "&",
- "<" => "<",
-// ">" => ">",
- );
- return(strtr($str, $e));
-}
-function xml_decode_entities($str) {
- $e = array(
- "<" => "<",
- ">" => ">",
- "'" => "'",
- """ => '"',
- "&" => "&",
- );
- if (strpos($e, "&#") !== false) {
- $e = preg_replace('/&#(\d+);/e', 'chr($1)', $e);
- $e = preg_replace('/&#x([\da-fA-F]+);/e', 'chr(hexdec("$1"))', $e);
- }
- return(strtr($str, $e));
-}
-
-
-#-- Can split simplified XML into a PHP array structure. The now used
-# 'compact' format will yield tag sub arrays with an "*,0" index and
-# just [",0"] for text nodes.
-function xml2array($xml, $compact="ALWAYS") {
- $r = array();
- if (function_exists("xml_parser_create") && (strlen($xml) >= 512)) {
- $r = xml2array_php($xml);
- }
- else {
- xml2array_parse($xml, $r, $compact);
- }
- return($r);
-}
-
-
-#-- Recursively builds an array of the chunks fetched via strtok() from
-# the original XML input string.
-function xml2array_parse(&$string, &$r, $compact=1) {
- $n = 0;
- do {
- #-- split chunks
- $l = strpos($string, "<");
- $p = strpos($string, ">", $l);
- $text = $attr=$close = $tag = false;
- if ($l === false) {
- $text = $string;
- $string = false;
- }
- else {
- $tag = strtok(substr($string, $l+1, $p-$l-1), " ");
- if ((strncmp($tag, "![CDATA[", 8)==0) && ($p = strpos($string, "]]>", $l))) {
- $text = substr($string, $l+9, $p-$l-9);
- }
- else {
- if ($l) {
- $text = xml_decode_entities(substr($string, 0, $l));
- }
- $attr = strtok("\000");
- $close = $attr && ($attr[strlen($attr)-1]=="/");
- $string = substr($string, $p+1);
- }
- }
- #-- insert text/body content into array
- if (trim($text)) {
-# if ($compact) {
- $r[",$n"] = $text;
-# }
-# else {
-# $r[] = $text;
-# }
- $n++;
- }
- #-- recurse for tags
- if ($tag && ($tag[0] >= 'A')) { #-- don't read <? <! </ pseudo-tags
-# if ($compact) {
- $r["$tag,$n"] = array();
- $new = &$r["$tag,$n"];
-# } else {
-# $r[] = array($tag => array());
-# $new = &$r[count($r)-1][$tag];
-# }
- if (!$close) {
- xml2array_parse($string, $new, $compact);
- }
- $n++;
- }
- #-- stop if no more tags or content
- if (empty($tag) && empty($text) || empty($string)) {
- $tag = "/";
- }
- } while ($tag[0] != "/");
-}
-
-
-#-- Uses the XML extension of PHP to convert an XML stream into the
-# compact array representation.
-function xml2array_php(&$xml, $compact=1) {
-
- $p = xml_parser_create(xml_which_charset($xml));
- xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, false);
- xml_parser_set_option($p, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
-
- xml_parse_into_struct($p, $xml, $struct);
-
- $a = array(); // will hold all tag nodes
- $tree = array(&$a); // stack of pointers to last node of any tree level
- $in = &$a; // pointer to last created node
-
- foreach ($struct as $t) {
- unset($value);
- extract($t);
-
- $depth = count($tree) - 1;
- $in = &$tree[$depth];
- $tag .= "," . count($in);
-//echo "#$depth, TAG=\"$tag\", TYP=$type, LEV=$level, VAL=$value\n";
-
- switch ($type[1]) {
-
- #-- OpEN
- case "p":
- $in[$tag] = array();
- if ($type=="open") {
- $tree[] = &$in[$tag];
- }
- if (isset($value) && trim($value)) {
- $in[$tag][",0"] = $value;
- }
- break;
-
- #-- CoMPLETE
- case "o":
- $in[$tag] = array();
- if (isset($value) && trim($value)) {
- $in[$tag][",0"] = $value;
- }
- break;
-
- #-- ClOSE
- case "l":
- array_pop($tree);
- break;
-
- #-- CdATA - usually just whitespace
- case "d":
- if (isset($value) && trim($value)) {
- $in[",".count($in)] = $value;
- }
- break;
-
- default:
- // case "attribute":
- // and anything else we do not want
- }
-
- }
-
- return($a);
-}
-
-
-
-function xml_which_charset(&$xml) {
- return( strpos(strtok($xml, "\n"), '-8859-1"') ? "iso-8859-1" : "utf-8" );
-}
-
-
-
-############################################################################
-# #
-# simplified XML creator #
-# #
-############################################################################
-
-
-#-- This is the opposite of the above xml2array, and can also work with the
-# so called $compact format.
-function array2xml($r, $compact=1, $ins="") {
- $string = "<?xml version=\"1.0\" $ins?>";
- array2xml_push($string, $r, $compact);
- return($string);
-}
-
-
-#-- Recursively throws out the XMLified tree generated by the xml2array()
-# 'parser' function.
-function array2xml_push(&$string, &$r, $compact, $ind=-1) {
- $old_ind = ++$ind - 1;
- if ($old_ind < 0) { $old_ind = 0; }
- foreach ($r as $i=>$d) {
- $d = &$r[$i];
- if (is_scalar($d)) {
- $string .= xml_entities($d);
- }
- elseif (is_array($d)) {
- if ($compact) {
- $i = strtok($i, ",");
- }
- $ls = str_repeat(" ", $ind);
- $string .= "\n$ls<$i>";
- $string .= array2xml_push($string, $d, $compact, $ind);
- $ls = str_repeat(" ", $old_ind);
- $string .= "</$i>\n$ls";
- }
- }
-}
-
-
-
-
-?>
\ No newline at end of file