summaryrefslogtreecommitdiffstats
path: root/modules/xmlrpc/xmlrpc.inc
diff options
context:
space:
mode:
Diffstat (limited to 'modules/xmlrpc/xmlrpc.inc')
-rwxr-xr-xmodules/xmlrpc/xmlrpc.inc794
1 files changed, 673 insertions, 121 deletions
diff --git a/modules/xmlrpc/xmlrpc.inc b/modules/xmlrpc/xmlrpc.inc
index 5b4c0150..bdb818e2 100755
--- a/modules/xmlrpc/xmlrpc.inc
+++ b/modules/xmlrpc/xmlrpc.inc
@@ -1,7 +1,7 @@
<?php // -*-c++-*-
// by Edd Dumbill (C) 1999-2002
// <edd@usefulinc.com>
-// $Id: xmlrpc.inc,v 1.20 2003/01/10 22:01:56 dilinger Exp $
+// $Id: xmlrpc.inc,v 1.40 2005/04/24 18:17:18 ggiunta Exp $
// Copyright (c) 1999,2000,2002 Edd Dumbill.
@@ -49,6 +49,34 @@
}
}
+ // G. Giunta 2005/01/29: declare global these variables,
+ // so that xmlrpc.inc will work even if included from within a function
+ // NB: it will give warnings in PHP3, so we comment it out
+ // Milosch: Next round, maybe we should explicitly request these via $GLOBALS where used.
+ if (phpversion() >= '4')
+ {
+ global $xmlrpcI4;
+ global $xmlrpcInt;
+ global $xmlrpcDouble;
+ global $xmlrpcString;
+ global $xmlrpcDateTime;
+ global $xmlrpcBase64;
+ global $xmlrpcArray;
+ global $xmlrpcStruct;
+
+ global $xmlrpcTypes;
+ global $xmlEntities;
+ global $xmlrpcerr;
+ global $xmlrpcstr;
+ global $xmlrpc_defencoding;
+ global $xmlrpc_internalencoding;
+ global $xmlrpcName;
+ global $xmlrpcVersion;
+ global $xmlrpcerruser;
+ global $xmlrpcerrxml;
+ global $xmlrpc_backslash;
+ global $_xh;
+ }
$xmlrpcI4='i4';
$xmlrpcInt='int';
$xmlrpcBoolean='boolean';
@@ -82,7 +110,7 @@
$xmlrpcerr['unknown_method']=1;
$xmlrpcstr['unknown_method']='Unknown method';
$xmlrpcerr['invalid_return']=2;
- $xmlrpcstr['invalid_return']='Invalid return payload: enabling debugging to examine incoming payload';
+ $xmlrpcstr['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
$xmlrpcerr['incorrect_params']=3;
$xmlrpcstr['incorrect_params']='Incorrect parameters passed to method';
$xmlrpcerr['introspect_unknown']=4;
@@ -110,13 +138,18 @@
$xmlrpcerr['multicall_notarray'] = 14;
$xmlrpcstr['multicall_notarray'] = 'params is not an array';
+ // The charset encoding expected by the server for received messages and
+ // by the client for received responses
$xmlrpc_defencoding='UTF-8';
+ // The encoding used by PHP.
+ // String values received will be converted to this.
+ $xmlrpc_internalencoding='ISO-8859-1';
$xmlrpcName='XML-RPC for PHP';
- $xmlrpcVersion='1.0.99';
+ $xmlrpcVersion='1.1.1';
// let user errors start at 800
- $xmlrpcerruser=800;
+ $xmlrpcerruser=800;
// let XML parse errors start at 100
$xmlrpcerrxml=100;
@@ -137,13 +170,19 @@
$_xh=array();
- if (!function_exists('xmlrpc_entity_decode')) {
-
+ /**
+ * To help correct communication of non-ascii chars inside strings, regardless
+ * of the charset used when sending requests, parsing them, sending responses
+ * and parsing responses, convert all non-ascii chars present in the message
+ * into their equivalent 'charset entity'. Charset entities enumerated this way
+ * are independent of the charset encoding used to transmit them, and all XML
+ * parsers are bound to understand them.
+ */
function xmlrpc_entity_decode($string)
{
$top=split('&', $string);
$op='';
- $i=0;
+ $i=0;
while($i<sizeof($top))
{
if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs))
@@ -168,8 +207,6 @@
return $op;
}
- } // if xmlrpc_entity_decode doesn't exist
-
function xmlrpc_lookup_entity($ent)
{
global $xmlEntities;
@@ -185,6 +222,299 @@
return '?';
}
+ /**
+ * These entities originate from HTML specs (1.1, proposed 2.0, etc),
+ * and are taken directly from php-4.3.1/ext/mbstring/html_entities.c.
+ * Until php provides functionality to translate these entities in its
+ * core library, use this function.
+ */
+ function xmlrpc_html_entity_xlate($data = '')
+ {
+ $entities = array(
+ "&nbsp;" => "&#160;",
+ "&iexcl;" => "&#161;",
+ "&cent;" => "&#162;",
+ "&pound;" => "&#163;",
+ "&curren;" => "&#164;",
+ "&yen;" => "&#165;",
+ "&brvbar;" => "&#166;",
+ "&sect;" => "&#167;",
+ "&uml;" => "&#168;",
+ "&copy;" => "&#169;",
+ "&ordf;" => "&#170;",
+ "&laquo;" => "&#171;",
+ "&not;" => "&#172;",
+ "&shy;" => "&#173;",
+ "&reg;" => "&#174;",
+ "&macr;" => "&#175;",
+ "&deg;" => "&#176;",
+ "&plusmn;" => "&#177;",
+ "&sup2;" => "&#178;",
+ "&sup3;" => "&#179;",
+ "&acute;" => "&#180;",
+ "&micro;" => "&#181;",
+ "&para;" => "&#182;",
+ "&middot;" => "&#183;",
+ "&cedil;" => "&#184;",
+ "&sup1;" => "&#185;",
+ "&ordm;" => "&#186;",
+ "&raquo;" => "&#187;",
+ "&frac14;" => "&#188;",
+ "&frac12;" => "&#189;",
+ "&frac34;" => "&#190;",
+ "&iquest;" => "&#191;",
+ "&Agrave;" => "&#192;",
+ "&Aacute;" => "&#193;",
+ "&Acirc;" => "&#194;",
+ "&Atilde;" => "&#195;",
+ "&Auml;" => "&#196;",
+ "&Aring;" => "&#197;",
+ "&AElig;" => "&#198;",
+ "&Ccedil;" => "&#199;",
+ "&Egrave;" => "&#200;",
+ "&Eacute;" => "&#201;",
+ "&Ecirc;" => "&#202;",
+ "&Euml;" => "&#203;",
+ "&Igrave;" => "&#204;",
+ "&Iacute;" => "&#205;",
+ "&Icirc;" => "&#206;",
+ "&Iuml;" => "&#207;",
+ "&ETH;" => "&#208;",
+ "&Ntilde;" => "&#209;",
+ "&Ograve;" => "&#210;",
+ "&Oacute;" => "&#211;",
+ "&Ocirc;" => "&#212;",
+ "&Otilde;" => "&#213;",
+ "&Ouml;" => "&#214;",
+ "&times;" => "&#215;",
+ "&Oslash;" => "&#216;",
+ "&Ugrave;" => "&#217;",
+ "&Uacute;" => "&#218;",
+ "&Ucirc;" => "&#219;",
+ "&Uuml;" => "&#220;",
+ "&Yacute;" => "&#221;",
+ "&THORN;" => "&#222;",
+ "&szlig;" => "&#223;",
+ "&agrave;" => "&#224;",
+ "&aacute;" => "&#225;",
+ "&acirc;" => "&#226;",
+ "&atilde;" => "&#227;",
+ "&auml;" => "&#228;",
+ "&aring;" => "&#229;",
+ "&aelig;" => "&#230;",
+ "&ccedil;" => "&#231;",
+ "&egrave;" => "&#232;",
+ "&eacute;" => "&#233;",
+ "&ecirc;" => "&#234;",
+ "&euml;" => "&#235;",
+ "&igrave;" => "&#236;",
+ "&iacute;" => "&#237;",
+ "&icirc;" => "&#238;",
+ "&iuml;" => "&#239;",
+ "&eth;" => "&#240;",
+ "&ntilde;" => "&#241;",
+ "&ograve;" => "&#242;",
+ "&oacute;" => "&#243;",
+ "&ocirc;" => "&#244;",
+ "&otilde;" => "&#245;",
+ "&ouml;" => "&#246;",
+ "&divide;" => "&#247;",
+ "&oslash;" => "&#248;",
+ "&ugrave;" => "&#249;",
+ "&uacute;" => "&#250;",
+ "&ucirc;" => "&#251;",
+ "&uuml;" => "&#252;",
+ "&yacute;" => "&#253;",
+ "&thorn;" => "&#254;",
+ "&yuml;" => "&#255;",
+ "&OElig;" => "&#338;",
+ "&oelig;" => "&#339;",
+ "&Scaron;" => "&#352;",
+ "&scaron;" => "&#353;",
+ "&Yuml;" => "&#376;",
+ "&fnof;" => "&#402;",
+ "&circ;" => "&#710;",
+ "&tilde;" => "&#732;",
+ "&Alpha;" => "&#913;",
+ "&Beta;" => "&#914;",
+ "&Gamma;" => "&#915;",
+ "&Delta;" => "&#916;",
+ "&Epsilon;" => "&#917;",
+ "&Zeta;" => "&#918;",
+ "&Eta;" => "&#919;",
+ "&Theta;" => "&#920;",
+ "&Iota;" => "&#921;",
+ "&Kappa;" => "&#922;",
+ "&Lambda;" => "&#923;",
+ "&Mu;" => "&#924;",
+ "&Nu;" => "&#925;",
+ "&Xi;" => "&#926;",
+ "&Omicron;" => "&#927;",
+ "&Pi;" => "&#928;",
+ "&Rho;" => "&#929;",
+ "&Sigma;" => "&#931;",
+ "&Tau;" => "&#932;",
+ "&Upsilon;" => "&#933;",
+ "&Phi;" => "&#934;",
+ "&Chi;" => "&#935;",
+ "&Psi;" => "&#936;",
+ "&Omega;" => "&#937;",
+ "&beta;" => "&#946;",
+ "&gamma;" => "&#947;",
+ "&delta;" => "&#948;",
+ "&epsilon;" => "&#949;",
+ "&zeta;" => "&#950;",
+ "&eta;" => "&#951;",
+ "&theta;" => "&#952;",
+ "&iota;" => "&#953;",
+ "&kappa;" => "&#954;",
+ "&lambda;" => "&#955;",
+ "&mu;" => "&#956;",
+ "&nu;" => "&#957;",
+ "&xi;" => "&#958;",
+ "&omicron;" => "&#959;",
+ "&pi;" => "&#960;",
+ "&rho;" => "&#961;",
+ "&sigmaf;" => "&#962;",
+ "&sigma;" => "&#963;",
+ "&tau;" => "&#964;",
+ "&upsilon;" => "&#965;",
+ "&phi;" => "&#966;",
+ "&chi;" => "&#967;",
+ "&psi;" => "&#968;",
+ "&omega;" => "&#969;",
+ "&thetasym;" => "&#977;",
+ "&upsih;" => "&#978;",
+ "&piv;" => "&#982;",
+ "&ensp;" => "&#8194;",
+ "&emsp;" => "&#8195;",
+ "&thinsp;" => "&#8201;",
+ "&zwnj;" => "&#8204;",
+ "&zwj;" => "&#8205;",
+ "&lrm;" => "&#8206;",
+ "&rlm;" => "&#8207;",
+ "&ndash;" => "&#8211;",
+ "&mdash;" => "&#8212;",
+ "&lsquo;" => "&#8216;",
+ "&rsquo;" => "&#8217;",
+ "&sbquo;" => "&#8218;",
+ "&ldquo;" => "&#8220;",
+ "&rdquo;" => "&#8221;",
+ "&bdquo;" => "&#8222;",
+ "&dagger;" => "&#8224;",
+ "&Dagger;" => "&#8225;",
+ "&bull;" => "&#8226;",
+ "&hellip;" => "&#8230;",
+ "&permil;" => "&#8240;",
+ "&prime;" => "&#8242;",
+ "&Prime;" => "&#8243;",
+ "&lsaquo;" => "&#8249;",
+ "&rsaquo;" => "&#8250;",
+ "&oline;" => "&#8254;",
+ "&frasl;" => "&#8260;",
+ "&euro;" => "&#8364;",
+ "&weierp;" => "&#8472;",
+ "&image;" => "&#8465;",
+ "&real;" => "&#8476;",
+ "&trade;" => "&#8482;",
+ "&alefsym;" => "&#8501;",
+ "&larr;" => "&#8592;",
+ "&uarr;" => "&#8593;",
+ "&rarr;" => "&#8594;",
+ "&darr;" => "&#8595;",
+ "&harr;" => "&#8596;",
+ "&crarr;" => "&#8629;",
+ "&lArr;" => "&#8656;",
+ "&uArr;" => "&#8657;",
+ "&rArr;" => "&#8658;",
+ "&dArr;" => "&#8659;",
+ "&hArr;" => "&#8660;",
+ "&forall;" => "&#8704;",
+ "&part;" => "&#8706;",
+ "&exist;" => "&#8707;",
+ "&empty;" => "&#8709;",
+ "&nabla;" => "&#8711;",
+ "&isin;" => "&#8712;",
+ "&notin;" => "&#8713;",
+ "&ni;" => "&#8715;",
+ "&prod;" => "&#8719;",
+ "&sum;" => "&#8721;",
+ "&minus;" => "&#8722;",
+ "&lowast;" => "&#8727;",
+ "&radic;" => "&#8730;",
+ "&prop;" => "&#8733;",
+ "&infin;" => "&#8734;",
+ "&ang;" => "&#8736;",
+ "&and;" => "&#8743;",
+ "&or;" => "&#8744;",
+ "&cap;" => "&#8745;",
+ "&cup;" => "&#8746;",
+ "&int;" => "&#8747;",
+ "&there4;" => "&#8756;",
+ "&sim;" => "&#8764;",
+ "&cong;" => "&#8773;",
+ "&asymp;" => "&#8776;",
+ "&ne;" => "&#8800;",
+ "&equiv;" => "&#8801;",
+ "&le;" => "&#8804;",
+ "&ge;" => "&#8805;",
+ "&sub;" => "&#8834;",
+ "&sup;" => "&#8835;",
+ "&nsub;" => "&#8836;",
+ "&sube;" => "&#8838;",
+ "&supe;" => "&#8839;",
+ "&oplus;" => "&#8853;",
+ "&otimes;" => "&#8855;",
+ "&perp;" => "&#8869;",
+ "&sdot;" => "&#8901;",
+ "&lceil;" => "&#8968;",
+ "&rceil;" => "&#8969;",
+ "&lfloor;" => "&#8970;",
+ "&rfloor;" => "&#8971;",
+ "&lang;" => "&#9001;",
+ "&rang;" => "&#9002;",
+ "&loz;" => "&#9674;",
+ "&spades;" => "&#9824;",
+ "&clubs;" => "&#9827;",
+ "&hearts;" => "&#9829;",
+ "&diams;" => "&#9830;");
+ return strtr($data, $entities);
+ }
+
+ function xmlrpc_encode_entitites($data)
+ {
+ $length = strlen($data);
+ $escapeddata = "";
+ for($position = 0; $position < $length; $position++)
+ {
+ $character = substr($data, $position, 1);
+ $code = Ord($character);
+ switch($code) {
+ case 34:
+ $character = "&quot;";
+ break;
+ case 38:
+ $character = "&amp;";
+ break;
+ case 39:
+ $character = "&apos;";
+ break;
+ case 60:
+ $character = "&lt;";
+ break;
+ case 62:
+ $character = "&gt;";
+ break;
+ default:
+ if ($code < 32 || $code > 159)
+ $character = ("&#".strval($code).";");
+ break;
+ }
+ $escapeddata .= $character;
+ }
+ return $escapeddata;
+ }
+
function xmlrpc_se($parser, $name, $attrs)
{
global $_xh, $xmlrpcDateTime, $xmlrpcString;
@@ -196,12 +526,13 @@
$_xh[$parser]['st'].='array(';
$_xh[$parser]['cm']++;
// this last line turns quoting off
- // this means if we get an empty array we'll
+ // this means if we get an empty array we'll
// simply get a bit of whitespace in the eval
$_xh[$parser]['qt']=0;
break;
case 'NAME':
- $_xh[$parser]['st'].="'"; $_xh[$parser]['ac']='';
+ $_xh[$parser]['st'].='"';
+ $_xh[$parser]['ac']='';
break;
case 'FAULT':
$_xh[$parser]['isf']=1;
@@ -278,11 +609,13 @@
$_xh[$parser]['cm']--;
break;
case 'NAME':
- $_xh[$parser]['st'].= $_xh[$parser]['ac'] . "' => ";
+ $_xh[$parser]['st'].= $_xh[$parser]['ac'] . '" => ';
break;
case 'BOOLEAN':
// special case here: we translate boolean 1 or 0 into PHP
// constants true or false
+ // NB: this simple checks helps a lot sanitizing input, ie no
+ // security problems around here
if ($_xh[$parser]['ac']=='1')
{
$_xh[$parser]['ac']='true';
@@ -290,9 +623,9 @@
else
{
$_xh[$parser]['ac']='false';
- $_xh[$parser]['vt']=strtolower($name);
- // Drop through intentionally.
}
+ $_xh[$parser]['vt']=strtolower($name);
+ // Drop through intentionally.
case 'I4':
case 'INT':
case 'STRING':
@@ -306,30 +639,48 @@
}
elseif ($_xh[$parser]['qt']==2)
{
- $_xh[$parser]['st'].="base64_decode('". $_xh[$parser]['ac'] . "')";
+ $_xh[$parser]['st'].='base64_decode("'. $_xh[$parser]['ac'] . '")';
}
elseif ($name=='BOOLEAN')
{
$_xh[$parser]['st'].=$_xh[$parser]['ac'];
}
- else
+ elseif ($name=='DOUBLE')
{
- // we have an I4, INT or a DOUBLE
+ // we have a DOUBLE
// we must check that only 0123456789-.<space> are characters here
- if (!ereg("^\-?[0123456789 \t\.]+$", $_xh[$parser]['ac']))
+ if (!ereg("^[+-]?[eE0123456789 \\t\\.]+$", $_xh[$parser]['ac']))
{
// TODO: find a better way of throwing an error
// than this!
- error_log('XML-RPC: non numeric value received in INT or DOUBLE');
- $_xh[$parser]['st'].='ERROR_NON_NUMERIC_FOUND';
+ error_log('XML-RPC: non numeric value received in DOUBLE: '.$_xh[$parser]['ac']);
+ $_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
}
else
{
// it's ok, add it on
- $_xh[$parser]['st'].=$_xh[$parser]['ac'];
+ $_xh[$parser]['st'].=(double)$_xh[$parser]['ac'];
}
}
- $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
+ else
+ {
+ // we have an I4/INT
+ // we must check that only 0123456789-<space> are characters here
+ if (!ereg("^[+-]?[0123456789 \\t]+$", $_xh[$parser]['ac']))
+ {
+ // TODO: find a better way of throwing an error
+ // than this!
+ error_log('XML-RPC: non numeric value received in INT: '.$_xh[$parser]['ac']);
+ $_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
+ }
+ else
+ {
+ // it's ok, add it on
+ $_xh[$parser]['st'].=(int)$_xh[$parser]['ac'];
+ }
+ }
+ $_xh[$parser]['ac']='';
+ $_xh[$parser]['qt']=0;
$_xh[$parser]['lv']=3; // indicate we've found a value
break;
case 'VALUE':
@@ -337,7 +688,7 @@
if (strlen($_xh[$parser]['ac'])>0 &&
$_xh[$parser]['vt']==$xmlrpcString)
{
- $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
+ $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
}
// This if() detects if no scalar was inside <VALUE></VALUE>
// and pads an empty ''.
@@ -345,17 +696,23 @@
{
$_xh[$parser]['st'].= '""';
}
- $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
+ // G. Giunta 2005/03/12 save some chars in the reconstruction of string vals...
+ if ($_xh[$parser]['vt'] != $xmlrpcString)
+ $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
+ else
+ $_xh[$parser]['st'].=")";
if ($_xh[$parser]['cm'])
{
$_xh[$parser]['st'].=',';
}
break;
case 'MEMBER':
- $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
+ $_xh[$parser]['ac']='';
+ $_xh[$parser]['qt']=0;
break;
case 'DATA':
- $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0;
+ $_xh[$parser]['ac']='';
+ $_xh[$parser]['qt']=0;
break;
case 'PARAM':
$_xh[$parser]['params'][]=$_xh[$parser]['st'];
@@ -363,7 +720,8 @@
case 'METHODNAME':
$_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']);
break;
- case 'BOOLEAN':
+ // BOOLEAN HAS BEEN ENUMERATED ABOVE!
+ /*case 'BOOLEAN':
// special case here: we translate boolean 1 or 0 into PHP
// constants true or false
if ($_xh[$parser]['ac']=='1')
@@ -375,7 +733,7 @@
$_xh[$parser]['ac']='false';
$_xh[$parser]['vt']=strtolower($name);
}
- break;
+ break;*/
default:
break;
}
@@ -401,9 +759,9 @@
{
// if we've found text and we're just in a <value> then
// turn quoting on, as this will be a string
- $_xh[$parser]['qt']=1;
+ $_xh[$parser]['qt']=1;
// and say we've found a value
- $_xh[$parser]['lv']=2;
+ $_xh[$parser]['lv']=2;
}
if(!@isset($_xh[$parser]['ac']))
{
@@ -415,13 +773,13 @@
function xmlrpc_dh($parser, $data)
{
- global $_xh;
+ global $_xh, $xmlrpc_backslash;
if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
{
if ($_xh[$parser]['lv']==1)
{
- $_xh[$parser]['qt']=1;
- $_xh[$parser]['lv']=2;
+ $_xh[$parser]['qt']=1;
+ $_xh[$parser]['lv']=2;
}
$_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
}
@@ -433,7 +791,7 @@
var $server;
var $port;
var $errno;
- var $errstring;
+ var $errstr;
var $debug=0;
var $username='';
var $password='';
@@ -512,20 +870,25 @@
function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='')
{
- global $xmlrpcerr, $xmlrpcstr;
+ global $xmlrpcerr, $xmlrpcstr, $xmlrpcName, $xmlrpcVersion, $xmlrpc_defencoding;
if ($port==0)
{
$port=80;
}
if($timeout>0)
{
- $fp=fsockopen($server, $port,$this->errno, $this->errstr, $timeout);
+ $fp=@fsockopen($server, $port,$this->errno, $this->errstr, $timeout);
}
else
{
- $fp=fsockopen($server, $port,$this->errno, $this->errstr);
+ $fp=@fsockopen($server, $port,$this->errno, $this->errstr);
}
- if (!$fp)
+ if ($fp)
+ {
+ if ($timeout>0 && function_exists('stream_set_timeout'))
+ stream_set_timeout($fp, $timeout);
+ }
+ else
{
$this->errstr='Connect error';
$r=new xmlrpcresp(0, $xmlrpcerr['http_error'],$xmlrpcstr['http_error']);
@@ -545,9 +908,11 @@
$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
}
- $op= "POST " . $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" .
- "Host: ". $this->server . "\r\n" .
+ $op= "POST " . $this->path. " HTTP/1.0\r\n" .
+ "User-Agent: " . $xmlrpcName . " " . $xmlrpcVersion . "\r\n" .
+ "Host: ". $server . "\r\n" .
$credentials .
+ "Accept-Charset: " . $xmlrpc_defencoding . "\r\n" .
"Content-Type: text/xml\r\nContent-Length: " .
strlen($msg->payload) . "\r\n\r\n" .
$msg->payload;
@@ -567,7 +932,7 @@
// requires curl to be built into PHP
function sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='')
{
- global $xmlrpcerr, $xmlrpcstr;
+ global $xmlrpcerr, $xmlrpcstr, $xmlrpcVersion, $xmlrpc_internalencoding;
if ($port == 0)
{
$port = 443;
@@ -594,7 +959,7 @@
{
curl_setopt($curl, CURLOPT_VERBOSE, 1);
}
- curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0');
+ curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC '.$xmlrpcVersion);
// required for XMLRPC
curl_setopt($curl, CURLOPT_POST, 1);
// post the data
@@ -602,7 +967,7 @@
// the data
curl_setopt($curl, CURLOPT_HEADER, 1);
// return the header too
- curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml', 'Accept-Charset: '.$xmlrpc_internalencoding));
// whether to verify remote host's cert
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
// whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
@@ -635,12 +1000,13 @@
{
$this->errstr='no response';
$resp=new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail']. ': '. curl_error($curl));
+ curl_close($curl);
}
else
{
+ curl_close($curl);
$resp = $msg->parseResponse($result);
}
- curl_close($curl);
return $resp;
}
@@ -653,10 +1019,10 @@
$results = $this->_try_multicall($msgs, $timeout, $method);
/* TODO - this is not php3-friendly */
// if($results !== false)
- if($results != false)
+ if(is_array($results))
{
// Either the system.multicall succeeded, or the send
- // failed (e.g. due to HTTP timeout). In either case,
+ // failed (e.g. due to HTTP timeout). In either case,
// we're done for now.
return $results;
}
@@ -704,19 +1070,27 @@
// Attempt RPC call
$result = $this->send($multicall, $timeout, $method);
- if (!is_object($result))
+ if(!is_object($result))
+ {
return ($result || 0); // transport failed
+ }
- if ($result->faultCode() != 0)
+ if($result->faultCode() != 0)
+ {
return false; // system.multicall failed
+ }
// Unpack responses.
$rets = $result->value();
- if ($rets->kindOf() != 'array')
+ if($rets->kindOf() != 'array')
+ {
return false; // bad return type from system.multicall
+ }
$numRets = $rets->arraysize();
- if ($numRets != count($msgs))
+ if($numRets != count($msgs))
+ {
return false; // wrong number of return values.
+ }
$response = array();
for ($i = 0; $i < $numRets; $i++)
@@ -725,18 +1099,24 @@
switch ($val->kindOf())
{
case 'array':
- if ($val->arraysize() != 1)
+ if($val->arraysize() != 1)
+ {
return false; // Bad value
+ }
// Normal return value
$response[$i] = new xmlrpcresp($val->arraymem(0));
break;
case 'struct':
$code = $val->structmem('faultCode');
- if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
+ if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
+ {
return false;
+ }
$str = $val->structmem('faultString');
- if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
+ if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
+ {
return false;
+ }
$response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
break;
default:
@@ -760,12 +1140,13 @@
{
// error
$this->errno = $fcode;
- $this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
+ $this->errstr = $fstr;
+ //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
}
- else if (!is_object($val))
+ elseif (!is_object($val))
{
// programmer error
- error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
+ error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
$this->val = new xmlrpcval();
}
else
@@ -795,6 +1176,7 @@
$result = "<methodResponse>\n";
if ($this->errno)
{
+ // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
$result .= '<fault>
<value>
<struct>
@@ -804,7 +1186,7 @@
</member>
<member>
<name>faultString</name>
-<value><string>' . $this->errstr . '</string></value>
+<value><string>' . xmlrpc_encode_entitites($this->errstr) . '</string></value>
</member>
</struct>
</value>
@@ -813,8 +1195,8 @@
else
{
$result .= "<params>\n<param>\n" .
- $this->val->serialize() .
- "</param>\n</params>";
+ $this->val->serialize() .
+ "</param>\n</params>";
}
$result .= "\n</methodResponse>";
return $result;
@@ -842,7 +1224,7 @@
function xml_header()
{
- return "<?xml version=\"1.0\"?>\n<methodCall>\n";
+ return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
}
function xml_footer()
@@ -865,7 +1247,7 @@
$this->payload.="</params>\n";
// }
$this->payload.=$this->xml_footer();
- $this->payload=str_replace("\n", "\r\n", $this->payload);
+ //$this->payload=str_replace("\n", "\r\n", $this->payload);
}
function method($meth='')
@@ -900,24 +1282,7 @@
function parseResponse($data='')
{
global $_xh,$xmlrpcerr,$xmlrpcstr;
- global $xmlrpc_defencoding;
-
- $parser = xml_parser_create($xmlrpc_defencoding);
-
- $_xh[$parser]=array();
-
- $_xh[$parser]['st']='';
- $_xh[$parser]['cm']=0;
- $_xh[$parser]['isf']=0;
- $_xh[$parser]['ac']='';
- $_xh[$parser]['qt']='';
- $_xh[$parser]['headers'] = array();
-
- 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');
- $xmlrpc_value=new xmlrpcval;
+ global $xmlrpc_defencoding, $xmlrpc_internalencoding;
$hdrfnd = 0;
if($this->debug)
@@ -930,45 +1295,128 @@
{
error_log('No response received from server.');
$r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']);
- xml_parser_free($parser);
return $r;
}
// see if we got an HTTP 200 OK, else bomb
// but only do this if we're using the HTTP protocol.
- if(ereg("^HTTP",$data) && !ereg("^HTTP/[0-9\.]+ 200 ", $data))
+ if(ereg("^HTTP",$data))
{
- $errstr= substr($data, 0, strpos($data, "\n")-1);
- error_log('HTTP error, got response: ' .$errstr);
- $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')');
- xml_parser_free($parser);
- return $r;
+ // Strip HTTP 1.1 100 Continue header if present
+ while (ereg('^HTTP/1.1 1[0-9]{2}', $data))
+ {
+ $pos = strpos($data, 'HTTP', 12);
+ // server sent a Continue header without any (valid) content following...
+ // give the client a chance to know it
+ if (!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
+ break;
+ $data = substr($data, $pos);
+ }
+ if (!ereg("^HTTP/[0-9\\.]+ 200 ", $data))
+ {
+ $errstr= substr($data, 0, strpos($data, "\n")-1);
+ error_log('HTTP error, got response: ' .$errstr);
+ $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')');
+ return $r;
+ }
}
+ $parser = xml_parser_create($xmlrpc_defencoding);
+
+ // G. Giunta 2004/04/06
+ // Clean up the accumulator, or it will grow indefinitely long
+ // if making xmlrpc calls for a while
+ $_xh=array();
+ $_xh[$parser]=array();
+ $_xh[$parser]['headers'] = array();
// separate HTTP headers from data
if (ereg("^HTTP", $data))
{
- $ar = split("\r\n", $data);
- while (($line = array_shift($ar)))
+ // be tolerant to usage of \n instead of \r\n to separate headers and data
+ // (even though it is not valid http)
+ $pos = strpos($data,"\r\n\r\n");
+ if($pos || is_int($pos))
+ $bd = $pos+4;
+ else
{
- if (strlen($line) < 1)
+ $pos = strpos($data,"\n\n");
+ if($pos || is_int($pos))
+ $bd = $pos+2;
+ else
{
- break;
+ // No separation between response headers and body: fault?
+ $bd = 0;
}
- $_xh[$parser]['headers'][] = $line;
}
- $data = join("\r\n", $ar);
- }
-
- if ($this->debug && count($_xh[$parser]['headers']))
- {
- print "<PRE>";
- foreach ($_xh[$parser]['headers'] as $header)
+ // be tolerant to line endings, and extra empty lines
+ $ar = split("\r?\n", trim(substr($data, 0, $pos)));
+ while (list(,$line) = @each($ar))
{
- print "HEADER: $header\n";
+ // take care of multi-line headers
+ $arr = explode(':',$line);
+ if(count($arr) > 1)
+ {
+ $header_name = trim($arr[0]);
+ // TO DO: some headers (the ones that allow a CSV list of values)
+ // do allow many values to be passed using multiple header lines.
+ // We should add content to $_xh[$parser]['headers'][$header_name]
+ // instead of replacing it for those...
+ $_xh[$parser]['headers'][$header_name] = $arr[1];
+ for ($i = 2; $i < count($arr); $i++)
+ {
+ $_xh[$parser]['headers'][$header_name] .= ':'.$arr[$i];
+ } // while
+ $_xh[$parser]['headers'][$header_name] = trim($_xh[$parser]['headers'][$header_name]);
+ } else if (isset($header_name))
+ {
+ $_xh[$parser]['headers'][$header_name] .= ' ' . trim($line);
+ }
+ }
+ $data = substr($data, $bd);
+
+ if ($this->debug && count($_xh[$parser]['headers']))
+ {
+ print '<PRE>';
+ //foreach ($_xh[$parser]['headers'] as $header)
+ @reset($_xh[$parser]['headers']);
+ while(list($header, $value) = @each($_xh[$parser]['headers']))
+ {
+ print "HEADER: $header: $value\n";
+ }
+ print "</PRE>\n";
}
- print "</PRE>\n";
}
+ // be tolerant of extra whitespace in response body
+ $data = trim($data);
+
+ // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
+ // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
+ $bd = false;
+ $pos = strpos($data, "</methodResponse>");
+ while ($pos || is_int($pos))
+ {
+ $bd = $pos+17;
+ $pos = strpos($data, "</methodResponse>", $bd);
+ }
+ if ($bd)
+ $data = substr($data, 0, $bd);
+
+ $_xh[$parser]['st']='';
+ $_xh[$parser]['cm']=0;
+ $_xh[$parser]['isf']=0;
+ $_xh[$parser]['ac']='';
+ $_xh[$parser]['qt']='';
+
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
+ // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
+ // the xml parser to give us back data in the expected charset
+ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
+
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
+ xml_set_character_data_handler($parser, 'xmlrpc_cd');
+ xml_set_default_handler($parser, 'xmlrpc_dh');
+ //$xmlrpc_value=new xmlrpcval;
+
if (!xml_parse($parser, $data, sizeof($data)))
{
// thanks to Peter Kocks <peter.kocks@baygate.com>
@@ -981,18 +1429,20 @@
$errstr = sprintf('XML error: %s at line %d',
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser));
- error_log($errstr);
- $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
- xml_parser_free($parser);
- echo $errstr;
- return $r;
}
+ error_log($errstr);
+ $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return'].' ('.$errstr.')');
+ xml_parser_free($parser);
+ if ($this->debug)
+ echo $errstr;
+ $r->hdrs = $_xh[$parser]['headers'];
+ return $r;
}
xml_parser_free($parser);
if ($this->debug)
{
- print "<PRE>---EVALING---[" .
- strlen($_xh[$parser]['st']) . " chars]---\n" .
+ print "<PRE>---EVALING---[" .
+ strlen($_xh[$parser]['st']) . " chars]---\n" .
htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
}
if (strlen($_xh[$parser]['st'])==0)
@@ -1005,7 +1455,13 @@
}
else
{
- eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
+ $allOK=0;
+ @eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
+ if (!$allOK)
+ {
+ $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
+ }
+ else
if ($_xh[$parser]['isf'])
{
$errno_v = $v->structmem('faultCode');
@@ -1041,7 +1497,7 @@
global $xmlrpcTypes;
$this->me=array();
$this->mytype=0;
- if ($val!=-1 || $type!='')
+ if ($val!=-1 || !is_int($val) || $type!='')
{
if ($type=='')
{
@@ -1172,7 +1628,7 @@
$rs='';
global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
$xmlrpcBoolean;
- switch($xmlrpcTypes[$typ])
+ switch(@$xmlrpcTypes[$typ])
{
case 3:
// struct
@@ -1205,7 +1661,10 @@
$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
break;
case $xmlrpcString:
- $rs.="<${typ}>" . htmlspecialchars($val). "</${typ}>";
+ // G. Giunta 2005/2/13: do NOT use htmlentities, since
+ // it will produce named html entities, which are invalid xml
+ $rs.="<${typ}>" . xmlrpc_encode_entitites($val). "</${typ}>";
+ // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
break;
default:
$rs.="<${typ}>${val}</${typ}>";
@@ -1224,7 +1683,7 @@
function serializeval($o)
{
- global $xmlrpcTypes;
+ //global $xmlrpcTypes;
$rs='';
$ar=$o->me;
reset($ar);
@@ -1292,7 +1751,7 @@
function scalarval()
{
- global $xmlrpcBoolean, $xmlrpcBase64;
+ //global $xmlrpcBoolean, $xmlrpcBase64;
reset($this->me);
list($a,$b)=each($this->me);
return $b;
@@ -1381,7 +1840,7 @@
* *
* author: Dan Libby (dan@libby.com) *
****************************************************************/
- function old_xmlrpc_decode($xmlrpc_val)
+ function php_xmlrpc_decode($xmlrpc_val)
{
$kind = $xmlrpc_val->kindOf();
@@ -1396,9 +1855,9 @@
for($i = 0; $i < $size; $i++)
{
- $arr[]=old_xmlrpc_decode($xmlrpc_val->arraymem($i));
+ $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i));
}
- return $arr;
+ return $arr;
}
elseif($kind == 'struct')
{
@@ -1407,12 +1866,52 @@
while(list($key,$value)=$xmlrpc_val->structeach())
{
- $arr[$key] = old_xmlrpc_decode($value);
+ $arr[$key] = php_xmlrpc_decode($value);
}
return $arr;
}
}
+ if(function_exists('xmlrpc_decode'))
+ {
+ define('XMLRPC_EPI_ENABLED','1');
+ }
+ else
+ {
+ define('XMLRPC_EPI_ENABLED','0');
+ function xmlrpc_decode($xmlrpc_val)
+ {
+ $kind = $xmlrpc_val->kindOf();
+
+ if($kind == 'scalar')
+ {
+ return $xmlrpc_val->scalarval();
+ }
+ elseif($kind == 'array')
+ {
+ $size = $xmlrpc_val->arraysize();
+ $arr = array();
+
+ for($i = 0; $i < $size; $i++)
+ {
+ $arr[]=xmlrpc_decode($xmlrpc_val->arraymem($i));
+ }
+ return $arr;
+ }
+ elseif($kind == 'struct')
+ {
+ $xmlrpc_val->structreset();
+ $arr = array();
+
+ while(list($key,$value)=$xmlrpc_val->structeach())
+ {
+ $arr[$key] = xmlrpc_decode($value);
+ }
+ return $arr;
+ }
+ }
+ }
+
/****************************************************************
* xmlrpc_encode takes native php types and encodes them into *
* xmlrpc PHP object format. *
@@ -1425,7 +1924,7 @@
* *
* author: Dan Libby (dan@libby.com) *
****************************************************************/
- function old_xmlrpc_encode($php_val)
+ function php_xmlrpc_encode($php_val)
{
global $xmlrpcInt;
global $xmlrpcDouble;
@@ -1444,7 +1943,7 @@
$arr = array();
while (list($k,$v) = each($php_val))
{
- $arr[$k] = old_xmlrpc_encode($v);
+ $arr[$k] = php_xmlrpc_encode($v);
}
$xmlrpc_val->addStruct($arr);
break;
@@ -1463,14 +1962,67 @@
$xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
break;
// </G_Giunta_2001-02-29>
- case 'unknown type':
+ // catch "resource", "NULL", "user function", "unknown type"
+ //case 'unknown type':
default:
// giancarlo pinerolo <ping@alt.it>
// it has to return
// an empty object in case (which is already
// at this point), not a boolean.
break;
+ }
+ return $xmlrpc_val;
+ }
+
+ if(XMLRPC_EPI_ENABLED == '0')
+ {
+ function xmlrpc_encode($php_val)
+ {
+ global $xmlrpcInt;
+ global $xmlrpcDouble;
+ global $xmlrpcString;
+ global $xmlrpcArray;
+ global $xmlrpcStruct;
+ global $xmlrpcBoolean;
+
+ $type = gettype($php_val);
+ $xmlrpc_val = new xmlrpcval;
+
+ switch($type)
+ {
+ case 'array':
+ case 'object':
+ $arr = array();
+ while (list($k,$v) = each($php_val))
+ {
+ $arr[$k] = xmlrpc_encode($v);
+ }
+ $xmlrpc_val->addStruct($arr);
+ break;
+ case 'integer':
+ $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
+ break;
+ case 'double':
+ $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
+ break;
+ case 'string':
+ $xmlrpc_val->addScalar($php_val, $xmlrpcString);
+ break;
+ // <G_Giunta_2001-02-29>
+ // Add support for encoding/decoding of booleans, since they are supported in PHP
+ case 'boolean':
+ $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
+ break;
+ // </G_Giunta_2001-02-29>
+ //case 'unknown type':
+ default:
+ // giancarlo pinerolo <ping@alt.it>
+ // it has to return
+ // an empty object in case (which is already
+ // at this point), not a boolean.
+ break;
+ }
+ return $xmlrpc_val;
}
- return $xmlrpc_val;
}
?>