PageRenderTime 238ms CodeModel.GetById 11ms app.highlight 204ms RepoModel.GetById 1ms app.codeStats 1ms

/ext/php_xmlrpc/xmlrpc.inc

http://mp-rechnungs-und-kundenverwaltung.googlecode.com/
PHP | 2143 lines | 1520 code | 109 blank | 514 comment | 262 complexity | 621dba3017b7a068d0755e2bf315e0df MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2// by Edd Dumbill (C) 1999-2002
   3// <edd@usefulinc.com>
   4// $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $
   5
   6// Copyright (c) 1999,2000,2002 Edd Dumbill.
   7// All rights reserved.
   8//
   9// Redistribution and use in source and binary forms, with or without
  10// modification, are permitted provided that the following conditions
  11// are met:
  12//
  13//    * Redistributions of source code must retain the above copyright
  14//      notice, this list of conditions and the following disclaimer.
  15//
  16//    * Redistributions in binary form must reproduce the above
  17//      copyright notice, this list of conditions and the following
  18//      disclaimer in the documentation and/or other materials provided
  19//      with the distribution.
  20//
  21//    * Neither the name of the "XML-RPC for PHP" nor the names of its
  22//      contributors may be used to endorse or promote products derived
  23//      from this software without specific prior written permission.
  24//
  25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  28// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  29// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  30// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  34// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  36// OF THE POSSIBILITY OF SUCH DAMAGE.
  37
  38	if(!function_exists('xml_parser_create'))
  39	{
  40		// For PHP 4 onward, XML functionality is always compiled-in on windows:
  41		// no more need to dl-open it. It might have been compiled out on *nix...
  42		if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
  43		{
  44			dl('xml.so');
  45		}
  46	}
  47
  48	// Try to be backward compat with php < 4.2 (are we not being nice ?)
  49	$phpversion = phpversion();
  50	if($phpversion[0] == '4' && $phpversion[2] < 2)
  51	{
  52		// give an opportunity to user to specify where to include other files from
  53		if(!defined('PHP_XMLRPC_COMPAT_DIR'))
  54		{
  55			define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
  56		}
  57		if($phpversion[2] == '0')
  58		{
  59			if($phpversion[4] < 6)
  60			{
  61				include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php');
  62			}
  63			include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php');
  64			include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php');
  65			include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php');
  66		}
  67		include(PHP_XMLRPC_COMPAT_DIR.'var_export.php');
  68		include(PHP_XMLRPC_COMPAT_DIR.'is_a.php');
  69	}
  70
  71	// G. Giunta 2005/01/29: declare global these variables,
  72	// so that xmlrpc.inc will work even if included from within a function
  73	// Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
  74	$GLOBALS['xmlrpcI4']='i4';
  75	$GLOBALS['xmlrpcInt']='int';
  76	$GLOBALS['xmlrpcBoolean']='boolean';
  77	$GLOBALS['xmlrpcDouble']='double';
  78	$GLOBALS['xmlrpcString']='string';
  79	$GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
  80	$GLOBALS['xmlrpcBase64']='base64';
  81	$GLOBALS['xmlrpcArray']='array';
  82	$GLOBALS['xmlrpcStruct']='struct';
  83	$GLOBALS['xmlrpcValue']='undefined';
  84
  85	$GLOBALS['xmlrpcTypes']=array(
  86		$GLOBALS['xmlrpcI4']       => 1,
  87		$GLOBALS['xmlrpcInt']      => 1,
  88		$GLOBALS['xmlrpcBoolean']  => 1,
  89		$GLOBALS['xmlrpcString']   => 1,
  90		$GLOBALS['xmlrpcDouble']   => 1,
  91		$GLOBALS['xmlrpcDateTime'] => 1,
  92		$GLOBALS['xmlrpcBase64']   => 1,
  93		$GLOBALS['xmlrpcArray']    => 2,
  94		$GLOBALS['xmlrpcStruct']   => 3
  95	);
  96
  97	$GLOBALS['xmlrpc_valid_parents'] = array(
  98		'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
  99		'BOOLEAN' => array('VALUE'),
 100		'I4' => array('VALUE'),
 101		'INT' => array('VALUE'),
 102		'STRING' => array('VALUE'),
 103		'DOUBLE' => array('VALUE'),
 104		'DATETIME.ISO8601' => array('VALUE'),
 105		'BASE64' => array('VALUE'),
 106		'MEMBER' => array('STRUCT'),
 107		'NAME' => array('MEMBER'),
 108		'DATA' => array('ARRAY'),
 109		'ARRAY' => array('VALUE'),
 110		'STRUCT' => array('VALUE'),
 111		'PARAM' => array('PARAMS'),
 112		'METHODNAME' => array('METHODCALL'),
 113		'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
 114		'FAULT' => array('METHODRESPONSE'),
 115		'NIL' => array('VALUE') // only used when extension activated
 116	);
 117
 118	// define extra types for supporting NULL (useful for json or <NIL/>)
 119	$GLOBALS['xmlrpcNull']='null';
 120	$GLOBALS['xmlrpcTypes']['null']=1;
 121
 122	// Not in use anymore since 2.0. Shall we remove it?
 123	/// @deprecated
 124	$GLOBALS['xmlEntities']=array(
 125		'amp'  => '&',
 126		'quot' => '"',
 127		'lt'   => '<',
 128		'gt'   => '>',
 129		'apos' => "'"
 130	);
 131
 132	// tables used for transcoding different charsets into us-ascii xml
 133
 134	$GLOBALS['xml_iso88591_Entities']=array();
 135	$GLOBALS['xml_iso88591_Entities']['in'] = array();
 136	$GLOBALS['xml_iso88591_Entities']['out'] = array();
 137	for ($i = 0; $i < 32; $i++)
 138	{
 139		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 140		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
 141	}
 142	for ($i = 160; $i < 256; $i++)
 143	{
 144		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 145		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
 146	}
 147
 148	/// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
 149	/// These will NOT be present in true ISO-8859-1, but will save the unwary
 150	/// windows user from sending junk (though no luck when reciving them...)
 151  /*
 152	$GLOBALS['xml_cp1252_Entities']=array();
 153	for ($i = 128; $i < 160; $i++)
 154	{
 155		$GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
 156	}
 157	$GLOBALS['xml_cp1252_Entities']['out'] = array(
 158		'&#x20AC;', '?',        '&#x201A;', '&#x0192;',
 159		'&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
 160		'&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
 161		'&#x0152;', '?',        '&#x017D;', '?',
 162		'?',        '&#x2018;', '&#x2019;', '&#x201C;',
 163		'&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
 164		'&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
 165		'&#x0153;', '?',        '&#x017E;', '&#x0178;'
 166	);
 167  */
 168
 169	$GLOBALS['xmlrpcerr'] = array(
 170	'unknown_method'=>1,
 171	'invalid_return'=>2,
 172	'incorrect_params'=>3,
 173	'introspect_unknown'=>4,
 174	'http_error'=>5,
 175	'no_data'=>6,
 176	'no_ssl'=>7,
 177	'curl_fail'=>8,
 178	'invalid_request'=>15,
 179	'no_curl'=>16,
 180	'server_error'=>17,
 181	'multicall_error'=>18,
 182	'multicall_notstruct'=>9,
 183	'multicall_nomethod'=>10,
 184	'multicall_notstring'=>11,
 185	'multicall_recursion'=>12,
 186	'multicall_noparams'=>13,
 187	'multicall_notarray'=>14,
 188
 189	'cannot_decompress'=>103,
 190	'decompress_fail'=>104,
 191	'dechunk_fail'=>105,
 192	'server_cannot_decompress'=>106,
 193	'server_decompress_fail'=>107
 194	);
 195
 196	$GLOBALS['xmlrpcstr'] = array(
 197	'unknown_method'=>'Unknown method',
 198	'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
 199	'incorrect_params'=>'Incorrect parameters passed to method',
 200	'introspect_unknown'=>"Can't introspect: method unknown",
 201	'http_error'=>"Didn't receive 200 OK from remote server.",
 202	'no_data'=>'No data received from server.',
 203	'no_ssl'=>'No SSL support compiled in.',
 204	'curl_fail'=>'CURL error',
 205	'invalid_request'=>'Invalid request payload',
 206	'no_curl'=>'No CURL support compiled in.',
 207	'server_error'=>'Internal server error',
 208	'multicall_error'=>'Received from server invalid multicall response',
 209	'multicall_notstruct'=>'system.multicall expected struct',
 210	'multicall_nomethod'=>'missing methodName',
 211	'multicall_notstring'=>'methodName is not a string',
 212	'multicall_recursion'=>'recursive system.multicall forbidden',
 213	'multicall_noparams'=>'missing params',
 214	'multicall_notarray'=>'params is not an array',
 215
 216	'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
 217	'decompress_fail'=>'Received from server invalid compressed HTTP',
 218	'dechunk_fail'=>'Received from server invalid chunked HTTP',
 219	'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
 220	'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
 221	);
 222
 223	// The charset encoding used by the server for received messages and
 224	// by the client for received responses when received charset cannot be determined
 225	// or is not supported
 226	$GLOBALS['xmlrpc_defencoding']='UTF-8';
 227
 228	// The encoding used internally by PHP.
 229	// String values received as xml will be converted to this, and php strings will be converted to xml
 230	// as if having been coded with this
 231	$GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
 232
 233	$GLOBALS['xmlrpcName']='XML-RPC for PHP';
 234	$GLOBALS['xmlrpcVersion']='2.2.2';
 235
 236	// let user errors start at 800
 237	$GLOBALS['xmlrpcerruser']=800;
 238	// let XML parse errors start at 100
 239	$GLOBALS['xmlrpcerrxml']=100;
 240
 241	// formulate backslashes for escaping regexp
 242	// Not in use anymore since 2.0. Shall we remove it?
 243	/// @deprecated
 244	$GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
 245
 246	// set to TRUE to enable correct decoding of <NIL/> values
 247	$GLOBALS['xmlrpc_null_extension']=false;
 248
 249	// used to store state during parsing
 250	// quick explanation of components:
 251	//   ac - used to accumulate values
 252	//   isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
 253	//   isf_reason - used for storing xmlrpcresp fault string
 254	//   lv - used to indicate "looking for a value": implements
 255	//        the logic to allow values with no types to be strings
 256	//   params - used to store parameters in method calls
 257	//   method - used to store method name
 258	//   stack - array with genealogy of xml elements names:
 259	//           used to validate nesting of xmlrpc elements
 260	$GLOBALS['_xh']=null;
 261
 262	/**
 263	* Convert a string to the correct XML representation in a target charset
 264	* To help correct communication of non-ascii chars inside strings, regardless
 265	* of the charset used when sending requests, parsing them, sending responses
 266	* and parsing responses, an option is to convert all non-ascii chars present in the message
 267	* into their equivalent 'charset entity'. Charset entities enumerated this way
 268	* are independent of the charset encoding used to transmit them, and all XML
 269	* parsers are bound to understand them.
 270	* Note that in the std case we are not sending a charset encoding mime type
 271	* along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
 272	*
 273	* @todo do a bit of basic benchmarking (strtr vs. str_replace)
 274	* @todo	make usage of iconv() or recode_string() or mb_string() where available
 275	*/
 276	function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
 277	{
 278		if ($src_encoding == '')
 279		{
 280			// lame, but we know no better...
 281			$src_encoding = $GLOBALS['xmlrpc_internalencoding'];
 282		}
 283
 284		switch(strtoupper($src_encoding.'_'.$dest_encoding))
 285		{
 286			case 'ISO-8859-1_':
 287			case 'ISO-8859-1_US-ASCII':
 288				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 289				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
 290				break;
 291			case 'ISO-8859-1_UTF-8':
 292				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 293				$escaped_data = utf8_encode($escaped_data);
 294				break;
 295			case 'ISO-8859-1_ISO-8859-1':
 296			case 'US-ASCII_US-ASCII':
 297			case 'US-ASCII_UTF-8':
 298			case 'US-ASCII_':
 299			case 'US-ASCII_ISO-8859-1':
 300			case 'UTF-8_UTF-8':
 301			//case 'CP1252_CP1252':
 302				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 303				break;
 304			case 'UTF-8_':
 305			case 'UTF-8_US-ASCII':
 306			case 'UTF-8_ISO-8859-1':
 307	// NB: this will choke on invalid UTF-8, going most likely beyond EOF
 308	$escaped_data = '';
 309	// be kind to users creating string xmlrpcvals out of different php types
 310	$data = (string) $data;
 311	$ns = strlen ($data);
 312	for ($nn = 0; $nn < $ns; $nn++)
 313	{
 314		$ch = $data[$nn];
 315		$ii = ord($ch);
 316		//1 7 0bbbbbbb (127)
 317		if ($ii < 128)
 318		{
 319			/// @todo shall we replace this with a (supposedly) faster str_replace?
 320			switch($ii){
 321				case 34:
 322					$escaped_data .= '&quot;';
 323					break;
 324				case 38:
 325					$escaped_data .= '&amp;';
 326					break;
 327				case 39:
 328					$escaped_data .= '&apos;';
 329					break;
 330				case 60:
 331					$escaped_data .= '&lt;';
 332					break;
 333				case 62:
 334					$escaped_data .= '&gt;';
 335					break;
 336				default:
 337					$escaped_data .= $ch;
 338			} // switch
 339		}
 340		//2 11 110bbbbb 10bbbbbb (2047)
 341		else if ($ii>>5 == 6)
 342		{
 343			$b1 = ($ii & 31);
 344			$ii = ord($data[$nn+1]);
 345			$b2 = ($ii & 63);
 346			$ii = ($b1 * 64) + $b2;
 347			$ent = sprintf ('&#%d;', $ii);
 348			$escaped_data .= $ent;
 349			$nn += 1;
 350		}
 351		//3 16 1110bbbb 10bbbbbb 10bbbbbb
 352		else if ($ii>>4 == 14)
 353		{
 354			$b1 = ($ii & 15);
 355			$ii = ord($data[$nn+1]);
 356			$b2 = ($ii & 63);
 357			$ii = ord($data[$nn+2]);
 358			$b3 = ($ii & 63);
 359			$ii = ((($b1 * 64) + $b2) * 64) + $b3;
 360			$ent = sprintf ('&#%d;', $ii);
 361			$escaped_data .= $ent;
 362			$nn += 2;
 363		}
 364		//4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 365		else if ($ii>>3 == 30)
 366		{
 367			$b1 = ($ii & 7);
 368			$ii = ord($data[$nn+1]);
 369			$b2 = ($ii & 63);
 370			$ii = ord($data[$nn+2]);
 371			$b3 = ($ii & 63);
 372			$ii = ord($data[$nn+3]);
 373			$b4 = ($ii & 63);
 374			$ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
 375			$ent = sprintf ('&#%d;', $ii);
 376			$escaped_data .= $ent;
 377			$nn += 3;
 378		}
 379	}
 380				break;
 381/*
 382			case 'CP1252_':
 383			case 'CP1252_US-ASCII':
 384				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 385				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
 386				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
 387				break;
 388			case 'CP1252_UTF-8':
 389				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 390				/// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
 391				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
 392				$escaped_data = utf8_encode($escaped_data);
 393				break;
 394			case 'CP1252_ISO-8859-1':
 395				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 396				// we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
 397				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
 398				break;
 399*/
 400			default:
 401				$escaped_data = '';
 402				error_log("Converting from $src_encoding to $dest_encoding: not supported...");
 403		}
 404		return $escaped_data;
 405	}
 406
 407	/// xml parser handler function for opening element tags
 408	function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
 409	{
 410		// if invalid xmlrpc already detected, skip all processing
 411		if ($GLOBALS['_xh']['isf'] < 2)
 412		{
 413			// check for correct element nesting
 414			// top level element can only be of 2 types
 415			/// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
 416			///       there is only a single top level element in xml anyway
 417			if (count($GLOBALS['_xh']['stack']) == 0)
 418			{
 419				if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
 420					$name != 'VALUE' && !$accept_single_vals))
 421				{
 422					$GLOBALS['_xh']['isf'] = 2;
 423					$GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
 424					return;
 425				}
 426				else
 427				{
 428					$GLOBALS['_xh']['rt'] = strtolower($name);
 429				}
 430			}
 431			else
 432			{
 433				// not top level element: see if parent is OK
 434				$parent = end($GLOBALS['_xh']['stack']);
 435				if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
 436				{
 437					$GLOBALS['_xh']['isf'] = 2;
 438					$GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
 439					return;
 440				}
 441			}
 442
 443			switch($name)
 444			{
 445				// optimize for speed switch cases: most common cases first
 446				case 'VALUE':
 447					/// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
 448					$GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
 449					$GLOBALS['_xh']['ac']='';
 450					$GLOBALS['_xh']['lv']=1;
 451					$GLOBALS['_xh']['php_class']=null;
 452					break;
 453				case 'I4':
 454				case 'INT':
 455				case 'STRING':
 456				case 'BOOLEAN':
 457				case 'DOUBLE':
 458				case 'DATETIME.ISO8601':
 459				case 'BASE64':
 460					if ($GLOBALS['_xh']['vt']!='value')
 461					{
 462						//two data elements inside a value: an error occurred!
 463						$GLOBALS['_xh']['isf'] = 2;
 464						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 465						return;
 466					}
 467					$GLOBALS['_xh']['ac']=''; // reset the accumulator
 468					break;
 469				case 'STRUCT':
 470				case 'ARRAY':
 471					if ($GLOBALS['_xh']['vt']!='value')
 472					{
 473						//two data elements inside a value: an error occurred!
 474						$GLOBALS['_xh']['isf'] = 2;
 475						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 476						return;
 477					}
 478					// create an empty array to hold child values, and push it onto appropriate stack
 479					$cur_val = array();
 480					$cur_val['values'] = array();
 481					$cur_val['type'] = $name;
 482					// check for out-of-band information to rebuild php objs
 483					// and in case it is found, save it
 484					if (@isset($attrs['PHP_CLASS']))
 485					{
 486						$cur_val['php_class'] = $attrs['PHP_CLASS'];
 487					}
 488					$GLOBALS['_xh']['valuestack'][] = $cur_val;
 489					$GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
 490					break;
 491				case 'DATA':
 492					if ($GLOBALS['_xh']['vt']!='data')
 493					{
 494						//two data elements inside a value: an error occurred!
 495						$GLOBALS['_xh']['isf'] = 2;
 496						$GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
 497						return;
 498					}
 499				case 'METHODCALL':
 500				case 'METHODRESPONSE':
 501				case 'PARAMS':
 502					// valid elements that add little to processing
 503					break;
 504				case 'METHODNAME':
 505				case 'NAME':
 506					/// @todo we could check for 2 NAME elements inside a MEMBER element
 507					$GLOBALS['_xh']['ac']='';
 508					break;
 509				case 'FAULT':
 510					$GLOBALS['_xh']['isf']=1;
 511					break;
 512				case 'MEMBER':
 513					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
 514					//$GLOBALS['_xh']['ac']='';
 515					// Drop trough intentionally
 516				case 'PARAM':
 517					// clear value type, so we can check later if no value has been passed for this param/member
 518					$GLOBALS['_xh']['vt']=null;
 519					break;
 520				case 'NIL':
 521					if ($GLOBALS['xmlrpc_null_extension'])
 522					{
 523						if ($GLOBALS['_xh']['vt']!='value')
 524						{
 525							//two data elements inside a value: an error occurred!
 526							$GLOBALS['_xh']['isf'] = 2;
 527							$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 528							return;
 529						}
 530						$GLOBALS['_xh']['ac']=''; // reset the accumulator
 531						break;
 532					}
 533					// we do not support the <NIL/> extension, so
 534					// drop through intentionally
 535				default:
 536					/// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
 537					$GLOBALS['_xh']['isf'] = 2;
 538					$GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
 539					break;
 540			}
 541
 542			// Save current element name to stack, to validate nesting
 543			$GLOBALS['_xh']['stack'][] = $name;
 544
 545			/// @todo optimization creep: move this inside the big switch() above
 546			if($name!='VALUE')
 547			{
 548				$GLOBALS['_xh']['lv']=0;
 549			}
 550		}
 551	}
 552
 553	/// Used in decoding xml chunks that might represent single xmlrpc values
 554	function xmlrpc_se_any($parser, $name, $attrs)
 555	{
 556		xmlrpc_se($parser, $name, $attrs, true);
 557	}
 558
 559	/// xml parser handler function for close element tags
 560	function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
 561	{
 562		if ($GLOBALS['_xh']['isf'] < 2)
 563		{
 564			// push this element name from stack
 565			// NB: if XML validates, correct opening/closing is guaranteed and
 566			// we do not have to check for $name == $curr_elem.
 567			// we also checked for proper nesting at start of elements...
 568			$curr_elem = array_pop($GLOBALS['_xh']['stack']);
 569
 570			switch($name)
 571			{
 572				case 'VALUE':
 573					// This if() detects if no scalar was inside <VALUE></VALUE>
 574					if ($GLOBALS['_xh']['vt']=='value')
 575					{
 576						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 577						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
 578					}
 579
 580					if ($rebuild_xmlrpcvals)
 581					{
 582						// build the xmlrpc val out of the data received, and substitute it
 583						$temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
 584						// in case we got info about underlying php class, save it
 585						// in the object we're rebuilding
 586						if (isset($GLOBALS['_xh']['php_class']))
 587							$temp->_php_class = $GLOBALS['_xh']['php_class'];
 588						// check if we are inside an array or struct:
 589						// if value just built is inside an array, let's move it into array on the stack
 590						$vscount = count($GLOBALS['_xh']['valuestack']);
 591						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 592						{
 593							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
 594						}
 595						else
 596						{
 597							$GLOBALS['_xh']['value'] = $temp;
 598						}
 599					}
 600					else
 601					{
 602						/// @todo this needs to treat correctly php-serialized objects,
 603						/// since std deserializing is done by php_xmlrpc_decode,
 604						/// which we will not be calling...
 605						if (isset($GLOBALS['_xh']['php_class']))
 606						{
 607						}
 608
 609						// check if we are inside an array or struct:
 610						// if value just built is inside an array, let's move it into array on the stack
 611						$vscount = count($GLOBALS['_xh']['valuestack']);
 612						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 613						{
 614							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
 615						}
 616					}
 617					break;
 618				case 'BOOLEAN':
 619				case 'I4':
 620				case 'INT':
 621				case 'STRING':
 622				case 'DOUBLE':
 623				case 'DATETIME.ISO8601':
 624				case 'BASE64':
 625					$GLOBALS['_xh']['vt']=strtolower($name);
 626					/// @todo: optimization creep - remove the if/elseif cycle below
 627					/// since the case() in which we are already did that
 628					if ($name=='STRING')
 629					{
 630						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 631					}
 632					elseif ($name=='DATETIME.ISO8601')
 633					{
 634						if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
 635						{
 636							error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
 637						}
 638						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
 639						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 640					}
 641					elseif ($name=='BASE64')
 642					{
 643						/// @todo check for failure of base64 decoding / catch warnings
 644						$GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
 645					}
 646					elseif ($name=='BOOLEAN')
 647					{
 648						// special case here: we translate boolean 1 or 0 into PHP
 649						// constants true or false.
 650						// Strings 'true' and 'false' are accepted, even though the
 651						// spec never mentions them (see eg. Blogger api docs)
 652						// NB: this simple checks helps a lot sanitizing input, ie no
 653						// security problems around here
 654						if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
 655						{
 656							$GLOBALS['_xh']['value']=true;
 657						}
 658						else
 659						{
 660							// log if receiveing something strange, even though we set the value to false anyway
 661							if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
 662								error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
 663							$GLOBALS['_xh']['value']=false;
 664						}
 665					}
 666					elseif ($name=='DOUBLE')
 667					{
 668						// we have a DOUBLE
 669						// we must check that only 0123456789-.<space> are characters here
 670						// NOTE: regexp could be much stricter than this...
 671						if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
 672						{
 673							/// @todo: find a better way of throwing an error than this!
 674							error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
 675							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
 676						}
 677						else
 678						{
 679							// it's ok, add it on
 680							$GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
 681						}
 682					}
 683					else
 684					{
 685						// we have an I4/INT
 686						// we must check that only 0123456789-<space> are characters here
 687						if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
 688						{
 689							/// @todo find a better way of throwing an error than this!
 690							error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
 691							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
 692						}
 693						else
 694						{
 695							// it's ok, add it on
 696							$GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
 697						}
 698					}
 699					//$GLOBALS['_xh']['ac']=''; // is this necessary?
 700					$GLOBALS['_xh']['lv']=3; // indicate we've found a value
 701					break;
 702				case 'NAME':
 703					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
 704					break;
 705				case 'MEMBER':
 706					//$GLOBALS['_xh']['ac']=''; // is this necessary?
 707					// add to array in the stack the last element built,
 708					// unless no VALUE was found
 709					if ($GLOBALS['_xh']['vt'])
 710					{
 711						$vscount = count($GLOBALS['_xh']['valuestack']);
 712						$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
 713					} else
 714						error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
 715					break;
 716				case 'DATA':
 717					//$GLOBALS['_xh']['ac']=''; // is this necessary?
 718					$GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
 719					break;
 720				case 'STRUCT':
 721				case 'ARRAY':
 722					// fetch out of stack array of values, and promote it to current value
 723					$curr_val = array_pop($GLOBALS['_xh']['valuestack']);
 724					$GLOBALS['_xh']['value'] = $curr_val['values'];
 725					$GLOBALS['_xh']['vt']=strtolower($name);
 726					if (isset($curr_val['php_class']))
 727					{
 728						$GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
 729					}
 730					break;
 731				case 'PARAM':
 732					// add to array of params the current value,
 733					// unless no VALUE was found
 734					if ($GLOBALS['_xh']['vt'])
 735					{
 736						$GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
 737						$GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
 738					}
 739					else
 740						error_log('XML-RPC: missing VALUE inside PARAM in received xml');
 741					break;
 742				case 'METHODNAME':
 743					$GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
 744					break;
 745				case 'NIL':
 746					if ($GLOBALS['xmlrpc_null_extension'])
 747					{
 748						$GLOBALS['_xh']['vt']='null';
 749						$GLOBALS['_xh']['value']=null;
 750						$GLOBALS['_xh']['lv']=3;
 751						break;
 752					}
 753					// drop through intentionally if nil extension not enabled
 754				case 'PARAMS':
 755				case 'FAULT':
 756				case 'METHODCALL':
 757				case 'METHORESPONSE':
 758					break;
 759				default:
 760					// End of INVALID ELEMENT!
 761					// shall we add an assert here for unreachable code???
 762					break;
 763			}
 764		}
 765	}
 766
 767	/// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
 768	function xmlrpc_ee_fast($parser, $name)
 769	{
 770		xmlrpc_ee($parser, $name, false);
 771	}
 772
 773	/// xml parser handler function for character data
 774	function xmlrpc_cd($parser, $data)
 775	{
 776		// skip processing if xml fault already detected
 777		if ($GLOBALS['_xh']['isf'] < 2)
 778		{
 779			// "lookforvalue==3" means that we've found an entire value
 780			// and should discard any further character data
 781			if($GLOBALS['_xh']['lv']!=3)
 782			{
 783				// G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
 784				//if($GLOBALS['_xh']['lv']==1)
 785				//{
 786					// if we've found text and we're just in a <value> then
 787					// say we've found a value
 788					//$GLOBALS['_xh']['lv']=2;
 789				//}
 790				// we always initialize the accumulator before starting parsing, anyway...
 791				//if(!@isset($GLOBALS['_xh']['ac']))
 792				//{
 793				//	$GLOBALS['_xh']['ac'] = '';
 794				//}
 795				$GLOBALS['_xh']['ac'].=$data;
 796			}
 797		}
 798	}
 799
 800	/// xml parser handler function for 'other stuff', ie. not char data or
 801	/// element start/end tag. In fact it only gets called on unknown entities...
 802	function xmlrpc_dh($parser, $data)
 803	{
 804		// skip processing if xml fault already detected
 805		if ($GLOBALS['_xh']['isf'] < 2)
 806		{
 807			if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
 808			{
 809				// G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
 810				//if($GLOBALS['_xh']['lv']==1)
 811				//{
 812				//	$GLOBALS['_xh']['lv']=2;
 813				//}
 814				$GLOBALS['_xh']['ac'].=$data;
 815			}
 816		}
 817		return true;
 818	}
 819
 820	class xmlrpc_client
 821	{
 822		var $path;
 823		var $server;
 824		var $port=0;
 825		var $method='http';
 826		var $errno;
 827		var $errstr;
 828		var $debug=0;
 829		var $username='';
 830		var $password='';
 831		var $authtype=1;
 832		var $cert='';
 833		var $certpass='';
 834		var $cacert='';
 835		var $cacertdir='';
 836		var $key='';
 837		var $keypass='';
 838		var $verifypeer=true;
 839		var $verifyhost=1;
 840		var $no_multicall=false;
 841		var $proxy='';
 842		var $proxyport=0;
 843		var $proxy_user='';
 844		var $proxy_pass='';
 845		var $proxy_authtype=1;
 846		var $cookies=array();
 847		/**
 848		* List of http compression methods accepted by the client for responses.
 849		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
 850		*
 851		* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
 852		* in those cases it will be up to CURL to decide the compression methods
 853		* it supports. You might check for the presence of 'zlib' in the output of
 854		* curl_version() to determine wheter compression is supported or not
 855		*/
 856		var $accepted_compression = array();
 857		/**
 858		* Name of compression scheme to be used for sending requests.
 859		* Either null, gzip or deflate
 860		*/
 861		var $request_compression = '';
 862		/**
 863		* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
 864		* http://curl.haxx.se/docs/faq.html#7.3)
 865		*/
 866		var $xmlrpc_curl_handle = null;
 867		/// Wheter to use persistent connections for http 1.1 and https
 868		var $keepalive = false;
 869		/// Charset encodings that can be decoded without problems by the client
 870		var $accepted_charset_encodings = array();
 871		/// Charset encoding to be used in serializing request. NULL = use ASCII
 872		var $request_charset_encoding = '';
 873		/**
 874		* Decides the content of xmlrpcresp objects returned by calls to send()
 875		* valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
 876		*/
 877		var $return_type = 'xmlrpcvals';
 878
 879		/**
 880		* @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
 881		* @param string $server the server name / ip address
 882		* @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
 883		* @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
 884		*/
 885		function xmlrpc_client($path, $server='', $port='', $method='')
 886		{
 887			// allow user to specify all params in $path
 888			if($server == '' and $port == '' and $method == '')
 889			{
 890				$parts = parse_url($path);
 891				$server = $parts['host'];
 892				$path = isset($parts['path']) ? $parts['path'] : '';
 893				if(isset($parts['query']))
 894				{
 895					$path .= '?'.$parts['query'];
 896				}
 897				if(isset($parts['fragment']))
 898				{
 899					$path .= '#'.$parts['fragment'];
 900				}
 901				if(isset($parts['port']))
 902				{
 903					$port = $parts['port'];
 904				}
 905				if(isset($parts['scheme']))
 906				{
 907					$method = $parts['scheme'];
 908				}
 909				if(isset($parts['user']))
 910				{
 911					$this->username = $parts['user'];
 912				}
 913				if(isset($parts['pass']))
 914				{
 915					$this->password = $parts['pass'];
 916				}
 917			}
 918			if($path == '' || $path[0] != '/')
 919			{
 920				$this->path='/'.$path;
 921			}
 922			else
 923			{
 924				$this->path=$path;
 925			}
 926			$this->server=$server;
 927			if($port != '')
 928			{
 929				$this->port=$port;
 930			}
 931			if($method != '')
 932			{
 933				$this->method=$method;
 934			}
 935
 936			// if ZLIB is enabled, let the client by default accept compressed responses
 937			if(function_exists('gzinflate') || (
 938				function_exists('curl_init') && (($info = curl_version()) &&
 939				((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
 940			))
 941			{
 942				$this->accepted_compression = array('gzip', 'deflate');
 943			}
 944
 945			// keepalives: enabled by default ONLY for PHP >= 4.3.8
 946			// (see http://curl.haxx.se/docs/faq.html#7.3)
 947			if(version_compare(phpversion(), '4.3.8') >= 0)
 948			{
 949				$this->keepalive = true;
 950			}
 951
 952			// by default the xml parser can support these 3 charset encodings
 953			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
 954		}
 955
 956		/**
 957		* Enables/disables the echoing to screen of the xmlrpc responses received
 958		* @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
 959		* @access public
 960		*/
 961		function setDebug($in)
 962		{
 963			$this->debug=$in;
 964		}
 965
 966		/**
 967		* Add some http BASIC AUTH credentials, used by the client to authenticate
 968		* @param string $u username
 969		* @param string $p password
 970		* @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
 971		* @access public
 972		*/
 973		function setCredentials($u, $p, $t=1)
 974		{
 975			$this->username=$u;
 976			$this->password=$p;
 977			$this->authtype=$t;
 978		}
 979
 980		/**
 981		* Add a client-side https certificate
 982		* @param string $cert
 983		* @param string $certpass
 984		* @access public
 985		*/
 986		function setCertificate($cert, $certpass)
 987		{
 988			$this->cert = $cert;
 989			$this->certpass = $certpass;
 990		}
 991
 992		/**
 993		* Add a CA certificate to verify server with (see man page about
 994		* CURLOPT_CAINFO for more details
 995		* @param string $cacert certificate file name (or dir holding certificates)
 996		* @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
 997		* @access public
 998		*/
 999		function setCaCertificate($cacert, $is_dir=false)
1000		{
1001			if ($is_dir)
1002			{
1003				$this->cacertdir = $cacert;
1004			}
1005			else
1006			{
1007				$this->cacert = $cacert;
1008			}
1009		}
1010
1011		/**
1012		* Set attributes for SSL communication: private SSL key
1013		* NB: does not work in older php/curl installs
1014		* Thanks to Daniel Convissor
1015		* @param string $key The name of a file containing a private SSL key
1016		* @param string $keypass The secret password needed to use the private SSL key
1017		* @access public
1018		*/
1019		function setKey($key, $keypass)
1020		{
1021			$this->key = $key;
1022			$this->keypass = $keypass;
1023		}
1024
1025		/**
1026		* Set attributes for SSL communication: verify server certificate
1027		* @param bool $i enable/disable verification of peer certificate
1028		* @access public
1029		*/
1030		function setSSLVerifyPeer($i)
1031		{
1032			$this->verifypeer = $i;
1033		}
1034
1035		/**
1036		* Set attributes for SSL communication: verify match of server cert w. hostname
1037		* @param int $i
1038		* @access public
1039		*/
1040		function setSSLVerifyHost($i)
1041		{
1042			$this->verifyhost = $i;
1043		}
1044
1045		/**
1046		* Set proxy info
1047		* @param string $proxyhost
1048		* @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1049		* @param string $proxyusername Leave blank if proxy has public access
1050		* @param string $proxypassword Leave blank if proxy has public access
1051		* @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1052		* @access public
1053		*/
1054		function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1055		{
1056			$this->proxy = $proxyhost;
1057			$this->proxyport = $proxyport;
1058			$this->proxy_user = $proxyusername;
1059			$this->proxy_pass = $proxypassword;
1060			$this->proxy_authtype = $proxyauthtype;
1061		}
1062
1063		/**
1064		* Enables/disables reception of compressed xmlrpc responses.
1065		* Note that enabling reception of compressed responses merely adds some standard
1066		* http headers to xmlrpc requests. It is up to the xmlrpc server to return
1067		* compressed responses when receiving such requests.
1068		* @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1069		* @access public
1070		*/
1071		function setAcceptedCompression($compmethod)
1072		{
1073			if ($compmethod == 'any')
1074				$this->accepted_compression = array('gzip', 'deflate');
1075			else
1076				$this->accepted_compression = array($compmethod);
1077		}
1078
1079		/**
1080		* Enables/disables http compression of xmlrpc request.
1081		* Take care when sending compressed requests: servers might not support them
1082		* (and automatic fallback to uncompressed requests is not yet implemented)
1083		* @param string $compmethod either 'gzip', 'deflate' or ''
1084		* @access public
1085		*/
1086		function setRequestCompression($compmethod)
1087		{
1088			$this->request_compression = $compmethod;
1089		}
1090
1091		/**
1092		* Adds a cookie to list of cookies that will be sent to server.
1093		* NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1094		* do not do it unless you know what you are doing
1095		* @param string $name
1096		* @param string $value
1097		* @param string $path
1098		* @param string $domain
1099		* @param int $port
1100		* @access public
1101		*
1102		* @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1103		*/
1104		function setCookie($name, $value='', $path='', $domain='', $port=null)
1105		{
1106			$this->cookies[$name]['value'] = urlencode($value);
1107			if ($path || $domain || $port)
1108			{
1109				$this->cookies[$name]['path'] = $path;
1110				$this->cookies[$name]['domain'] = $domain;
1111				$this->cookies[$name]['port'] = $port;
1112				$this->cookies[$name]['version'] = 1;
1113			}
1114			else
1115			{
1116				$this->cookies[$name]['version'] = 0;
1117			}
1118		}
1119
1120		/**
1121		* Send an xmlrpc request
1122		* @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1123		* @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1124		* @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1125		* @return xmlrpcresp
1126		* @access public
1127		*/
1128		function& send($msg, $timeout=0, $method='')
1129		{
1130			// if user deos not specify http protocol, use native method of this client
1131			// (i.e. method set during call to constructor)
1132			if($method == '')
1133			{
1134				$method = $this->method;
1135			}
1136
1137			if(is_array($msg))
1138			{
1139				// $msg is an array of xmlrpcmsg's
1140				$r = $this->multicall($msg, $timeout, $method);
1141				return $r;
1142			}
1143			elseif(is_string($msg))
1144			{
1145				$n =& new xmlrpcmsg('');
1146				$n->payload = $msg;
1147				$msg = $n;
1148			}
1149
1150			// where msg is an xmlrpcmsg
1151			$msg->debug=$this->debug;
1152
1153			if($method == 'https')
1154			{
1155				$r =& $this->sendPayloadHTTPS(
1156					$msg,
1157					$this->server,
1158					$this->port,
1159					$timeout,
1160					$this->username,
1161					$this->password,
1162					$this->authtype,
1163					$this->cert,
1164					$this->certpass,
1165					$this->cacert,
1166					$this->cacertdir,
1167					$this->proxy,
1168					$this->proxyport,
1169					$this->proxy_user,
1170					$this->proxy_pass,
1171					$this->proxy_authtype,
1172					$this->keepalive,
1173					$this->key,
1174					$this->keypass
1175				);
1176			}
1177			elseif($method == 'http11')
1178			{
1179				$r =& $this->sendPayloadCURL(
1180					$msg,
1181					$this->server,
1182					$this->port,
1183					$timeout,
1184					$this->username,
1185					$this->password,
1186					$this->authtype,
1187					null,
1188					null,
1189					null,
1190					null,
1191					$this->proxy,
1192					$this->proxyport,
1193					$this->proxy_user,
1194					$this->proxy_pass,
1195					$this->proxy_authtype,
1196					'http',
1197					$this->keepalive
1198				);
1199			}
1200			else
1201			{
1202				$r =& $this->sendPayloadHTTP10(
1203					$msg,
1204					$this->server,
1205					$this->port,
1206					$timeout,
1207					$this->username,
1208					$this->password,
1209					$this->authtype,
1210					$this->proxy,
1211					$this->proxyport,
1212					$this->proxy_user,
1213					$this->proxy_pass,
1214					$this->proxy_authtype
1215				);
1216			}
1217
1218			return $r;
1219		}
1220
1221		/**
1222		* @access private
1223		*/
1224		function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1225			$username='', $password='', $authtype=1, $proxyhost='',
1226			$proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1227		{
1228			if($port==0)
1229			{
1230				$port=80;
1231			}
1232
1233			// Only create the payload if it was not created previously
1234			if(empty($msg->payload))
1235			{
1236				$msg->createPayload($this->request_charset_encoding);
1237			}
1238
1239			$payload = $msg->payload;
1240			// Deflate request body and set appropriate request headers
1241			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1242			{
1243				if($this->request_compression == 'gzip')
1244				{
1245					$a = @gzencode($payload);
1246					if($a)
1247					{
1248						$payload = $a;
1249						$encoding_hdr = "Content-Encoding: gzip\r\n";
1250					}
1251				}
1252				else
1253				{
1254					$a = @gzcompress($payload);
1255					if($a)
1256					{
1257						$payload = $a;
1258						$encoding_hdr = "Content-Encoding: deflate\r\n";
1259					}
1260				}
1261			}
1262			else
1263			{
1264				$encoding_hdr = '';
1265			}
1266
1267			// thanks to Grant Rauscher <grant7@firstworld.net> for this
1268			$credentials='';
1269			if($username!='')
1270			{
1271				$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1272				if ($authtype != 1)
1273				{
1274					error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0');
1275				}
1276			}
1277
1278			$accepted_encoding = '';
1279			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1280			{
1281				$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1282			}
1283
1284			$proxy_credentials = '';
1285			if($proxyhost)
1286			{
1287				if($proxyport == 0)
1288				{
1289					$proxyport = 8080;
1290				}
1291				$connectserver = $proxyhost;
1292				$connectport = $proxyport;
1293				$uri = 'http://'.$server.':'.$port.$this->path;
1294				if($proxyusername != '')
1295				{
1296					if ($proxyauthtype != 1)
1297					{
1298						error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0');
1299					}
1300					$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1301				}
1302			}
1303			else
1304			{
1305				$connectserver = $server;
1306				$connectport = $port;
1307				$uri = $this->path;
1308			}
1309
1310			// Cookie generation, as per rfc2965 (version 1 cookies) or
1311			// netscape's rules (version 0 cookies)
1312			$cookieheader='';
1313			if (count($this->cookies))
1314			{
1315				$version = '';
1316				foreach ($this->cookies as $name => $cookie)
1317				{
1318					if ($cookie['version'])
1319					{
1320						$version = ' $Version="' . $cookie['version'] . '";';
1321						$cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1322						if ($cookie['path'])
1323							$cookieheader .= ' $Path="' . $cookie['path'] . '";';
1324						if ($cookie['domain'])
1325							$cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1326						if ($cookie['port'])
1327							$cookieheader .= ' $Port="' . $cookie['port'] . '";';
1328					}
1329					else
1330					{
1331						$cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1332					}
1333				}
1334				$cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1335			}
1336
1337			$op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1338				'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" .
1339				'Host: '. $server . ':' . $port . "\r\n" .
1340				$credentials .
1341				$proxy_credentials .
1342				$accepted_encoding .
1343				$encoding_hdr .
1344				'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1345				$cookieheader .
1346				'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1347				strlen($payload) . "\r\n\r\n" .
1348				$payload;
1349
1350			if($this->debug > 1)
1351			{
1352				print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1353				// let the client see this now in case http times out...
1354				flush();
1355			}
1356
1357			if($timeout>0)
1358			{
1359				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1360			}
1361			else
1362			{
1363				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1364			}
1365			if($fp)
1366			{
1367				if($timeout>0 && function_exists('stream_set_timeout'))
1368				{
1369					stream_set_timeout($fp, $timeout);
1370				}
1371			}
1372			else
1373			{
1374				$this->errstr='Connect error: '.$this->errstr;
1375				$r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1376				return $r;
1377			}
1378
1379			if(!fputs($fp, $op, strlen($op)))
1380			{
1381    			fclose($fp);
1382				$this->errstr='Write error';
1383				$r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1384				return $r;
1385			}
1386			else
1387			{
1388				// reset errno and errstr on succesful socket connection
1389				$this->errstr = '';
1390			}
1391			// G. Giunta 2005/10/24: close socket before parsing.
1392			// should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1393			$ipd='';
1394			do
1395			{
1396				// shall we check for $data === FALSE?
1397				// as per the manual, it signals an error
1398				$ipd.=fread($fp, 32768);
1399			} while(!feof($fp));
1400			fclose($fp);
1401			$r =& $msg->parseResponse($ipd, false, $this->return_type);
1402			return $r;
1403
1404		}
1405
1406		/**
1407		* @access private
1408		*/
1409		function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1410			$password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1411			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1412			$keepalive=false, $key='', $keypass='')
1413		{
1414			$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1415				$password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1416				$proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1417			return $r;
1418		}
1419
1420		/**
1421		* Contributed by Justin Miller <justin@voxel.net>
1422		* Requires curl to be built into PHP
1423		* NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1424		* @access private
1425		*/
1426		function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1427			$password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1428			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1429			$keepalive=false, $key='', $keypass='')
1430		{
1431			if(!function_exists('curl_init'))
1432			{
1433				$this->errstr='CURL unavailable on this install';
1434				$r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1435				return $r;
1436			}
1437			if($method == 'https')
1438			{
1439				if(($info = curl_version()) &&
1440					((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1441				{
1442					$this->errstr='SSL unavailable on this install';
1443					$r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1444					return $r;
1445				}
1446			}
1447
1448			if($port == 0)
1449			{
1450				if($method == 'http')
1451				{
1452					$port = 80;
1453				}
1454				else
1455				{
1456					$port = 443;
1457				}
1458			}
1459
1460			// Only create the payload if it was not created previously
1461			if(empty($msg->payload))
1462			{
1463				$msg->createPayload($this->request_charset_encoding);
1464			}
1465
1466			// Deflate request body and set appropriate request headers
1467			$payload = $msg->payload;
1468			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1469			{
1470				if($this->request_compression == 'gzip')
1471				{
1472					$a = @gzencode($payload);
1473					if($a)
1474					{
1475						$payload = $a;
1476						$encoding_hdr = 'Content-Encoding: gzip';
1477					}
1478				}
1479				else
1480				{
1481					$a = @gzcompress($payload);
1482					if($a)
1483					{
1484						$payload = $a;
1485						$encoding_hdr = 'Content-Encoding: deflate';
1486					}
1487				}
1488			}
1489			else
1490			{
1491				$encoding_hdr = '';
1492			}
1493
1494			if($this->debug > 1)
1495			{
1496				print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1497				// let the client see this now in case http times out...
1498				flush();
1499			}
1500
1501			if(!$keepalive || !$this->xmlrpc_curl_handle)
1502			{
1503				$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1504				if($keepalive)
1505				{
1506					$this->xmlrpc_curl_handle = $curl;
1507				}
1508			}
1509			else
1510			{
1511				$curl = $this->xmlrpc_curl_handle;
1512			}
1513
1514			// results into variable
1515			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1516
1517			if($this->debug)
1518			{
1519				curl_setopt($curl, CURLOPT_VERBOSE, 1);
1520			}
1521			curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
1522			// required for XMLRPC: post the data
1523			curl_setopt($curl, CURLOPT_POST, 1);
1524			// the data
1525			curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1526
1527			// return the header too
1528			curl_setopt($curl, CURLOPT_HEADER, 1);
1529
1530			// will only work with PHP >= 5.0
1531			// NB: if we set an empty string, CURL will add http header indicating
1532			// ALL methods it is supporting. This is possibly a better option than
1533			// letting the user tell what curl can / cannot do...
1534			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1535			{
1536				//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1537				// empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1538				if (count($this->accepted_compression) == 1)
1539				{
1540					curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1541				}
1542				else
1543					curl_setopt($curl, CURLOPT_ENCODING, '');
1544			}
1545			// extra headers
1546			$headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1547			// if no keepalive is wanted, let the server know it in advance
1548			if(!$keepalive)
1549			{
1550				$headers[] = 'Connection: close';
1551			}
1552			// request compression header
1553			if($encoding_hdr)
1554			{
1555				$headers[] = $encoding_hdr;
1556			}
1557
1558			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1559			// timeout is borked
1560			if($timeout)
1561			{
1562				curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1563			}
1564
1565			if($username && $password)
1566			{
1567				curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1568				if (defined('CURLOPT_HTTPAUTH'))
1569				{
1570					curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1571				}
1572				else if ($authtype != 1)
1573				{
1574					error_log('XML-RPC: xmlrpc_client::send: warn

Large files files are truncated, but you can click here to view the full file