<?php
	// by Edd Dumbill (C) 1999-2001
	// <edd@usefulinc.com>
	// xmlrpc.inc,v 1.18 2001/07/06 18:23:57 edmundd

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

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

  /* $Id$ */

	class xmlrpcval
	{
		var $me=array();
		var $mytype=0;

		function xmlrpcval($val=-1, $type='')
		{
			//$this->me=array();
			//$this->mytype=0;
			if($val!==-1 || $type!='')
			{
				if($type=='')
				{
					$type='string';
				}
				if($GLOBALS['xmlrpcTypes'][$type]==1)
				{
					$this->addScalar($val,$type);
				}
				elseif($GLOBALS['xmlrpcTypes'][$type]==2)
				{
					$this->addArray($val);
				}
				elseif($GLOBALS['xmlrpcTypes'][$type]==3)
				{
					$this->addStruct($val);
				}
			}
		}

		function addScalar($val, $type='string')
		{
			$typeof=@$GLOBALS['xmlrpcTypes'][$type];
			if($typeof!=1)
			{
				error_log("addScalar: not a scalar type ($typeof)");
				return 0;
			}

			// coerce booleans into correct values
			// NB: shall we do it for datetime too?
			if($type == xmlrpcBoolean)
			{
				if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
				{
					$val=true;
				}
				else
				{
					$val=false;
				}
			}

			switch($this->mytype)
			{
				case 1:
					error_log('addScalar: scalar xmlrpcval can have only one value');
					return 0;
				case 3:
					error_log('addScalar: cannot add anonymous scalar to struct xmlrpcval');
					return 0;
				case 2:
					// we're adding a scalar value to an array here
					//$ar=$this->me['array'];
					//$ar[]=&new xmlrpcval($val, $type);
					//$this->me['array']=$ar;
					// Faster (?) avoid all the costly array-copy-by-val done here...
					$this->me['array'][]=& CreateObject('phpgwapi.xmlrpcval',$val, $type);
					return 1;
				default:
					// a scalar, so set the value and remember we're scalar
					$this->me[$type]=$val;
					$this->mytype=$typeof;
					return 1;
			}
		}

		///@todo add some checking for $vals to be an array of xmlrpcvals?
		function addArray($vals)
		{
			if($this->mytype==0)
			{
				$this->mytype=$GLOBALS['xmlrpcTypes']['array'];
				$this->me['array']=$vals;
				return 1;
			}
			elseif($this->mytype==2)
			{
				// we're adding to an array here
				$this->me['array'] = array_merge($this->me['array'], $vals);
			}
			else
			{
				error_log('xmlrpcval: already initialized as a [' . $this->kindOf() . ']');
				return 0;
			}
		}

		///@todo add some checking for $vals to be an array?
		function addStruct($vals)
		{
			if($this->mytype==0)
			{
				$this->mytype = $GLOBALS['xmlrpcTypes']['struct'];
				$this->me['struct']=$vals;
				return 1;
			}
			elseif($this->mytype==3)
			{
				// we're adding to a struct here
				$this->me['struct'] = array_merge($this->me['struct'], $vals);
			}
			else
			{
				error_log('xmlrpcval: already initialized as a [' . $this->kindOf() . ']');
				return 0;
			}
		}

		function dump($ar)
		{
			foreach($ar as $key => $val)
			{
				echo "$key => $val<br />";
				if($key == 'array')
				{
					while(list($key2, $val2) = each($val))
					{
						echo "-- $key2 => $val2<br />";
					}
				}
			}
		}

		function kindOf()
		{
			switch($this->mytype)
			{
				case 3:
					return 'struct';
					break;
				case 2:
					return 'array';
					break;
				case 1:
					return 'scalar';
					break;
				default:
					return 'undef';
			}
		}

		function serializedata($typ, $val)
		{
			$rs='';
			switch(@$GLOBALS['xmlrpcTypes'][$typ])
			{
				case 3:
					// struct
					$rs.="<struct>\n";
					foreach($val as $key2 => $val2)
					{
						$rs.="<member><name>${key2}</name>\n";
						$rs.=$this->serializeval($val2);
						$rs.="</member>\n";
					}
					$rs.='</struct>';
					break;
				case 2:
					// array
					$rs.="<array>\n<data>\n";
					for($i=0; $i<sizeof($val); $i++)
					{
						$rs.=$this->serializeval($val[$i]);
					}
					$rs.="</data>\n</array>";
					break;
				case 1:
					switch($typ)
					{
						case xmlrpcBase64:
							$rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
							break;
						case xmlrpcBoolean:
							$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
							break;
						case xmlrpcString:
							// G. Giunta 2005/2/13: do NOT use htmlentities, since
							// it will produce named html entities, which are invalid xml
							$rs.="<${typ}>" . xmlrpc_encode_entities($val). "</${typ}>";
							// $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
							break;
						default:
							$rs.="<${typ}>${val}</${typ}>";
					}
					break;
				default:
					break;
			}
			return $rs;
		}

		function serialize()
		{
			return $this->serializeval($this);
		}

		function serializeval($o)
		{
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
			//{
			$ar=$o->me;
			reset($ar);
			list($typ, $val) = each($ar);
			return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
			//}
		}

		function structmemexists($m)
		{
			return array_key_exists($this->me['struct'][$m]);
		}

		function structmem($m)
		{
			return $this->me['struct'][$m];
		}

		function structreset()
		{
			reset($this->me['struct']);
		}

		function structeach()
		{
			return each($this->me['struct']);
		}

		function scalarval()
		{
			reset($this->me);
			list(,$b)=each($this->me);
			return $b;
		}

		function scalartyp()
		{
			reset($this->me);
			list($a,$b)=each($this->me);
			if($a == xmlrpcI4)
			{
				$a = xmlrpcInt;
			}
			return $a;
		}

		function arraymem($m)
		{
			return $this->me['array'][$m];
		}

		function arraysize()
		{
			return count($this->me['array']);
		}

		function structsize()
		{
			return count($this->me['struct']);
		}

		// DEPRECATED! this code looks like it is very fragile and has not been fixed
		// for a long long time. Shall we remove it for 2.0?
		function getval()
		{
			// UNSTABLE ?
			reset($this->me);
			list($a,$b)=each($this->me);
			// contributed by I Sofer, 2001-03-24
			// add support for nested arrays to scalarval
			// i've created a new method here, so as to
			// preserve back compatibility

			if(is_array($b))
			{
				foreach($b as $id => $cont)
				{
					$b[$id] = $cont->scalarval();
				}
			}

			// add support for structures directly encoding php objects
			if(is_object($b))
			{
				$t = get_object_vars($b);
				foreach($t as $id => $cont)
				{
					$t[$id] = $cont->scalarval();
				}

				foreach($t as $id => $cont)
				{
					@$b->$id = $cont;
				}
			}
			// end contrib
			return $b;
		}
	}
?>