PageRenderTime 385ms CodeModel.GetById 232ms app.highlight 55ms RepoModel.GetById 86ms app.codeStats 0ms

/system/core/core.xmlrpc.php

https://github.com/danboy/Croissierd
PHP | 1322 lines | 995 code | 193 blank | 134 comment | 122 complexity | 3ebee3edfd4aa9ba9e28e9fa049ff8aa MD5 | raw file
   1<?php
   2
   3/*
   4=====================================================
   5 ExpressionEngine - by EllisLab
   6-----------------------------------------------------
   7 http://expressionengine.com/
   8-----------------------------------------------------
   9 Copyright (c) 2003 - 2010 EllisLab, Inc.
  10=====================================================
  11 THIS IS COPYRIGHTED SOFTWARE
  12 PLEASE READ THE LICENSE AGREEMENT
  13 http://expressionengine.com/docs/license.html
  14=====================================================
  15 File: core.xmlrpc.php
  16-----------------------------------------------------
  17 Purpose: XML-RPC class
  18=====================================================
  19*/
  20
  21if ( ! defined('EXT'))
  22{
  23    exit('Invalid file request');
  24}
  25
  26if ( ! function_exists('xml_parser_create'))
  27{	
  28    exit('Your PHP installation does not support XML');
  29}
  30
  31
  32class XML_RPC {
  33
  34	// Some of this could be elsewhere, but I left it up here for easy access
  35	
  36	var $xmlrpcI4		= 'i4'; 
  37	var $xmlrpcInt		= 'int';
  38	var $xmlrpcBoolean	= 'boolean';
  39	var $xmlrpcDouble	= 'double';	
  40	var $xmlrpcString	= 'string';
  41	var $xmlrpcDateTime	= 'dateTime.iso8601';
  42	var $xmlrpcBase64	= 'base64';
  43	var $xmlrpcArray	= 'array';
  44	var $xmlrpcStruct	= 'struct';
  45	
  46	var $xmlrpcTypes	= array();
  47	var $valid_parents	= array();
  48	var $xmlrpcerr		= array();	// Response numbers
  49	var $xmlrpcstr		= array();  // Response strings
  50	var $debug			= FALSE; 	// Debugging on or off
  51	
  52	var $xmlrpc_defencoding = 'UTF-8';
  53	var $xmlrpcName			= 'XML-RPC for ';
  54	var $xmlrpcVersion		= '1.1';
  55	var $xmlrpcerruser		= 800; // Start of user errors
  56	var $xmlrpcerrxml		= 100; // Start of XML Parse errors
  57	var $xmlrpc_backslash	= ''; // formulate backslashes for escaping regexp
  58
  59
  60	/** -------------------------------------
  61    /**  VALUES THAT MULTIPLE CLASSES NEED
  62    /** -------------------------------------*/
  63
  64	function XML_RPC () {
  65		
  66		global $PREFS;
  67		
  68		$this->xmlrpcName 		= $this->xmlrpcName.APP_NAME;
  69		$this->xmlrpc_backslash = chr(92).chr(92);
  70		
  71		// if ($PREFS->ini('debug') == 1) $this->debug = true;
  72	
  73		// Types for info sent back and forth
  74		$this->xmlrpcTypes = array(
  75			$this->xmlrpcI4       => '1',
  76			$this->xmlrpcInt      => '1',
  77			$this->xmlrpcBoolean  => '1',
  78			$this->xmlrpcString   => '1',
  79			$this->xmlrpcDouble   => '1',
  80			$this->xmlrpcDateTime => '1',
  81			$this->xmlrpcBase64   => '1',
  82			$this->xmlrpcArray    => '2',
  83			$this->xmlrpcStruct   => '3'
  84			);
  85			
  86		// Array of Valid Parents for Various XML-RPC elements
  87		$this->valid_parents = array('BOOLEAN'			=> array('VALUE'),
  88									 'I4'				=> array('VALUE'),
  89									 'INT'				=> array('VALUE'),
  90									 'STRING'			=> array('VALUE'),
  91									 'DOUBLE'			=> array('VALUE'),
  92									 'DATETIME.ISO8601'	=> array('VALUE'),
  93									 'BASE64'			=> array('VALUE'),
  94									 'ARRAY'			=> array('VALUE'),
  95									 'STRUCT'			=> array('VALUE'),
  96									 'PARAM'			=> array('PARAMS'),
  97									 'METHODNAME'		=> array('METHODCALL'),
  98									 'PARAMS'			=> array('METHODCALL', 'METHODRESPONSE'),
  99									 'MEMBER'			=> array('STRUCT'),
 100									 'NAME'				=> array('MEMBER'),
 101									 'DATA'				=> array('ARRAY'),
 102									 'FAULT'			=> array('METHODRESPONSE'),
 103									 'VALUE'			=> array('MEMBER', 'DATA', 'PARAM', 'FAULT')
 104									 );
 105			
 106			
 107		// XML-RPC Responses
 108		$this->xmlrpcerr['unknown_method']= '1';
 109		$this->xmlrpcstr['unknown_method']='Unknown method';
 110		$this->xmlrpcerr['invalid_return']= '2';
 111		$this->xmlrpcstr['invalid_return']='Invalid return payload: enabling debugging to examine incoming payload';
 112		$this->xmlrpcerr['incorrect_params']= '3';
 113		$this->xmlrpcstr['incorrect_params']='Incorrect parameters passed to method';
 114		$this->xmlrpcerr['introspect_unknown']= '4';
 115		$this->xmlrpcstr['introspect_unknown']="Can't introspect: method unknown";
 116		$this->xmlrpcerr['http_error']= '5';
 117		$this->xmlrpcstr['http_error']="Didn't receive 200 OK from remote server.";
 118		$this->xmlrpcerr['no_data']= '6';
 119		$this->xmlrpcstr['no_data']='No data received from server.';
 120		$this->xmlrpcerr['no_ssl']= '7';
 121		$this->xmlrpcstr['no_ssl']='No SSL support compiled in.';
 122		$this->xmlrpcerr['curl_fail']= '8';
 123		$this->xmlrpcstr['curl_fail']='CURL error';
 124		$this->xmlrpcerr['multicall_notstruct'] = '9';
 125		$this->xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct';
 126		$this->xmlrpcerr['multicall_nomethod']  = '10';
 127		$this->xmlrpcstr['multicall_nomethod']  = 'missing methodName';
 128		$this->xmlrpcerr['multicall_notstring'] = '11';
 129		$this->xmlrpcstr['multicall_notstring'] = 'methodName is not a string';
 130		$this->xmlrpcerr['multicall_recursion'] = '12';
 131		$this->xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden';
 132		$this->xmlrpcerr['multicall_noparams']  = '13';
 133		$this->xmlrpcstr['multicall_noparams']  = 'missing params';
 134		$this->xmlrpcerr['multicall_notarray']  = '14';
 135		$this->xmlrpcstr['multicall_notarray']  = 'params is not an array';
 136		$this->xmlrpcerr['multicall_notarray']  = '15';
 137		$this->xmlrpcstr['multicall_notarray']  = 'Invalid Request Payload';
 138		
 139	} /* END */
 140	
 141	
 142	
 143    /** -------------------------------------
 144    /**  Weblogs.com Type Ping
 145    /** -------------------------------------*/
 146    
 147    // Might move this elsewhere, but it works fine here too...
 148    
 149    function weblogs_com_ping($server,$port=80,$name, $blog_url, $rss_url='')
 150    {
 151		global $PREFS;
 152		
 153		if (stristr($server, 'ping.pmachine.com') !== FALSE)
 154		{
 155			$server = str_replace('ping.pmachine.com', 'ping.expressionengine.com', $server);
 156		}
 157				
 158		// $server = "rpc.weblogs.com/RPC2/";
 159		if (substr($server, 0, 4) != "http") $server = "http://".$server; 
 160		
 161		$parts = parse_url($server);
 162		
 163		// Weblogs.com Fixeroo
 164		if (isset($parts['path']) && $parts['path'] == "/RPC2/")
 165		{
 166			$path = str_replace('/RPC2/', '/RPC2', $parts['path']);
 167		}
 168		else
 169		{
 170			$path = (!isset($parts['path'])) ? '/' : $parts['path'];
 171		}
 172		
 173		if (isset($parts['query']) && $parts['query'] != '')
 174		{
 175			$path .= '?'.$parts['query'];
 176		}	
 177		 
 178		$client = new XML_RPC_Client($path, $parts['host'], $port);
 179		$client->timeout = 5;
 180		
 181		if (stristr($parts['host'], 'ping.expressionengine.com') === FALSE)
 182		{
 183			if ($rss_url != '')
 184			{
 185				$message = new XML_RPC_Message('weblogUpdates.extendedPing',array(
 186												new XML_RPC_Values($name),
 187												new XML_RPC_Values($blog_url),
 188												new XML_RPC_Values($PREFS->ini('site_index')),
 189												new XML_RPC_Values($rss_url)));
 190						
 191				if ( ! $result = $client->send($message) OR ! $result->value())
 192				{
 193					$message = new XML_RPC_Message('weblogUpdates.ping',
 194													array(
 195													new XML_RPC_Values($name),
 196													new XML_RPC_Values($blog_url)));
 197				}
 198				else
 199				{
 200					if ( ! $result->value())
 201					{
 202						return $result->errstr;
 203					}
 204					else
 205					{
 206						return TRUE;
 207					}
 208				}
 209			}
 210			else
 211			{
 212				$message = new XML_RPC_Message('weblogUpdates.ping',
 213												array(
 214												new XML_RPC_Values($name),
 215												new XML_RPC_Values($blog_url)));
 216			}
 217		}
 218		else
 219		{
 220			if ( ! $license = $PREFS->ini('license_number'))
 221			{
 222				return 'Invalid License';
 223			}
 224			
 225			$message = new XML_RPC_Message('ExpressionEngine.ping',
 226											array(
 227											new XML_RPC_Values($name),
 228											new XML_RPC_Values($blog_url),
 229											new XML_RPC_Values($license)));
 230		}
 231    
 232   	 	if ( ! $result = $client->send($message)) return $result->errstr;
 233    	
 234    	if ( ! $result->value())
 235		{
 236			return $result->errstr;
 237		}
 238		else
 239		{
 240			return TRUE;
 241		}
 242    }
 243	
 244	
 245} // END XML_RPC Class
 246
 247	
 248	
 249////////////
 250///////////
 251///////////
 252
 253
 254class XML_RPC_Client extends XML_RPC
 255{
 256	var $path			= '';
 257	var $server			= '';
 258	var $port			= 80;
 259	var $errno			= '';
 260	var $errstring		= '';
 261	var $timeout		= 5;
 262	var $no_multicall	= false;
 263
 264	function XML_RPC_Client($path, $server, $port=80)
 265	{
 266		global $PREFS;
 267		
 268		parent::XML_RPC();
 269		
 270		$this->port = $port; 
 271		$this->server = $server; 
 272		$this->path = $path;
 273	}
 274	
 275	function send($msg)
 276	{
 277		if (is_array($msg))
 278		{
 279			// Multi-call disabled
 280			$r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
 281			return $r;
 282		}
 283
 284		return $this->sendPayload($msg);
 285	}
 286
 287	function sendPayload($msg)
 288	{	
 289		$fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
 290		
 291		if (! is_resource($fp))
 292		{
 293			error_log($this->xmlrpcstr['http_error']);
 294			$r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
 295			return $r;
 296		}
 297		
 298		if(empty($msg->payload))
 299		{
 300			// $msg = XML_RPC_Messages
 301			$msg->createPayload();
 302		}
 303		
 304		$r = "\r\n";
 305		$op  = "POST {$this->path} HTTP/1.0$r";
 306		$op .= "Host: {$this->server}$r";
 307		$op .= "Content-Type: text/xml$r";
 308		$op .= "User-Agent: {$this->xmlrpcName}$r";
 309		$op .= "Content-Length: ".strlen($msg->payload). "$r$r";
 310		$op .= $msg->payload;
 311		
 312
 313		if (!fputs($fp, $op, strlen($op)))
 314		{
 315			error_log($this->xmlrpcstr['http_error']);
 316			$r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
 317			return $r;
 318		}
 319		$resp = $msg->parseResponse($fp);
 320		fclose($fp);
 321		return $resp;
 322	}
 323
 324} // end class XML_RPC_Client
 325
 326
 327///////////
 328///////////
 329///////////
 330
 331
 332
 333class XML_RPC_Response
 334{
 335	var $val = 0;
 336	var $errno = 0;
 337	var $errstr = '';
 338	var $headers = array();
 339
 340	function XML_RPC_Response($val, $code = 0, $fstr = '')
 341	{
 342		if ($code != 0)
 343		{
 344			// error
 345			$this->errno = $code;
 346			$this->errstr = htmlentities($fstr); 
 347		}
 348		else if (!is_object($val))
 349		{
 350			// programmer error, not an object
 351			error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response.  Defaulting to empty value.");
 352			$this->val = new XML_RPC_Values();
 353		}
 354		else
 355		{
 356			$this->val = $val;
 357		}
 358	}
 359
 360	function faultCode()
 361	{
 362		return $this->errno;
 363	}
 364
 365	function faultString()
 366	{
 367		return $this->errstr;
 368	}
 369
 370	function value()
 371	{
 372		return $this->val;
 373	}
 374	
 375	function prepare_response()
 376	{
 377		$result = "<methodResponse>\n";
 378		if ($this->errno)
 379		{
 380			$result .= '<fault>
 381	<value>
 382		<struct>
 383			<member>
 384				<name>faultCode</name>
 385				<value><int>' . $this->errno . '</int></value>
 386			</member>
 387			<member>
 388				<name>faultString</name>
 389				<value><string>' . $this->errstr . '</string></value>
 390			</member>
 391		</struct>
 392	</value>
 393</fault>';
 394		}
 395		else
 396		{
 397			$result .= "<params>\n<param>\n" .
 398					$this->val->serialize_class() . 
 399					"</param>\n</params>";
 400		}
 401		$result .= "\n</methodResponse>";
 402		return $result;
 403	}
 404	
 405	function decode($array=FALSE)
 406	{
 407		global $REGX;
 408		
 409		if ($array !== FALSE && is_array($array))
 410		{
 411			while (list($key) = each($array))
 412			{
 413				if (is_array($array[$key]))
 414				{
 415					$array[$key] = $this->decode($array[$key]);
 416				}
 417				else
 418				{
 419					$array[$key] = $REGX->xss_clean($array[$key]);
 420				}
 421			}
 422			
 423			$result = $array;
 424		}
 425		else
 426		{
 427			$result = $this->xmlrpc_decoder($this->val);
 428			
 429			if (is_array($result))
 430			{
 431				$result = $this->decode($result);
 432			}
 433			else
 434			{
 435				$result = $REGX->xss_clean($result);
 436			}
 437		}
 438		
 439		return $result;
 440	}
 441
 442	
 443	
 444	/** -------------------------------------
 445	/**  XML-RPC Object to PHP Types
 446	/** -------------------------------------*/
 447
 448	function xmlrpc_decoder($xmlrpc_val)
 449	{
 450		$kind = $xmlrpc_val->kindOf();
 451
 452		if($kind == 'scalar')
 453		{
 454			return $xmlrpc_val->scalarval();
 455		}
 456		elseif($kind == 'array')
 457		{
 458			reset($xmlrpc_val->me);
 459			list($a,$b) = each($xmlrpc_val->me);
 460			$size = sizeof($b);
 461			
 462			$arr = array();
 463
 464			for($i = 0; $i < $size; $i++)
 465			{
 466				$arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
 467			}
 468			return $arr; 
 469		}
 470		elseif($kind == 'struct')
 471		{
 472			reset($xmlrpc_val->me['struct']);
 473			$arr = array();
 474
 475			while(list($key,$value) = each($xmlrpc_val->me['struct']))
 476			{
 477				$arr[$key] = $this->xmlrpc_decoder($value);
 478			}
 479			return $arr;
 480		}
 481	}
 482	
 483	
 484	/** -------------------------------------
 485	/**  ISO-8601 time to server or UTC time
 486	/** -------------------------------------*/
 487
 488	function iso8601_decode($time, $utc=0)
 489	{
 490		// return a timet in the localtime, or UTC
 491		$t = 0;
 492		if (preg_match("#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})#", $time, $regs))
 493		{
 494			if ($utc == 1)
 495				$t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 496			else
 497				$t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 498		} 
 499		return $t;
 500	}
 501	
 502} // End Response Class
 503
 504
 505
 506
 507////////////
 508////////////
 509////////////
 510
 511
 512class XML_RPC_Message extends XML_RPC
 513{
 514	var $payload;
 515	var $method_name;
 516	var $params			= array();
 517	var $xh 			= array();
 518
 519	function XML_RPC_Message($method, $pars=0)
 520	{
 521		parent::XML_RPC();
 522		
 523		$this->method_name = $method;
 524		if (is_array($pars) && sizeof($pars) > 0)
 525		{
 526			for($i=0; $i<sizeof($pars); $i++)
 527			{
 528				// $pars[$i] = XML_RPC_Values
 529				$this->params[] = $pars[$i];
 530			}
 531		}
 532	}
 533	
 534	/** -------------------------------------
 535	/**  Create Payload to Send
 536	/** -------------------------------------*/
 537	
 538	function createPayload()
 539	{
 540		$this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
 541		$this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
 542		$this->payload .= "<params>\r\n";
 543		
 544		for($i=0; $i<sizeof($this->params); $i++)
 545		{
 546			// $p = XML_RPC_Values
 547			$p = $this->params[$i];
 548			$this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
 549		}
 550		
 551		$this->payload .= "</params>\r\n</methodCall>\r\n";
 552	}
 553	
 554	/** -------------------------------------
 555	/**  Parse External XML-RPC Server's Response
 556	/** -------------------------------------*/
 557	
 558	function parseResponse($fp)
 559	{
 560		$data = '';
 561		
 562		while($datum = fread($fp, 4096))
 563		{
 564			$data .= $datum;
 565		}
 566		
 567		/** -------------------------------------
 568		/**  DISPLAY HTTP CONTENT for DEBUGGING
 569		/** -------------------------------------*/
 570		
 571		if ($this->debug)
 572		{
 573			echo "<pre>";
 574			echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
 575			echo "</pre>";
 576		}
 577		
 578		/** -------------------------------------
 579		/**  Check for data
 580		/** -------------------------------------*/
 581
 582		if($data == "")
 583		{
 584			error_log($this->xmlrpcstr['no_data']);
 585			$r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
 586			return $r;
 587		}
 588		
 589		
 590		/** -------------------------------------
 591		/**  Check for HTTP 200 Response
 592		/** -------------------------------------*/
 593		
 594		if(preg_match("#^HTTP#",$data) && ! preg_match("#^HTTP/[0-9\.]+ 200 #", $data))
 595		{
 596			$errstr= substr($data, 0, strpos($data, "\n")-1);
 597			$r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
 598			return $r;
 599		}
 600		
 601		/** -------------------------------------
 602		/**  Create and Set Up XML Parser
 603		/** -------------------------------------*/
 604	
 605		$parser = xml_parser_create($this->xmlrpc_defencoding);
 606
 607		$this->xh[$parser]				 = array();
 608		$this->xh[$parser]['isf']		 = 0;
 609		$this->xh[$parser]['ac']		 = '';
 610		$this->xh[$parser]['headers'] 	 = array();
 611		$this->xh[$parser]['stack']		 = array();
 612		$this->xh[$parser]['valuestack'] = array();
 613		$this->xh[$parser]['isf_reason'] = 0;
 614
 615		xml_set_object($parser, $this);
 616		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
 617		xml_set_element_handler($parser, 'open_tag', 'closing_tag');
 618		xml_set_character_data_handler($parser, 'character_data');
 619		//xml_set_default_handler($parser, 'default_handler');
 620
 621
 622		/** -------------------------------------
 623		/**  GET HEADERS
 624		/** -------------------------------------*/
 625		
 626		$lines = explode("\r\n", $data);
 627		while (($line = array_shift($lines)))
 628		{
 629			if (strlen($line) < 1)
 630			{
 631				break;
 632			}
 633			$this->xh[$parser]['headers'][] = $line;
 634		}
 635		$data = implode("\r\n", $lines);
 636		
 637		
 638		/** -------------------------------------
 639		/**  PARSE XML DATA
 640		/** -------------------------------------*/
 641
 642		if (!xml_parse($parser, $data, sizeof($data)))
 643		{
 644			$errstr = sprintf('XML error: %s at line %d',
 645					xml_error_string(xml_get_error_code($parser)),
 646					xml_get_current_line_number($parser));
 647			//error_log($errstr);
 648			$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
 649			xml_parser_free($parser);
 650			return $r;
 651		}
 652		xml_parser_free($parser);
 653		
 654		/** ---------------------------------------
 655		/**  Got Ourselves Some Badness, It Seems
 656		/** ---------------------------------------*/
 657		
 658		if ($this->xh[$parser]['isf'] > 1)
 659		{
 660			if ($this->debug)
 661			{
 662				echo "---Invalid Return---\n";
 663				echo $this->xh[$parser]['isf_reason'];
 664				echo "---Invalid Return---\n\n";
 665			}
 666				
 667			$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
 668			return $r;
 669		}
 670		elseif ( ! is_object($this->xh[$parser]['value']))
 671		{
 672			$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
 673			return $r;
 674		}
 675		
 676		/** -------------------------------------
 677		/**  DISPLAY XML CONTENT for DEBUGGING
 678		/** -------------------------------------*/
 679		
 680		if ($this->debug)
 681		{
 682			echo "<pre>";
 683			
 684			if (count($this->xh[$parser]['headers'] > 0))
 685			{
 686				echo "---HEADERS---\n";
 687				foreach ($this->xh[$parser]['headers'] as $header)
 688				{
 689					echo "$header\n";
 690				}
 691				echo "---END HEADERS---\n\n";
 692			}
 693			
 694			echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
 695			
 696			echo "---PARSED---\n" ;
 697			var_dump($this->xh[$parser]['value']);
 698			echo "\n---END PARSED---</pre>";
 699		}
 700		
 701		/** -------------------------------------
 702		/**  SEND RESPONSE
 703		/** -------------------------------------*/
 704		
 705		$v = $this->xh[$parser]['value'];
 706			
 707		if ($this->xh[$parser]['isf'])
 708		{
 709			$errno_v = $v->me['struct']['faultCode'];
 710			$errstr_v = $v->me['struct']['faultString'];
 711			$errno = $errno_v->scalarval();
 712
 713			if ($errno == 0)
 714			{
 715				// FAULT returned, errno needs to reflect that
 716				$errno = -1;
 717			}
 718
 719			$r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
 720		}
 721		else
 722		{
 723			$r = new XML_RPC_Response($v);
 724		}
 725
 726		$r->headers = $this->xh[$parser]['headers'];
 727		return $r;
 728	}
 729	
 730	/** ------------------------------------
 731	/**  Begin Return Message Parsing section
 732	/** ------------------------------------*/
 733	
 734	// quick explanation of components:
 735	//   ac - used to accumulate values
 736	//   isf - used to indicate a fault
 737	//   lv - used to indicate "looking for a value": implements
 738	//        the logic to allow values with no types to be strings
 739	//   params - used to store parameters in method calls
 740	//   method - used to store method name
 741	//	 stack - array with parent tree of the xml element,
 742	//			 used to validate the nesting of elements
 743
 744	/** -------------------------------------
 745	/**  Start Element Handler
 746	/** -------------------------------------*/
 747
 748	function open_tag($the_parser, $name, $attrs)
 749	{
 750		// If invalid nesting, then return
 751		if ($this->xh[$the_parser]['isf'] > 1) return;
 752		
 753		// Evaluate and check for correct nesting of XML elements
 754		
 755		if (count($this->xh[$the_parser]['stack']) == 0)
 756		{
 757			if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
 758			{
 759				$this->xh[$the_parser]['isf'] = 2;
 760				$this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
 761				return;
 762			}
 763		}
 764		else
 765		{
 766			// not top level element: see if parent is OK
 767			if (!in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name]))
 768			{
 769				$this->xh[$the_parser]['isf'] = 2;
 770				$this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
 771				return;
 772			}
 773		}
 774		
 775		switch($name)
 776		{
 777			case 'STRUCT':
 778			case 'ARRAY':
 779				// Creates array for child elements
 780				
 781				$cur_val = array('value' => array(),
 782								 'type'	 => $name);
 783								 
 784				array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
 785			break;
 786			case 'METHODNAME':
 787			case 'NAME':
 788				$this->xh[$the_parser]['ac'] = '';
 789			break;
 790			case 'FAULT':
 791				$this->xh[$the_parser]['isf'] = 1;
 792			break;
 793			case 'PARAM':
 794				$this->xh[$the_parser]['value'] = null;
 795			break;
 796			case 'VALUE':
 797				$this->xh[$the_parser]['vt'] = 'value';
 798				$this->xh[$the_parser]['ac'] = '';
 799				$this->xh[$the_parser]['lv'] = 1;
 800			break;
 801			case 'I4':
 802			case 'INT':
 803			case 'STRING':
 804			case 'BOOLEAN':
 805			case 'DOUBLE':
 806			case 'DATETIME.ISO8601':
 807			case 'BASE64':
 808				if ($this->xh[$the_parser]['vt'] != 'value')
 809				{
 810					//two data elements inside a value: an error occurred!
 811					$this->xh[$the_parser]['isf'] = 2;
 812					$this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
 813					return;
 814				}
 815				
 816				$this->xh[$the_parser]['ac'] = '';
 817			break;
 818			case 'MEMBER':
 819				// Set name of <member> to nothing to prevent errors later if no <name> is found
 820				$this->xh[$the_parser]['valuestack'][0]['name'] = '';
 821				
 822				// Set NULL value to check to see if value passed for this param/member
 823				$this->xh[$the_parser]['value'] = null;
 824			break;
 825			case 'DATA':
 826			case 'METHODCALL':
 827			case 'METHODRESPONSE':
 828			case 'PARAMS':
 829				// valid elements that add little to processing
 830			break;
 831			default:
 832				/// An Invalid Element is Found, so we have trouble
 833				$this->xh[$the_parser]['isf'] = 2;
 834				$this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
 835			break;
 836		}
 837		
 838		// Add current element name to stack, to allow validation of nesting
 839		array_unshift($this->xh[$the_parser]['stack'], $name);
 840
 841		if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
 842	}
 843	/* END */
 844
 845
 846	/** -------------------------------------
 847	/**  End Element Handler
 848	/** -------------------------------------*/
 849
 850	function closing_tag($the_parser, $name)
 851	{
 852		if ($this->xh[$the_parser]['isf'] > 1) return;
 853		
 854		// Remove current element from stack and set variable
 855		// NOTE: If the XML validates, then we do not have to worry about
 856		// the opening and closing of elements.  Nesting is checked on the opening
 857		// tag so we be safe there as well.
 858		
 859		$curr_elem = array_shift($this->xh[$the_parser]['stack']);
 860	
 861		switch($name)
 862		{
 863			case 'STRUCT':
 864			case 'ARRAY':
 865				$cur_val = array_shift($this->xh[$the_parser]['valuestack']);
 866				$this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
 867				$this->xh[$the_parser]['vt']	= strtolower($name);
 868			break;
 869			case 'NAME':
 870				$this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
 871			break;
 872			case 'BOOLEAN':
 873			case 'I4':
 874			case 'INT':
 875			case 'STRING':
 876			case 'DOUBLE':
 877			case 'DATETIME.ISO8601':
 878			case 'BASE64':
 879				$this->xh[$the_parser]['vt'] = strtolower($name);
 880				
 881				if ($name == 'STRING')
 882				{
 883					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
 884				}
 885				elseif ($name=='DATETIME.ISO8601')
 886				{
 887					$this->xh[$the_parser]['vt']    = $this->xmlrpcDateTime;
 888					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
 889				}
 890				elseif ($name=='BASE64')
 891				{
 892					$this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
 893				}
 894				elseif ($name=='BOOLEAN')
 895				{
 896					// Translated BOOLEAN values to TRUE AND FALSE
 897					if ($this->xh[$the_parser]['ac'] == '1')
 898					{
 899						$this->xh[$the_parser]['value'] = TRUE;
 900					}
 901					else
 902					{
 903						$this->xh[$the_parser]['value'] = FALSE;
 904					}
 905				}
 906				elseif ($name=='DOUBLE')
 907				{
 908					// we have a DOUBLE
 909					// we must check that only 0123456789-.<space> are characters here
 910					if ( ! preg_match("#^[+-]?[eE0123456789 \\t\\.]+$#", $this->xh[$the_parser]['ac']))
 911					{
 912						$this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
 913					}
 914					else
 915					{
 916						$this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
 917					}
 918				}
 919				else
 920				{
 921					// we have an I4/INT
 922					// we must check that only 0123456789-<space> are characters here
 923					if ( ! preg_match("#^[+-]?[0123456789 \\t]+$#", $this->xh[$the_parser]['ac']))
 924					{
 925						$this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
 926					}
 927					else
 928					{
 929						$this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
 930					}
 931				}
 932				$this->xh[$the_parser]['ac'] = '';
 933				$this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
 934			break;
 935			case 'VALUE':
 936				// This if() detects if no scalar was inside <VALUE></VALUE>
 937				if ($this->xh[$the_parser]['vt']=='value')
 938				{
 939					$this->xh[$the_parser]['value']	= $this->xh[$the_parser]['ac'];
 940					$this->xh[$the_parser]['vt']	= $this->xmlrpcString;
 941				}
 942				
 943				// build the XML-RPC value out of the data received, and substitute it
 944				$temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
 945				
 946				if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
 947				{
 948					// Array
 949					$this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
 950				}
 951				else
 952				{
 953					// Struct
 954	    			$this->xh[$the_parser]['value'] = $temp;
 955				}
 956			break;
 957			case 'MEMBER':
 958				$this->xh[$the_parser]['ac']='';
 959				
 960				// If value add to array in the stack for the last element built
 961				if ($this->xh[$the_parser]['value'])
 962				{
 963					$this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
 964				}
 965			break;
 966			case 'DATA':
 967				$this->xh[$the_parser]['ac']='';
 968			break;
 969			case 'PARAM':
 970				if ($this->xh[$the_parser]['value'])
 971				{
 972					$this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
 973				}
 974			break;
 975			case 'METHODNAME':
 976				$this->xh[$the_parser]['method'] = preg_replace("#^[\n\r\t ]+#", '', $this->xh[$the_parser]['ac']);
 977			break;
 978			case 'PARAMS':
 979			case 'FAULT':
 980			case 'METHODCALL':
 981			case 'METHORESPONSE':
 982				// We're all good kids with nuthin' to do
 983			break;
 984			default:
 985				// End of an Invalid Element.  Taken care of during the opening tag though
 986			break;
 987		}
 988	}
 989
 990	/** -------------------------------------
 991	/**  Parses Character Data
 992	/** -------------------------------------*/
 993
 994	function character_data($the_parser, $data)
 995	{
 996		if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
 997		
 998		// If a value has not been found
 999		if ($this->xh[$the_parser]['lv'] != 3)
1000		{
1001			if ($this->xh[$the_parser]['lv'] == 1)
1002			{
1003				$this->xh[$the_parser]['lv'] = 2; // Found a value
1004			}
1005				
1006			if( ! @isset($this->xh[$the_parser]['ac']))
1007			{
1008				$this->xh[$the_parser]['ac'] = '';
1009			}
1010				
1011			$this->xh[$the_parser]['ac'] .= $data;
1012		}
1013	}
1014	
1015	
1016	function addParam($par) { $this->params[]=$par; }
1017	
1018	function output_parameters($array=FALSE)
1019	{
1020		global $REGX;
1021		
1022		if ($array !== FALSE && is_array($array))
1023		{
1024			while (list($key) = each($array))
1025			{
1026				if (is_array($array[$key]))
1027				{
1028					$array[$key] = $this->output_parameters($array[$key]);
1029				}
1030				else
1031				{
1032					/* 'bits is for the MetaWeblog API image bits */
1033					$array[$key] = ($key == 'bits') ? $array[$key] : $REGX->xss_clean($array[$key]);
1034				}
1035			}
1036			
1037			$parameters = $array;
1038		}
1039		else
1040		{
1041			$parameters = array();
1042		
1043			for ($i = 0; $i < sizeof($this->params); $i++)
1044    		{
1045    			$a_param = $this->decode_message($this->params[$i]);
1046    			
1047    			if (is_array($a_param))
1048    			{
1049    				$parameters[] = $this->output_parameters($a_param);
1050    			}
1051    			else
1052    			{
1053    				$parameters[] = $REGX->xss_clean($a_param);
1054    			}
1055    		}	
1056    	}
1057    	
1058    	return $parameters;
1059	}
1060	
1061	
1062	function decode_message($param)
1063	{
1064		$kind = $param->kindOf();
1065
1066		if($kind == 'scalar')
1067		{
1068			return $param->scalarval();
1069		}
1070		elseif($kind == 'array')
1071		{
1072			reset($param->me);
1073			list($a,$b) = each($param->me);
1074			
1075			$arr = array();
1076
1077			for($i = 0; $i < sizeof($b); $i++)
1078			{
1079				$arr[] = $this->decode_message($param->me['array'][$i]);
1080			}
1081			
1082			return $arr; 
1083		}
1084		elseif($kind == 'struct')
1085		{
1086			reset($param->me['struct']);
1087			
1088			$arr = array();
1089
1090			while(list($key,$value) = each($param->me['struct']))
1091			{
1092				$arr[$key] = $this->decode_message($value);
1093			}
1094			
1095			return $arr;
1096		}
1097	}
1098	
1099} // End XML_RPC_Messages class
1100
1101
1102
1103//////////
1104//////////
1105//////////
1106
1107
1108class XML_RPC_Values extends XML_RPC
1109{
1110	var $me 	= array();
1111	var $mytype	= 0;
1112
1113	function XML_RPC_Values($val=-1, $type='')
1114	{	
1115		parent::XML_RPC();
1116		
1117		if ($val != -1 || $type != '')
1118		{
1119			$type = $type == '' ? 'string' : $type;
1120			
1121			if ($this->xmlrpcTypes[$type] == 1)
1122			{
1123				$this->addScalar($val,$type);
1124			}
1125			elseif ($this->xmlrpcTypes[$type] == 2)
1126			{
1127				$this->addArray($val);
1128			}
1129			elseif ($this->xmlrpcTypes[$type] == 3)
1130			{
1131				$this->addStruct($val);
1132			}
1133		}
1134	}
1135
1136	function addScalar($val, $type='string')
1137	{
1138		$typeof = $this->xmlrpcTypes[$type];
1139		
1140		if ($this->mytype==1)
1141		{
1142			echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
1143			return 0;
1144		}
1145		
1146		if ($typeof != 1)
1147		{
1148			echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
1149			return 0;
1150		}
1151
1152		if ($type == $this->xmlrpcBoolean)
1153		{
1154			if (strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
1155			{
1156				$val = 1;
1157			}
1158			else
1159			{
1160				$val=0;
1161			}
1162		}
1163
1164		if ($this->mytype == 2)
1165		{
1166			// adding to an array here
1167			$ar = $this->me['array'];
1168			$ar[] = new XML_RPC_Values($val, $type);
1169			$this->me['array'] = $ar;
1170		}
1171		else
1172		{
1173			// a scalar, so set the value and remember we're scalar
1174			$this->me[$type] = $val;
1175			$this->mytype = $typeof;
1176		}
1177		return 1;
1178	}
1179
1180	function addArray($vals)
1181	{
1182		if ($this->mytype != 0)
1183		{
1184			echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1185			return 0;
1186		}
1187
1188		$this->mytype = $this->xmlrpcTypes['array'];
1189		$this->me['array'] = $vals;
1190		return 1;
1191	}
1192
1193	function addStruct($vals)
1194	{
1195		if ($this->mytype != 0)
1196		{
1197			echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1198			return 0;
1199		}
1200		$this->mytype = $this->xmlrpcTypes['struct'];
1201		$this->me['struct'] = $vals;
1202		return 1;
1203	}
1204
1205	function kindOf()
1206	{
1207		switch($this->mytype)
1208		{
1209			case 3:
1210				return 'struct';
1211				break;
1212			case 2:
1213				return 'array';
1214				break;
1215			case 1:
1216				return 'scalar';
1217				break;
1218			default:
1219				return 'undef';
1220		}
1221	}
1222
1223	function serializedata($typ, $val)
1224	{
1225		$rs = '';
1226		
1227		switch($this->xmlrpcTypes[$typ])
1228		{
1229			case 3:
1230				// struct
1231				$rs .= "<struct>\n";
1232				reset($val);
1233				while(list($key2, $val2) = each($val))
1234				{
1235					$rs .= "<member>\n<name>{$key2}</name>\n";
1236					$rs .= $this->serializeval($val2);
1237					$rs .= "</member>\n";
1238				}
1239				$rs .= '</struct>';
1240			break;
1241			case 2:
1242				// array
1243				$rs .= "<array>\n<data>\n";
1244				for($i=0; $i < sizeof($val); $i++)
1245				{
1246					$rs .= $this->serializeval($val[$i]);
1247				}
1248				$rs.="</data>\n</array>\n";
1249				break;
1250			case 1:
1251				// others
1252				switch ($typ)
1253				{
1254					case $this->xmlrpcBase64:
1255						$rs .= "<{$typ}>" . base64_encode($val) . "</{$typ}>\n";
1256					break;
1257					case $this->xmlrpcBoolean:
1258						$rs .= "<{$typ}>" . ($val ? '1' : '0') . "</{$typ}>\n";
1259					break;
1260					case $this->xmlrpcString:
1261						$rs .= "<{$typ}>" . htmlspecialchars($val). "</{$typ}>\n";
1262					break;
1263					default:
1264						$rs .= "<{$typ}>{$val}</{$typ}>\n";
1265					break;
1266				}
1267			default:
1268			break;
1269		}
1270		return $rs;
1271	}
1272
1273	function serialize_class()
1274	{
1275		return $this->serializeval($this);
1276	}
1277
1278	function serializeval($o)
1279	{
1280		
1281		$ar = $o->me;
1282		reset($ar);
1283		
1284		list($typ, $val) = each($ar);
1285		$rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
1286		return $rs;
1287	}
1288	
1289	function scalarval()
1290	{
1291		reset($this->me);
1292		list($a,$b) = each($this->me);
1293		return $b;
1294	}
1295
1296
1297	/** -------------------------------------
1298	/**  Encode time in ISO-8601 form.
1299	/** -------------------------------------*/
1300	
1301	// Useful for sending time in XML-RPC
1302
1303	function iso8601_encode($time, $utc=0)
1304	{	
1305		if ($utc == 1)
1306		{
1307			$t = strftime("%Y%m%dT%H:%M:%S", $time);
1308		}
1309		else
1310		{
1311			if (function_exists('gmstrftime'))
1312				$t = gmstrftime("%Y%m%dT%H:%M:%S", $time);
1313			else
1314				$t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z'));
1315		}
1316		return $t;
1317	}
1318	
1319}
1320// END XML_RPC_Values Class
1321
1322?>