1<?php
2
3/*
4$Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
5
6NuSOAP - Web Services Toolkit for PHP
7
8Copyright (c) 2002 NuSphere Corporation
9
10This library is free software; you can redistribute it and/or
11modify it under the terms of the GNU Lesser General Public
12License as published by the Free Software Foundation; either
13version 2.1 of the License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18Lesser General Public License for more details.
19
20You should have received a copy of the GNU Lesser General Public
21License along with this library; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24If you have any questions or comments, please email:
25
26Dietrich Ayala
27dietrich@ganx4.com
28http://dietrich.ganx4.com/nusoap
29
30NuSphere Corporation
31http://www.nusphere.com
32
33*/
34
35/* load classes
36
37// necessary classes
38require_once('class.soapclient.php');
39require_once('class.soap_val.php');
40require_once('class.soap_parser.php');
41require_once('class.soap_fault.php');
42
43// transport classes
44require_once('class.soap_transport_http.php');
45
46// optional add-on classes
47require_once('class.xmlschema.php');
48require_once('class.wsdl.php');
49
50// server class
51require_once('class.soap_server.php');*/
52
53// class variable emulation
54// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
55$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
56
57/**
58*
59* nusoap_base
60*
61* @author   Dietrich Ayala <dietrich@ganx4.com>
62* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
63* @access   public
64*/
65class nusoap_base {
66	/**
67	 * Identification for HTTP headers.
68	 *
69	 * @var string
70	 * @access private
71	 */
72	var $title = 'NuSOAP';
73	/**
74	 * Version for HTTP headers.
75	 *
76	 * @var string
77	 * @access private
78	 */
79	var $version = '0.7.2';
80	/**
81	 * CVS revision for HTTP headers.
82	 *
83	 * @var string
84	 * @access private
85	 */
86	var $revision = '$Revision: 1.94 $';
87    /**
88     * Current error string (manipulated by getError/setError)
89	 *
90	 * @var string
91	 * @access private
92	 */
93	var $error_str = '';
94    /**
95     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
96	 *
97	 * @var string
98	 * @access private
99	 */
100    var $debug_str = '';
101    /**
102	 * toggles automatic encoding of special characters as entities
103	 * (should always be true, I think)
104	 *
105	 * @var boolean
106	 * @access private
107	 */
108	var $charencoding = true;
109	/**
110	 * the debug level for this instance
111	 *
112	 * @var	integer
113	 * @access private
114	 */
115	var $debugLevel;
116
117    /**
118	* set schema version
119	*
120	* @var      string
121	* @access   public
122	*/
123	var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
124
125    /**
126	* charset encoding for outgoing messages
127	*
128	* @var      string
129	* @access   public
130	*/
131    var $soap_defencoding = 'ISO-8859-1';
132	//var $soap_defencoding = 'UTF-8';
133
134	/**
135	* namespaces in an array of prefix => uri
136	*
137	* this is "seeded" by a set of constants, but it may be altered by code
138	*
139	* @var      array
140	* @access   public
141	*/
142	var $namespaces = array(
143		'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
144		'xsd' => 'http://www.w3.org/2001/XMLSchema',
145		'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
146		'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
147		);
148
149	/**
150	* namespaces used in the current context, e.g. during serialization
151	*
152	* @var      array
153	* @access   private
154	*/
155	var $usedNamespaces = array();
156
157	/**
158	* XML Schema types in an array of uri => (array of xml type => php type)
159	* is this legacy yet?
160	* no, this is used by the xmlschema class to verify type => namespace mappings.
161	* @var      array
162	* @access   public
163	*/
164	var $typemap = array(
165	'http://www.w3.org/2001/XMLSchema' => array(
166		'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
167		'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
168		'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
169		// abstract "any" types
170		'anyType'=>'string','anySimpleType'=>'string',
171		// derived datatypes
172		'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
173		'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
174		'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
175		'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
176	'http://www.w3.org/2000/10/XMLSchema' => array(
177		'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
178		'float'=>'double','dateTime'=>'string',
179		'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
180	'http://www.w3.org/1999/XMLSchema' => array(
181		'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
182		'float'=>'double','dateTime'=>'string',
183		'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
184	'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
185	'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
186    'http://xml.apache.org/xml-soap' => array('Map')
187	);
188
189	/**
190	* XML entities to convert
191	*
192	* @var      array
193	* @access   public
194	* @deprecated
195	* @see	expandEntities
196	*/
197	var $xmlEntities = array('quot' => '"','amp' => '&',
198		'lt' => '<','gt' => '>','apos' => "'");
199
200	/**
201	* constructor
202	*
203	* @access	public
204	*/
205	function nusoap_base() {
206		$this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
207	}
208
209	/**
210	* gets the global debug level, which applies to future instances
211	*
212	* @return	integer	Debug level 0-9, where 0 turns off
213	* @access	public
214	*/
215	function getGlobalDebugLevel() {
216		return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
217	}
218
219	/**
220	* sets the global debug level, which applies to future instances
221	*
222	* @param	int	$level	Debug level 0-9, where 0 turns off
223	* @access	public
224	*/
225	function setGlobalDebugLevel($level) {
226		$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
227	}
228
229	/**
230	* gets the debug level for this instance
231	*
232	* @return	int	Debug level 0-9, where 0 turns off
233	* @access	public
234	*/
235	function getDebugLevel() {
236		return $this->debugLevel;
237	}
238
239	/**
240	* sets the debug level for this instance
241	*
242	* @param	int	$level	Debug level 0-9, where 0 turns off
243	* @access	public
244	*/
245	function setDebugLevel($level) {
246		$this->debugLevel = $level;
247	}
248
249	/**
250	* adds debug data to the instance debug string with formatting
251	*
252	* @param    string $string debug data
253	* @access   private
254	*/
255	function debug($string){
256		if ($this->debugLevel > 0) {
257			$this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
258		}
259	}
260
261	/**
262	* adds debug data to the instance debug string without formatting
263	*
264	* @param    string $string debug data
265	* @access   public
266	*/
267	function appendDebug($string){
268		if ($this->debugLevel > 0) {
269			// it would be nice to use a memory stream here to use
270			// memory more efficiently
271			$this->debug_str .= $string;
272		}
273	}
274
275	/**
276	* clears the current debug data for this instance
277	*
278	* @access   public
279	*/
280	function clearDebug() {
281		// it would be nice to use a memory stream here to use
282		// memory more efficiently
283		$this->debug_str = '';
284	}
285
286	/**
287	* gets the current debug data for this instance
288	*
289	* @return   debug data
290	* @access   public
291	*/
292	function &getDebug() {
293		// it would be nice to use a memory stream here to use
294		// memory more efficiently
295		return $this->debug_str;
296	}
297
298	/**
299	* gets the current debug data for this instance as an XML comment
300	* this may change the contents of the debug data
301	*
302	* @return   debug data as an XML comment
303	* @access   public
304	*/
305	function &getDebugAsXMLComment() {
306		// it would be nice to use a memory stream here to use
307		// memory more efficiently
308		while (strpos($this->debug_str, '--')) {
309			$this->debug_str = str_replace('--', '- -', $this->debug_str);
310		}
311    	return "<!--\n" . $this->debug_str . "\n-->";
312	}
313
314	/**
315	* expands entities, e.g. changes '<' to '&lt;'.
316	*
317	* @param	string	$val	The string in which to expand entities.
318	* @access	private
319	*/
320	function expandEntities($val) {
321		if ($this->charencoding) {
322	    	$val = str_replace('&', '&amp;', $val);
323	    	$val = str_replace("'", '&apos;', $val);
324	    	$val = str_replace('"', '&quot;', $val);
325	    	$val = str_replace('<', '&lt;', $val);
326	    	$val = str_replace('>', '&gt;', $val);
327	    }
328	    return $val;
329	}
330
331	/**
332	* returns error string if present
333	*
334	* @return   mixed error string or false
335	* @access   public
336	*/
337	function getError(){
338		if($this->error_str != ''){
339			return $this->error_str;
340		}
341		return false;
342	}
343
344	/**
345	* sets error string
346	*
347	* @return   boolean $string error string
348	* @access   private
349	*/
350	function setError($str){
351		$this->error_str = $str;
352	}
353
354	/**
355	* detect if array is a simple array or a struct (associative array)
356	*
357	* @param	mixed	$val	The PHP array
358	* @return	string	(arraySimple|arrayStruct)
359	* @access	private
360	*/
361	function isArraySimpleOrStruct($val) {
362        $keyList = array_keys($val);
363		foreach ($keyList as $keyListValue) {
364			if (!is_int($keyListValue)) {
365				return 'arrayStruct';
366			}
367		}
368		return 'arraySimple';
369	}
370
371	/**
372	* serializes PHP values in accordance w/ section 5. Type information is
373	* not serialized if $use == 'literal'.
374	*
375	* @param	mixed	$val	The value to serialize
376	* @param	string	$name	The name (local part) of the XML element
377	* @param	string	$type	The XML schema type (local part) for the element
378	* @param	string	$name_ns	The namespace for the name of the XML element
379	* @param	string	$type_ns	The namespace for the type of the element
380	* @param	array	$attributes	The attributes to serialize as name=>value pairs
381	* @param	string	$use	The WSDL "use" (encoded|literal)
382	* @return	string	The serialized element, possibly with child elements
383    * @access	public
384	*/
385	function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
386		$this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
387		$this->appendDebug('value=' . $this->varDump($val));
388		$this->appendDebug('attributes=' . $this->varDump($attributes));
389
390    	if(is_object($val) && get_class($val) == 'soapval'){
391        	return $val->serialize($use);
392        }
393		// force valid name if necessary
394		if (is_numeric($name)) {
395			$name = '__numeric_' . $name;
396		} elseif (! $name) {
397			$name = 'noname';
398		}
399		// if name has ns, add ns prefix to name
400		$xmlns = '';
401        if($name_ns){
402			$prefix = 'nu'.rand(1000,9999);
403			$name = $prefix.':'.$name;
404			$xmlns .= " xmlns:$prefix=\"$name_ns\"";
405		}
406		// if type is prefixed, create type prefix
407		if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
408			// need to fix this. shouldn't default to xsd if no ns specified
409		    // w/o checking against typemap
410			$type_prefix = 'xsd';
411		} elseif($type_ns){
412			$type_prefix = 'ns'.rand(1000,9999);
413			$xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
414		}
415		// serialize attributes if present
416		$atts = '';
417		if($attributes){
418			foreach($attributes as $k => $v){
419				$atts .= " $k=\"".$this->expandEntities($v).'"';
420			}
421		}
422		// serialize null value
423		if (is_null($val)) {
424			if ($use == 'literal') {
425				// TODO: depends on minOccurs
426	        	return "<$name$xmlns $atts/>";
427        	} else {
428				if (isset($type) && isset($type_prefix)) {
429					$type_str = " xsi:type=\"$type_prefix:$type\"";
430				} else {
431					$type_str = '';
432				}
433	        	return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
434        	}
435		}
436        // serialize if an xsd built-in primitive type
437        if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
438        	if (is_bool($val)) {
439        		if ($type == 'boolean') {
440	        		$val = $val ? 'true' : 'false';
441	        	} elseif (! $val) {
442	        		$val = 0;
443	        	}
444			} else if (is_string($val)) {
445				$val = $this->expandEntities($val);
446			}
447			if ($use == 'literal') {
448	        	return "<$name$xmlns $atts>$val</$name>";
449        	} else {
450	        	return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
451        	}
452        }
453		// detect type and serialize
454		$xml = '';
455		switch(true) {
456			case (is_bool($val) || $type == 'boolean'):
457        		if ($type == 'boolean') {
458	        		$val = $val ? 'true' : 'false';
459	        	} elseif (! $val) {
460	        		$val = 0;
461	        	}
462				if ($use == 'literal') {
463					$xml .= "<$name$xmlns $atts>$val</$name>";
464				} else {
465					$xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
466				}
467				break;
468			case (is_int($val) || is_long($val) || $type == 'int'):
469				if ($use == 'literal') {
470					$xml .= "<$name$xmlns $atts>$val</$name>";
471				} else {
472					$xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
473				}
474				break;
475			case (is_float($val)|| is_double($val) || $type == 'float'):
476				if ($use == 'literal') {
477					$xml .= "<$name$xmlns $atts>$val</$name>";
478				} else {
479					$xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
480				}
481				break;
482			case (is_string($val) || $type == 'string'):
483				$val = $this->expandEntities($val);
484				if ($use == 'literal') {
485					$xml .= "<$name$xmlns $atts>$val</$name>";
486				} else {
487					$xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
488				}
489				break;
490			case is_object($val):
491				if (! $name) {
492					$name = get_class($val);
493					$this->debug("In serialize_val, used class name $name as element name");
494				} else {
495					$this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
496				}
497				foreach(get_object_vars($val) as $k => $v){
498					$pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
499				}
500				$xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
501				break;
502			break;
503			case (is_array($val) || $type):
504				// detect if struct or array
505				$valueType = $this->isArraySimpleOrStruct($val);
506                if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
507					$i = 0;
508					if(is_array($val) && count($val)> 0){
509						foreach($val as $v){
510	                    	if(is_object($v) && get_class($v) ==  'soapval'){
511								$tt_ns = $v->type_ns;
512								$tt = $v->type;
513							} elseif (is_array($v)) {
514								$tt = $this->isArraySimpleOrStruct($v);
515							} else {
516								$tt = gettype($v);
517	                        }
518							$array_types[$tt] = 1;
519							// TODO: for literal, the name should be $name
520							$xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
521							++$i;
522						}
523						if(count($array_types) > 1){
524							$array_typename = 'xsd:anyType';
525						} elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
526							if ($tt == 'integer') {
527								$tt = 'int';
528							}
529							$array_typename = 'xsd:'.$tt;
530						} elseif(isset($tt) && $tt == 'arraySimple'){
531							$array_typename = 'SOAP-ENC:Array';
532						} elseif(isset($tt) && $tt == 'arrayStruct'){
533							$array_typename = 'unnamed_struct_use_soapval';
534						} else {
535							// if type is prefixed, create type prefix
536							if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
537								 $array_typename = 'xsd:' . $tt;
538							} elseif ($tt_ns) {
539								$tt_prefix = 'ns' . rand(1000, 9999);
540								$array_typename = "$tt_prefix:$tt";
541								$xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
542							} else {
543								$array_typename = $tt;
544							}
545						}
546						$array_type = $i;
547						if ($use == 'literal') {
548							$type_str = '';
549						} else if (isset($type) && isset($type_prefix)) {
550							$type_str = " xsi:type=\"$type_prefix:$type\"";
551						} else {
552							$type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
553						}
554					// empty array
555					} else {
556						if ($use == 'literal') {
557							$type_str = '';
558						} else if (isset($type) && isset($type_prefix)) {
559							$type_str = " xsi:type=\"$type_prefix:$type\"";
560						} else {
561							$type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
562						}
563					}
564					// TODO: for array in literal, there is no wrapper here
565					$xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
566				} else {
567					// got a struct
568					if(isset($type) && isset($type_prefix)){
569						$type_str = " xsi:type=\"$type_prefix:$type\"";
570					} else {
571						$type_str = '';
572					}
573					if ($use == 'literal') {
574						$xml .= "<$name$xmlns $atts>";
575					} else {
576						$xml .= "<$name$xmlns$type_str$atts>";
577					}
578					foreach($val as $k => $v){
579						// Apache Map
580						if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
581							$xml .= '<item>';
582							$xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
583							$xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
584							$xml .= '</item>';
585						} else {
586							$xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
587						}
588					}
589					$xml .= "</$name>";
590				}
591				break;
592			default:
593				$xml .= 'not detected, got '.gettype($val).' for '.$val;
594				break;
595		}
596		return $xml;
597	}
598
599    /**
600    * serializes a message
601    *
602    * @param string $body the XML of the SOAP body
603    * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
604    * @param array $namespaces optional the namespaces used in generating the body and headers
605    * @param string $style optional (rpc|document)
606    * @param string $use optional (encoded|literal)
607    * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
608    * @return string the message
609    * @access public
610    */
611    function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
612    // TODO: add an option to automatically run utf8_encode on $body and $headers
613    // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
614    // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
615
616	$this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
617	$this->debug("headers:");
618	$this->appendDebug($this->varDump($headers));
619	$this->debug("namespaces:");
620	$this->appendDebug($this->varDump($namespaces));
621
622	// serialize namespaces
623    $ns_string = '';
624	foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
625		$ns_string .= " xmlns:$k=\"$v\"";
626	}
627	if($encodingStyle) {
628		$ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
629	}
630
631	// serialize headers
632	if($headers){
633		if (is_array($headers)) {
634			$xml = '';
635			foreach ($headers as $header) {
636				$xml .= $this->serialize_val($header, false, false, false, false, false, $use);
637			}
638			$headers = $xml;
639			$this->debug("In serializeEnvelope, serialzied array of headers to $headers");
640		}
641		$headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
642	}
643	// serialize envelope
644	return
645	'<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
646	'<SOAP-ENV:Envelope'.$ns_string.">".
647	$headers.
648	"<SOAP-ENV:Body>".
649		$body.
650	"</SOAP-ENV:Body>".
651	"</SOAP-ENV:Envelope>";
652    }
653
654	/**
655	 * formats a string to be inserted into an HTML stream
656	 *
657	 * @param string $str The string to format
658	 * @return string The formatted string
659	 * @access public
660	 * @deprecated
661	 */
662    function formatDump($str){
663		$str = htmlspecialchars($str);
664		return nl2br($str);
665    }
666
667	/**
668	* contracts (changes namespace to prefix) a qualified name
669	*
670	* @param    string $qname qname
671	* @return	string contracted qname
672	* @access   private
673	*/
674	function contractQname($qname){
675		// get element namespace
676		//$this->xdebug("Contract $qname");
677		if (strrpos($qname, ':')) {
678			// get unqualified name
679			$name = substr($qname, strrpos($qname, ':') + 1);
680			// get ns
681			$ns = substr($qname, 0, strrpos($qname, ':'));
682			$p = $this->getPrefixFromNamespace($ns);
683			if ($p) {
684				return $p . ':' . $name;
685			}
686			return $qname;
687		} else {
688			return $qname;
689		}
690	}
691
692	/**
693	* expands (changes prefix to namespace) a qualified name
694	*
695	* @param    string $string qname
696	* @return	string expanded qname
697	* @access   private
698	*/
699	function expandQname($qname){
700		// get element prefix
701		if(strpos($qname,':') && !ereg('^http://',$qname)){
702			// get unqualified name
703			$name = substr(strstr($qname,':'),1);
704			// get ns prefix
705			$prefix = substr($qname,0,strpos($qname,':'));
706			if(isset($this->namespaces[$prefix])){
707				return $this->namespaces[$prefix].':'.$name;
708			} else {
709				return $qname;
710			}
711		} else {
712			return $qname;
713		}
714	}
715
716    /**
717    * returns the local part of a prefixed string
718    * returns the original string, if not prefixed
719    *
720    * @param string $str The prefixed string
721    * @return string The local part
722    * @access public
723    */
724	function getLocalPart($str){
725		if($sstr = strrchr($str,':')){
726			// get unqualified name
727			return substr( $sstr, 1 );
728		} else {
729			return $str;
730		}
731	}
732
733	/**
734    * returns the prefix part of a prefixed string
735    * returns false, if not prefixed
736    *
737    * @param string $str The prefixed string
738    * @return mixed The prefix or false if there is no prefix
739    * @access public
740    */
741	function getPrefix($str){
742		if($pos = strrpos($str,':')){
743			// get prefix
744			return substr($str,0,$pos);
745		}
746		return false;
747	}
748
749	/**
750    * pass it a prefix, it returns a namespace
751    *
752    * @param string $prefix The prefix
753    * @return mixed The namespace, false if no namespace has the specified prefix
754    * @access public
755    */
756	function getNamespaceFromPrefix($prefix){
757		if (isset($this->namespaces[$prefix])) {
758			return $this->namespaces[$prefix];
759		}
760		//$this->setError("No namespace registered for prefix '$prefix'");
761		return false;
762	}
763
764	/**
765    * returns the prefix for a given namespace (or prefix)
766    * or false if no prefixes registered for the given namespace
767    *
768    * @param string $ns The namespace
769    * @return mixed The prefix, false if the namespace has no prefixes
770    * @access public
771    */
772	function getPrefixFromNamespace($ns) {
773		foreach ($this->namespaces as $p => $n) {
774			if ($ns == $n || $ns == $p) {
775			    $this->usedNamespaces[$p] = $n;
776				return $p;
777			}
778		}
779		return false;
780	}
781
782	/**
783    * returns the time in ODBC canonical form with microseconds
784    *
785    * @return string The time in ODBC canonical form with microseconds
786    * @access public
787    */
788	function getmicrotime() {
789		if (function_exists('gettimeofday')) {
790			$tod = gettimeofday();
791			$sec = $tod['sec'];
792			$usec = $tod['usec'];
793		} else {
794			$sec = time();
795			$usec = 0;
796		}
797		return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
798	}
799
800	/**
801	 * Returns a string with the output of var_dump
802	 *
803	 * @param mixed $data The variable to var_dump
804	 * @return string The output of var_dump
805	 * @access public
806	 */
807    function varDump($data) {
808		ob_start();
809		var_dump($data);
810		$ret_val = ob_get_contents();
811		ob_end_clean();
812		return $ret_val;
813	}
814}
815
816// XML Schema Datatype Helper Functions
817
818//xsd:dateTime helpers
819
820/**
821* convert unix timestamp to ISO 8601 compliant date string
822*
823* @param    string $timestamp Unix time stamp
824* @access   public
825*/
826function timestamp_to_iso8601($timestamp,$utc=true){
827	$datestr = date('Y-m-d\TH:i:sO',$timestamp);
828	if($utc){
829		$eregStr =
830		'([0-9]{4})-'.	// centuries & years CCYY-
831		'([0-9]{2})-'.	// months MM-
832		'([0-9]{2})'.	// days DD
833		'T'.			// separator T
834		'([0-9]{2}):'.	// hours hh:
835		'([0-9]{2}):'.	// minutes mm:
836		'([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
837		'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
838
839		if(ereg($eregStr,$datestr,$regs)){
840			return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
841		}
842		return false;
843	} else {
844		return $datestr;
845	}
846}
847
848/**
849* convert ISO 8601 compliant date string to unix timestamp
850*
851* @param    string $datestr ISO 8601 compliant date string
852* @access   public
853*/
854function iso8601_to_timestamp($datestr){
855	$eregStr =
856	'([0-9]{4})-'.	// centuries & years CCYY-
857	'([0-9]{2})-'.	// months MM-
858	'([0-9]{2})'.	// days DD
859	'T'.			// separator T
860	'([0-9]{2}):'.	// hours hh:
861	'([0-9]{2}):'.	// minutes mm:
862	'([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
863	'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
864	if(ereg($eregStr,$datestr,$regs)){
865		// not utc
866		if($regs[8] != 'Z'){
867			$op = substr($regs[8],0,1);
868			$h = substr($regs[8],1,2);
869			$m = substr($regs[8],strlen($regs[8])-2,2);
870			if($op == '-'){
871				$regs[4] = $regs[4] + $h;
872				$regs[5] = $regs[5] + $m;
873			} elseif($op == '+'){
874				$regs[4] = $regs[4] - $h;
875				$regs[5] = $regs[5] - $m;
876			}
877		}
878		return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
879	} else {
880		return false;
881	}
882}
883
884/**
885* sleeps some number of microseconds
886*
887* @param    string $usec the number of microseconds to sleep
888* @access   public
889* @deprecated
890*/
891function usleepWindows($usec)
892{
893	$start = gettimeofday();
894
895	do
896	{
897		$stop = gettimeofday();
898		$timePassed = 1000000 * ($stop['sec'] - $start['sec'])
899		+ $stop['usec'] - $start['usec'];
900	}
901	while ($timePassed < $usec);
902}
903
904?><?php
905
906
907
908/**
909* Contains information for a SOAP fault.
910* Mainly used for returning faults from deployed functions
911* in a server instance.
912* @author   Dietrich Ayala <dietrich@ganx4.com>
913* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
914* @access public
915*/
916class soap_fault extends nusoap_base {
917	/**
918	 * The fault code (client|server)
919	 * @var string
920	 * @access private
921	 */
922	var $faultcode;
923	/**
924	 * The fault actor
925	 * @var string
926	 * @access private
927	 */
928	var $faultactor;
929	/**
930	 * The fault string, a description of the fault
931	 * @var string
932	 * @access private
933	 */
934	var $faultstring;
935	/**
936	 * The fault detail, typically a string or array of string
937	 * @var mixed
938	 * @access private
939	 */
940	var $faultdetail;
941
942	/**
943	* constructor
944    *
945    * @param string $faultcode (client | server)
946    * @param string $faultactor only used when msg routed between multiple actors
947    * @param string $faultstring human readable error message
948    * @param mixed $faultdetail detail, typically a string or array of string
949	*/
950	function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
951		parent::nusoap_base();
952		$this->faultcode = $faultcode;
953		$this->faultactor = $faultactor;
954		$this->faultstring = $faultstring;
955		$this->faultdetail = $faultdetail;
956	}
957
958	/**
959	* serialize a fault
960	*
961	* @return	string	The serialization of the fault instance.
962	* @access   public
963	*/
964	function serialize(){
965		$ns_string = '';
966		foreach($this->namespaces as $k => $v){
967			$ns_string .= "\n  xmlns:$k=\"$v\"";
968		}
969		$return_msg =
970			'<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
971			'<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
972				'<SOAP-ENV:Body>'.
973				'<SOAP-ENV:Fault>'.
974					$this->serialize_val($this->faultcode, 'faultcode').
975					$this->serialize_val($this->faultactor, 'faultactor').
976					$this->serialize_val($this->faultstring, 'faultstring').
977					$this->serialize_val($this->faultdetail, 'detail').
978				'</SOAP-ENV:Fault>'.
979				'</SOAP-ENV:Body>'.
980			'</SOAP-ENV:Envelope>';
981		return $return_msg;
982	}
983}
984
985
986
987?><?php
988
989
990
991/**
992* parses an XML Schema, allows access to it's data, other utility methods
993* no validation... yet.
994* very experimental and limited. As is discussed on XML-DEV, I'm one of the people
995* that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
996* tutorials I refer to :)
997*
998* @author   Dietrich Ayala <dietrich@ganx4.com>
999* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
1000* @access   public
1001*/
1002class XMLSchema extends nusoap_base  {
1003
1004	// files
1005	var $schema = '';
1006	var $xml = '';
1007	// namespaces
1008	var $enclosingNamespaces;
1009	// schema info
1010	var $schemaInfo = array();
1011	var $schemaTargetNamespace = '';
1012	// types, elements, attributes defined by the schema
1013	var $attributes = array();
1014	var $complexTypes = array();
1015	var $complexTypeStack = array();
1016	var $currentComplexType = null;
1017	var $elements = array();
1018	var $elementStack = array();
1019	var $currentElement = null;
1020	var $simpleTypes = array();
1021	var $simpleTypeStack = array();
1022	var $currentSimpleType = null;
1023	// imports
1024	var $imports = array();
1025	// parser vars
1026	var $parser;
1027	var $position = 0;
1028	var $depth = 0;
1029	var $depth_array = array();
1030	var $message = array();
1031	var $defaultNamespace = array();
1032
1033	/**
1034	* constructor
1035	*
1036	* @param    string $schema schema document URI
1037	* @param    string $xml xml document URI
1038	* @param	string $namespaces namespaces defined in enclosing XML
1039	* @access   public
1040	*/
1041	function XMLSchema($schema='',$xml='',$namespaces=array()){
1042		parent::nusoap_base();
1043		$this->debug('xmlschema class instantiated, inside constructor');
1044		// files
1045		$this->schema = $schema;
1046		$this->xml = $xml;
1047
1048		// namespaces
1049		$this->enclosingNamespaces = $namespaces;
1050		$this->namespaces = array_merge($this->namespaces, $namespaces);
1051
1052		// parse schema file
1053		if($schema != ''){
1054			$this->debug('initial schema file: '.$schema);
1055			$this->parseFile($schema, 'schema');
1056		}
1057
1058		// parse xml file
1059		if($xml != ''){
1060			$this->debug('initial xml file: '.$xml);
1061			$this->parseFile($xml, 'xml');
1062		}
1063
1064	}
1065
1066    /**
1067    * parse an XML file
1068    *
1069    * @param string $xml, path/URL to XML file
1070    * @param string $type, (schema | xml)
1071	* @return boolean
1072    * @access public
1073    */
1074	function parseFile($xml,$type){
1075		// parse xml file
1076		if($xml != ""){
1077			$xmlStr = @join("",@file($xml));
1078			if($xmlStr == ""){
1079				$msg = 'Error reading XML from '.$xml;
1080				$this->setError($msg);
1081				$this->debug($msg);
1082			return false;
1083			} else {
1084				$this->debug("parsing $xml");
1085				$this->parseString($xmlStr,$type);
1086				$this->debug("done parsing $xml");
1087			return true;
1088			}
1089		}
1090		return false;
1091	}
1092
1093	/**
1094	* parse an XML string
1095	*
1096	* @param    string $xml path or URL
1097    * @param string $type, (schema|xml)
1098	* @access   private
1099	*/
1100	function parseString($xml,$type){
1101		// parse xml string
1102		if($xml != ""){
1103
1104	    	// Create an XML parser.
1105	    	$this->parser = xml_parser_create();
1106	    	// Set the options for parsing the XML data.
1107	    	xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1108
1109	    	// Set the object for the parser.
1110	    	xml_set_object($this->parser, $this);
1111
1112	    	// Set the element handlers for the parser.
1113			if($type == "schema"){
1114		    	xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1115		    	xml_set_character_data_handler($this->parser,'schemaCharacterData');
1116			} elseif($type == "xml"){
1117				xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1118		    	xml_set_character_data_handler($this->parser,'xmlCharacterData');
1119			}
1120
1121		    // Parse the XML file.
1122		    if(!xml_parse($this->parser,$xml,true)){
1123			// Display an error message.
1124				$errstr = sprintf('XML error parsing XML schema on line %d: %s',
1125				xml_get_current_line_number($this->parser),
1126				xml_error_string(xml_get_error_code($this->parser))
1127				);
1128				$this->debug($errstr);
1129				$this->debug("XML payload:\n" . $xml);
1130				$this->setError($errstr);
1131	    	}
1132
1133			xml_parser_free($this->parser);
1134		} else{
1135			$this->debug('no xml passed to parseString()!!');
1136			$this->setError('no xml passed to parseString()!!');
1137		}
1138	}
1139
1140	/**
1141	* start-element handler
1142	*
1143	* @param    string $parser XML parser object
1144	* @param    string $name element name
1145	* @param    string $attrs associative array of attributes
1146	* @access   private
1147	*/
1148	function schemaStartElement($parser, $name, $attrs) {
1149
1150		// position in the total number of elements, starting from 0
1151		$pos = $this->position++;
1152		$depth = $this->depth++;
1153		// set self as current value for this depth
1154		$this->depth_array[$depth] = $pos;
1155		$this->message[$pos] = array('cdata' => '');
1156		if ($depth > 0) {
1157			$this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1158		} else {
1159			$this->defaultNamespace[$pos] = false;
1160		}
1161
1162		// get element prefix
1163		if($prefix = $this->getPrefix($name)){
1164			// get unqualified name
1165			$name = $this->getLocalPart($name);
1166		} else {
1167        	$prefix = '';
1168        }
1169
1170        // loop thru attributes, expanding, and registering namespace declarations
1171        if(count($attrs) > 0){
1172        	foreach($attrs as $k => $v){
1173                // if ns declarations, add to class level array of valid namespaces
1174				if(ereg("^xmlns",$k)){
1175                	//$this->xdebug("$k: $v");
1176                	//$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1177                	if($ns_prefix = substr(strrchr($k,':'),1)){
1178                		//$this->xdebug("Add namespace[$ns_prefix] = $v");
1179						$this->namespaces[$ns_prefix] = $v;
1180					} else {
1181						$this->defaultNamespace[$pos] = $v;
1182						if (! $this->getPrefixFromNamespace($v)) {
1183							$this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1184						}
1185					}
1186					if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1187						$this->XMLSchemaVersion = $v;
1188						$this->namespaces['xsi'] = $v.'-instance';
1189					}
1190				}
1191        	}
1192        	foreach($attrs as $k => $v){
1193                // expand each attribute
1194                $k = strpos($k,':') ? $this->expandQname($k) : $k;
1195                $v = strpos($v,':') ? $this->expandQname($v) : $v;
1196        		$eAttrs[$k] = $v;
1197        	}
1198        	$attrs = $eAttrs;
1199        } else {
1200        	$attrs = array();
1201        }
1202		// find status, register data
1203		switch($name){
1204			case 'all':			// (optional) compositor content for a complexType
1205			case 'choice':
1206			case 'group':
1207			case 'sequence':
1208				//$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1209				$this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1210				//if($name == 'all' || $name == 'sequence'){
1211				//	$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1212				//}
1213			break;
1214			case 'attribute':	// complexType attribute
1215            	//$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1216            	$this->xdebug("parsing attribute:");
1217            	$this->appendDebug($this->varDump($attrs));
1218				if (!isset($attrs['form'])) {
1219					$attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1220				}
1221            	if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1222					$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1223					if (!strpos($v, ':')) {
1224						// no namespace in arrayType attribute value...
1225						if ($this->defaultNamespace[$pos]) {
1226							// ...so use the default
1227							$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1228						}
1229					}
1230            	}
1231                if(isset($attrs['name'])){
1232					$this->attributes[$attrs['name']] = $attrs;
1233					$aname = $attrs['name'];
1234				} elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1235					if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1236	                	$aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1237	                } else {
1238	                	$aname = '';
1239	                }
1240				} elseif(isset($attrs['ref'])){
1241					$aname = $attrs['ref'];
1242                    $this->attributes[$attrs['ref']] = $attrs;
1243				}
1244
1245				if($this->currentComplexType){	// This should *always* be
1246					$this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1247				}
1248				// arrayType attribute
1249				if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1250					$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1251                	$prefix = $this->getPrefix($aname);
1252					if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1253						$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1254					} else {
1255						$v = '';
1256					}
1257                    if(strpos($v,'[,]')){
1258                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1259                    }
1260                    $v = substr($v,0,strpos($v,'[')); // clip the []
1261                    if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1262                        $v = $this->XMLSchemaVersion.':'.$v;
1263                    }
1264                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1265				}
1266			break;
1267			case 'complexContent':	// (optional) content for a complexType
1268			break;
1269			case 'complexType':
1270				array_push($this->complexTypeStack, $this->currentComplexType);
1271				if(isset($attrs['name'])){
1272					$this->xdebug('processing named complexType '.$attrs['name']);
1273					//$this->currentElement = false;
1274					$this->currentComplexType = $attrs['name'];
1275					$this->complexTypes[$this->currentComplexType] = $attrs;
1276					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1277					// This is for constructs like
1278					//           <complexType name="ListOfString" base="soap:Array">
1279					//                <sequence>
1280					//                    <element name="string" type="xsd:string"
1281					//                        minOccurs="0" maxOccurs="unbounded" />
1282					//                </sequence>
1283					//            </complexType>
1284					if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1285						$this->xdebug('complexType is unusual array');
1286						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1287					} else {
1288						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1289					}
1290				}else{
1291					$this->xdebug('processing unnamed complexType for element '.$this->currentElement);
1292					$this->currentComplexType = $this->currentElement . '_ContainedType';
1293					//$this->currentElement = false;
1294					$this->complexTypes[$this->currentComplexType] = $attrs;
1295					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1296					// This is for constructs like
1297					//           <complexType name="ListOfString" base="soap:Array">
1298					//                <sequence>
1299					//                    <element name="string" type="xsd:string"
1300					//                        minOccurs="0" maxOccurs="unbounded" />
1301					//                </sequence>
1302					//            </complexType>
1303					if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1304						$this->xdebug('complexType is unusual array');
1305						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1306					} else {
1307						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1308					}
1309				}
1310			break;
1311			case 'element':
1312				array_push($this->elementStack, $this->currentElement);
1313				// elements defined as part of a complex type should
1314				// not really be added to $this->elements, but for some
1315				// reason, they are
1316				if (!isset($attrs['form'])) {
1317					$attrs['form'] = $this->schemaInfo['elementFormDefault'];
1318				}
1319				if(isset($attrs['type'])){
1320					$this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1321					if (! $this->getPrefix($attrs['type'])) {
1322						if ($this->defaultNamespace[$pos]) {
1323							$attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1324							$this->xdebug('used default namespace to make type ' . $attrs['type']);
1325						}
1326					}
1327					// This is for constructs like
1328					//           <complexType name="ListOfString" base="soap:Array">
1329					//                <sequence>
1330					//                    <element name="string" type="xsd:string"
1331					//                        minOccurs="0" maxOccurs="unbounded" />
1332					//                </sequence>
1333					//            </complexType>
1334					if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1335						$this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1336						$this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1337					}
1338					$this->currentElement = $attrs['name'];
1339					$this->elements[ $attrs['name'] ] = $attrs;
1340					$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1341					$ename = $attrs['name'];
1342				} elseif(isset($attrs['ref'])){
1343					$this->xdebug("processing element as ref to ".$attrs['ref']);
1344					$this->currentElement = "ref to ".$attrs['ref'];
1345					$ename = $this->getLocalPart($attrs['ref']);
1346				} else {
1347					$this->xdebug("processing untyped element ".$attrs['name']);
1348					$this->currentElement = $attrs['name'];
1349					$this->elements[ $attrs['name'] ] = $attrs;
1350					$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1351					$attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1352					$this->elements[ $attrs['name'] ]['type'] = $attrs['type'];
1353					$ename = $attrs['name'];
1354				}
1355				if(isset($ename) && $this->currentComplexType){
1356					$this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1357				}
1358			break;
1359			case 'enumeration':	//	restriction value list member
1360				$this->xdebug('enumeration ' . $attrs['value']);
1361				if ($this->currentSimpleType) {
1362					$this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1363				} elseif ($this->currentComplexType) {
1364					$this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1365				}
1366			break;
1367			case 'extension':	// simpleContent or complexContent type extension
1368				$this->xdebug('extension ' . $attrs['base']);
1369				if ($this->currentComplexType) {
1370					$this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1371				}
1372			break;
1373			case 'import':
1374			    if (isset($attrs['schemaLocation'])) {
1375					//$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1376                    $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1377				} else {
1378					//$this->xdebug('import namespace ' . $attrs['namespace']);
1379                    $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1380					if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1381						$this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1382					}
1383				}
1384			break;
1385			case 'list':	// simpleType value list
1386			break;
1387			case 'restriction':	// simpleType, simpleContent or complexContent value restriction
1388				$this->xdebug('restriction ' . $attrs['base']);
1389				if($this->currentSimpleType){
1390					$this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1391				} elseif($this->currentComplexType){
1392					$this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1393					if(strstr($attrs['base'],':') == ':Array'){
1394						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1395					}
1396				}
1397			break;
1398			case 'schema':
1399				$this->schemaInfo = $attrs;
1400				$this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1401				if (isset($attrs['targetNamespace'])) {
1402					$this->schemaTargetNamespace = $attrs['targetNamespace'];
1403				}
1404				if (!isset($attrs['elementFormDefault'])) {
1405					$this->schemaInfo['elementFormDefault'] = 'unqualified';
1406				}
1407				if (!isset($attrs['attributeFormDefault'])) {
1408					$this->schemaInfo['attributeFormDefault'] = 'unqualified';
1409				}
1410			break;
1411			case 'simpleContent':	// (optional) content for a complexType
1412			break;
1413			case 'simpleType':
1414				array_push($this->simpleTypeStack, $this->currentSimpleType);
1415				if(isset($attrs['name'])){
1416					$this->xdebug("processing simpleType for name " . $attrs['name']);
1417					$this->currentSimpleType = $attrs['name'];
1418					$this->simpleTypes[ $attrs['name'] ] = $attrs;
1419					$this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1420					$this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1421				} else {
1422					$this->xdebug('processing unnamed simpleType for element '.$this->currentElement);
1423					$this->currentSimpleType = $this->currentElement . '_ContainedType';
1424					//$this->currentElement = false;
1425					$this->simpleTypes[$this->currentSimpleType] = $attrs;
1426					$this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1427				}
1428			break;
1429			case 'union':	// simpleType type list
1430			break;
1431			default:
1432				//$this->xdebug("do not have anything to do for element $name");
1433		}
1434	}
1435
1436	/**
1437	* end-element handler
1438	*
1439	* @param    string $parser XML parser object
1440	* @param    string $name element name
1441	* @access   private
1442	*/
1443	function schemaEndElement($parser, $name) {
1444		// bring depth down a notch
1445		$this->depth--;
1446		// position of current element is equal to the last value left in depth_array for my depth
1447		if(isset($this->depth_array[$this->depth])){
1448        	$pos = $this->depth_array[$this->depth];
1449        }
1450		// get element prefix
1451		if ($prefix = $this->getPrefix($name)){
1452			// get unqualified name
1453			$name = $this->getLocalPart($name);
1454		} else {
1455        	$prefix = '';
1456        }
1457		// move on...
1458		if($name == 'complexType'){
1459			$this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1460			$this->currentComplexType = array_pop($this->complexTypeStack);
1461			//$this->currentElement = false;
1462		}
1463		if($name == 'element'){
1464			$this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1465			$this->currentElement = array_pop($this->elementStack);
1466		}
1467		if($name == 'simpleType'){
1468			$this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1469			$this->currentSimpleType = array_pop($this->simpleTypeStack);
1470		}
1471	}
1472
1473	/**
1474	* element content handler
1475	*
1476	* @param    string $parser XML parser object
1477	* @param    string $data element content
1478	* @access   private
1479	*/
1480	function schemaCharacterData($parser, $data){
1481		$pos = $this->depth_array[$this->depth - 1];
1482		$this->message[$pos]['cdata'] .= $data;
1483	}
1484
1485	/**
1486	* serialize the schema
1487	*
1488	* @access   public
1489	*/
1490	function serializeSchema(){
1491
1492		$schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1493		$xml = '';
1494		// imports
1495		if (sizeof($this->imports) > 0) {
1496			foreach($this->imports as $ns => $list) {
1497				foreach ($list as $ii) {
1498					if ($ii['location'] != '') {
1499						$xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1500					} else {
1501						$xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1502					}
1503				}
1504			}
1505		}
1506		// complex types
1507		foreach($this->complexTypes as $typeName => $attrs){
1508			$contentStr = '';
1509			// serialize child elements
1510			if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1511				foreach($attrs['elements'] as $element => $eParts){
1512					if(isset($eParts['ref'])){
1513						$contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1514					} else {
1515						$contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1516						foreach ($eParts as $aName => $aValue) {
1517							// handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1518							if ($aName != 'name' && $aName != 'type') {
1519								$contentStr .= " $aName=\"$aValue\"";
1520							}
1521						}
1522						$contentStr .= "/>\n";
1523					}
1524				}
1525				// compositor wraps elements
1526				if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1527					$contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1528				}
1529			}
1530			// attributes
1531			if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1532				foreach($attrs['attrs'] as $attr => $aParts){
1533					$contentStr .= "    <$schemaPrefix:attribute";
1534					foreach ($aParts as $a => $v) {
1535						if ($a == 'ref' || $a == 'type') {
1536							$contentStr .= " $a=\"".$this->contractQName($v).'"';
1537						} elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1538							$this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1539							$contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1540						} else {
1541							$contentStr .= " $a=\"$v\"";
1542						}
1543					}
1544					$contentStr .= "/>\n";
1545				}
1546			}
1547			// if restriction
1548			if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1549				$contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1550				// complex or simple content
1551				if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1552					$contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1553				}
1554			}
1555			// finalize complex type
1556			if($contentStr != ''){
1557				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1558			} else {
1559				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1560			}
1561			$xml .= $contentStr;
1562		}
1563		// simple types
1564		if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1565			foreach($this->simpleTypes as $typeName => $eParts){
1566				$xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n";
1567				if (isset($eParts['enumeration'])) {
1568					foreach ($eParts['enumeration'] as $e) {
1569						$xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1570					}
1571				}
1572				$xml .= " </$schemaPrefix:simpleType>";
1573			}
1574		}
1575		// elements
1576		if(isset($this->elements) && count($this->elements) > 0){
1577			foreach($this->elements as $element => $eParts){
1578				$xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1579			}
1580		}
1581		// attributes
1582		if(isset($this->attributes) && count($this->attributes) > 0){
1583			foreach($this->attributes as $attr => $aParts){
1584				$xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1585			}
1586		}
1587		// finish 'er up
1588		$el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1589		foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1590			$el .= " xmlns:$nsp=\"$ns\"\n";
1591		}
1592		$xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1593		return $xml;
1594	}
1595
1596	/**
1597	* adds debug data to the clas level debug string
1598	*
1599	* @param    string $string debug data
1600	* @access   private
1601	*/
1602	function xdebug($string){
1603		$this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1604	}
1605
1606    /**
1607    * get the PHP type of a user defined type in the schema
1608    * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1609    * returns false if no type exists, or not w/ the given namespace
1610    * else returns a string that is either a native php type, or 'struct'
1611    *
1612    * @param string $type, name of defined type
1613    * @param string $ns, namespace of type
1614    * @return mixed
1615    * @access public
1616    * @deprecated
1617    */
1618	function getPHPType($type,$ns){
1619		if(isset($this->typemap[$ns][$type])){
1620			//print "found type '$type' and ns $ns in typemap<br>";
1621			return $this->typemap[$ns][$type];
1622		} elseif(isset($this->complexTypes[$type])){
1623			//print "getting type '$type' and ns $ns from complexTypes array<br>";
1624			return $this->complexTypes[$type]['phpType'];
1625		}
1626		return false;
1627	}
1628
1629	/**
1630    * returns an associative array of information about a given type
1631    * returns false if no type exists by the given name
1632    *
1633	*	For a complexType typeDef = array(
1634	*	'restrictionBase' => '',
1635	*	'phpType' => '',
1636	*	'compositor' => '(sequence|all)',
1637	*	'elements' => array(), // refs to elements array
1638	*	'attrs' => array() // refs to attributes array
1639	*	... and so on (see addComplexType)
1640	*	)
1641	*
1642	*   For simpleType or element, the array has different keys.
1643    *
1644    * @param string
1645    * @return mixed
1646    * @access public
1647    * @see addComplexType
1648    * @see addSimpleType
1649    * @see addElement
1650    */
1651	function getTypeDef($type){
1652		//$this->debug("in getTypeDef for type $type");
1653		if(isset($this->complexTypes[$type])){
1654			$this->xdebug("in getTypeDef, found complexType $type");
1655			return $this->complexTypes[$type];
1656		} elseif(isset($this->simpleTypes[$type])){
1657			$this->xdebug("in getTypeDef, found simpleType $type");
1658			if (!isset($this->simpleTypes[$type]['phpType'])) {
1659				// get info for type to tack onto the simple type
1660				// TODO: can this ever really apply (i.e. what is a simpleType really?)
1661				$uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1662				$ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1663				$etype = $this->getTypeDef($uqType);
1664				if ($etype) {
1665					$this->xdebug("in getTypeDef, found type for simpleType $type:");
1666					$this->xdebug($this->varDump($etype));
1667					if (isset($etype['phpType'])) {
1668						$this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1669					}
1670					if (isset($etype['elements'])) {
1671						$this->simpleTypes[$type]['elements'] = $etype['elements'];
1672					}
1673				}
1674			}
1675			return $this->simpleTypes[$type];
1676		} elseif(isset($this->elements[$type])){
1677			$this->xdebug("in getTypeDef, found element $type");
1678			if (!isset($this->elements[$type]['phpType'])) {
1679				// get info for type to tack onto the element
1680				$uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1681				$ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1682				$etype = $this->getTypeDef($uqType);
1683				if ($etype) {
1684					$this->xdebug("in getTypeDef, found type for element $type:");
1685					$this->xdebug($this->varDump($etype));
1686					if (isset($etype['phpType'])) {
1687						$this->elements[$type]['phpType'] = $etype['phpType'];
1688					}
1689					if (isset($etype['elements'])) {
1690						$this->elements[$type]['elements'] = $etype['elements'];
1691					}
1692				} elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1693					$this->xdebug("in getTypeDef, element $type is an XSD type");
1694					$this->elements[$type]['phpType'] = 'scalar';
1695				}
1696			}
1697			return $this->elements[$type];
1698		} elseif(isset($this->attributes[$type])){
1699			$this->xdebug("in getTypeDef, found attribute $type");
1700			return $this->attributes[$type];
1701		} elseif (ereg('_ContainedType$', $type)) {
1702			$this->xdebug("in getTypeDef, have an untyped element $type");
1703			$typeDef['typeClass'] = 'simpleType';
1704			$typeDef['phpType'] = 'scalar';
1705			$typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1706			return $typeDef;
1707		}
1708		$this->xdebug("in getTypeDef, did not find $type");
1709		return false;
1710	}
1711
1712	/**
1713    * returns a sample serialization of a given type, or false if no type by the given name
1714    *
1715    * @param string $type, name of type
1716    * @return mixed
1717    * @access public
1718    * @deprecated
1719    */
1720    function serializeTypeDef($type){
1721    	//print "in sTD() for type $type<br>";
1722	if($typeDef = $this->getTypeDef($type)){
1723		$str .= '<'.$type;
1724	    if(is_array($typeDef['attrs'])){
1725		foreach($attrs as $attName => $data){
1726		    $str .= " $attName=\"{type = ".$data['type']."}\"";
1727		}
1728	    }
1729	    $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1730	    if(count($typeDef['elements']) > 0){
1731		$str .= ">";
1732		foreach($typeDef['elements'] as $element => $eData){
1733		    $str .= $this->serializeTypeDef($element);
1734		}
1735		$str .= "</$type>";
1736	    } elseif($typeDef['typeClass'] == 'element') {
1737		$str .= "></$type>";
1738	    } else {
1739		$str .= "/>";
1740	    }
1741			return $str;
1742	}
1743    	return false;
1744    }
1745
1746    /**
1747    * returns HTML form elements that allow a user
1748    * to enter values for creating an instance of the given type.
1749    *
1750    * @param string $name, name for type instance
1751    * @param string $type, name of type
1752    * @return string
1753    * @access public
1754    * @deprecated
1755	*/
1756	function typeToForm($name,$type){
1757		// get typedef
1758		if($typeDef = $this->getTypeDef($type)){
1759			// if struct
1760			if($typeDef['phpType'] == 'struct'){
1761				$buffer .= '<table>';
1762				foreach($typeDef['elements'] as $child => $childDef){
1763					$buffer .= "
1764					<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1765					<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1766				}
1767				$buffer .= '</table>';
1768			// if array
1769			} elseif($typeDef['phpType'] == 'array'){
1770				$buffer .= '<table>';
1771				for($i=0;$i < 3; $i++){
1772					$buffer .= "
1773					<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1774					<td><input type='text' name='parameters[".$name."][]'></td></tr>";
1775				}
1776				$buffer .= '</table>';
1777			// if scalar
1778			} else {
1779				$buffer .= "<input type='text' name='parameters[$name]'>";
1780			}
1781		} else {
1782			$buffer .= "<input type='text' name='parameters[$name]'>";
1783		}
1784		return $buffer;
1785	}
1786
1787	/**
1788	* adds a complex type to the schema
1789	*
1790	* example: array
1791	*
1792	* addType(
1793	* 	'ArrayOfstring',
1794	* 	'complexType',
1795	* 	'array',
1796	* 	'',
1797	* 	'SOAP-ENC:Array',
1798	* 	array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1799	* 	'xsd:string'
1800	* );
1801	*
1802	* example: PHP associative array ( SOAP Struct )
1803	*
1804	* addType(
1805	* 	'SOAPStruct',
1806	* 	'complexType',
1807	* 	'struct',
1808	* 	'all',
1809	* 	array('myVar'=> array('name'=>'myVar','type'=>'string')
1810	* );
1811	*
1812	* @param name
1813	* @param typeClass (complexType|simpleType|attribute)
1814	* @param phpType: currently supported are array and struct (php assoc array)
1815	* @param compositor (all|sequence|choice)
1816	* @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1817	* @param elements = array ( name = array(name=>'',type=>'') )
1818	* @param attrs = array(
1819	* 	array(
1820	*		'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1821	*		"http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1822	* 	)
1823	* )
1824	* @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1825	* @access public
1826	* @see getTypeDef
1827	*/
1828	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1829		$this->complexTypes[$name] = array(
1830	    'name'		=> $name,
1831	    'typeClass'	=> $typeClass,
1832	    'phpType'	=> $phpType,
1833		'compositor'=> $compositor,
1834	    'restrictionBase' => $restrictionBase,
1835		'elements'	=> $elements,
1836	    'attrs'		=> $attrs,
1837	    'arrayType'	=> $arrayType
1838		);
1839
1840		$this->xdebug("addComplexType $name:");
1841		$this->appendDebug($this->varDump($this->complexTypes[$name]));
1842	}
1843
1844	/**
1845	* adds a simple type to the schema
1846	*
1847	* @param string $name
1848	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1849	* @param string $typeClass (should always be simpleType)
1850	* @param string $phpType (should always be scalar)
1851	* @param array $enumeration array of values
1852	* @access public
1853	* @see xmlschema
1854	* @see getTypeDef
1855	*/
1856	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1857		$this->simpleTypes[$name] = array(
1858	    'name'			=> $name,
1859	    'typeClass'		=> $typeClass,
1860	    'phpType'		=> $phpType,
1861	    'type'			=> $restrictionBase,
1862	    'enumeration'	=> $enumeration
1863		);
1864
1865		$this->xdebug("addSimpleType $name:");
1866		$this->appendDebug($this->varDump($this->simpleTypes[$name]));
1867	}
1868
1869	/**
1870	* adds an element to the schema
1871	*
1872	* @param array $attrs attributes that must include name and type
1873	* @see xmlschema
1874	* @access public
1875	*/
1876	function addElement($attrs) {
1877		if (! $this->getPrefix($attrs['type'])) {
1878			$attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1879		}
1880		$this->elements[ $attrs['name'] ] = $attrs;
1881		$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1882
1883		$this->xdebug("addElement " . $attrs['name']);
1884		$this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1885	}
1886}
1887
1888
1889
1890?><?php
1891
1892
1893
1894/**
1895* For creating serializable abstractions of native PHP types.  This class
1896* allows element name/namespace, XSD type, and XML attributes to be
1897* associated with a value.  This is extremely useful when WSDL is not
1898* used, but is also useful when WSDL is used with polymorphic types, including
1899* xsd:anyType and user-defined types.
1900*
1901* @author   Dietrich Ayala <dietrich@ganx4.com>
1902* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
1903* @access   public
1904*/
1905class soapval extends nusoap_base {
1906	/**
1907	 * The XML element name
1908	 *
1909	 * @var string
1910	 * @access private
1911	 */
1912	var $name;
1913	/**
1914	 * The XML type name (string or false)
1915	 *
1916	 * @var mixed
1917	 * @access private
1918	 */
1919	var $type;
1920	/**
1921	 * The PHP value
1922	 *
1923	 * @var mixed
1924	 * @access private
1925	 */
1926	var $value;
1927	/**
1928	 * The XML element namespace (string or false)
1929	 *
1930	 * @var mixed
1931	 * @access private
1932	 */
1933	var $element_ns;
1934	/**
1935	 * The XML type namespace (string or false)
1936	 *
1937	 * @var mixed
1938	 * @access private
1939	 */
1940	var $type_ns;
1941	/**
1942	 * The XML element attributes (array or false)
1943	 *
1944	 * @var mixed
1945	 * @access private
1946	 */
1947	var $attributes;
1948
1949	/**
1950	* constructor
1951	*
1952	* @param    string $name optional name
1953	* @param    mixed $type optional type name
1954	* @param	mixed $value optional value
1955	* @param	mixed $element_ns optional namespace of value
1956	* @param	mixed $type_ns optional namespace of type
1957	* @param	mixed $attributes associative array of attributes to add to element serialization
1958	* @access   public
1959	*/
1960  	function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1961		parent::nusoap_base();
1962		$this->name = $name;
1963		$this->type = $type;
1964		$this->value = $value;
1965		$this->element_ns = $element_ns;
1966		$this->type_ns = $type_ns;
1967		$this->attributes = $attributes;
1968    }
1969
1970	/**
1971	* return serialized value
1972	*
1973	* @param	string $use The WSDL use value (encoded|literal)
1974	* @return	string XML data
1975	* @access   public
1976	*/
1977	function serialize($use='encoded') {
1978		return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1979    }
1980
1981	/**
1982	* decodes a soapval object into a PHP native type
1983	*
1984	* @return	mixed
1985	* @access   public
1986	*/
1987	function decode(){
1988		return $this->value;
1989	}
1990}
1991
1992
1993
1994?><?php
1995
1996
1997
1998/**
1999* transport class for sending/receiving data via HTTP and HTTPS
2000* NOTE: PHP must be compiled with the CURL extension for HTTPS support
2001*
2002* @author   Dietrich Ayala <dietrich@ganx4.com>
2003* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
2004* @access public
2005*/
2006class soap_transport_http extends nusoap_base {
2007
2008	var $url = '';
2009	var $uri = '';
2010	var $digest_uri = '';
2011	var $scheme = '';
2012	var $host = '';
2013	var $port = '';
2014	var $path = '';
2015	var $request_method = 'POST';
2016	var $protocol_version = '1.0';
2017	var $encoding = '';
2018	var $outgoing_headers = array();
2019	var $incoming_headers = array();
2020	var $incoming_cookies = array();
2021	var $outgoing_payload = '';
2022	var $incoming_payload = '';
2023	var $useSOAPAction = true;
2024	var $persistentConnection = false;
2025	var $ch = false;	// cURL handle
2026	var $username = '';
2027	var $password = '';
2028	var $authtype = '';
2029	var $digestRequest = array();
2030	var $certRequest = array();	// keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
2031								// cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2032								// sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2033								// sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2034								// passphrase: SSL key password/passphrase
2035								// verifypeer: default is 1
2036								// verifyhost: default is 1
2037
2038	/**
2039	* constructor
2040	*/
2041	function soap_transport_http($url){
2042		parent::nusoap_base();
2043		$this->setURL($url);
2044		ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2045		$this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
2046		$this->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']);
2047	}
2048
2049	function setURL($url) {
2050		$this->url = $url;
2051
2052		$u = parse_url($url);
2053		foreach($u as $k => $v){
2054			$this->debug("$k = $v");
2055			$this->$k = $v;
2056		}
2057
2058		// add any GET params to path
2059		if(isset($u['query']) && $u['query'] != ''){
2060            $this->path .= '?' . $u['query'];
2061		}
2062
2063		// set default port
2064		if(!isset($u['port'])){
2065			if($u['scheme'] == 'https'){
2066				$this->port = 443;
2067			} else {
2068				$this->port = 80;
2069			}
2070		}
2071
2072		$this->uri = $this->path;
2073		$this->digest_uri = $this->uri;
2074
2075		// build headers
2076		if (!isset($u['port'])) {
2077			$this->outgoing_headers['Host'] = $this->host;
2078		} else {
2079			$this->outgoing_headers['Host'] = $this->host.':'.$this->port;
2080		}
2081		$this->debug('set Host: ' . $this->outgoing_headers['Host']);
2082
2083		if (isset($u['user']) && $u['user'] != '') {
2084			$this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2085		}
2086	}
2087
2088	function connect($connection_timeout=0,$response_timeout=30){
2089	  	// For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2090	  	// "regular" socket.
2091	  	// TODO: disabled for now because OpenSSL must be *compiled* in (not just
2092	  	//       loaded), and until PHP5 stream_get_wrappers is not available.
2093//	  	if ($this->scheme == 'https') {
2094//		  	if (version_compare(phpversion(), '4.3.0') >= 0) {
2095//		  		if (extension_loaded('openssl')) {
2096//		  			$this->scheme = 'ssl';
2097//		  			$this->debug('Using SSL over OpenSSL');
2098//		  		}
2099//		  	}
2100//		}
2101		$this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2102	  if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2103		// use persistent connection
2104		if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2105			if (!feof($this->fp)) {
2106				$this->debug('Re-use persistent connection');
2107				return true;
2108			}
2109			fclose($this->fp);
2110			$this->debug('Closed persistent connection at EOF');
2111		}
2112
2113		// munge host if using OpenSSL
2114		if ($this->scheme == 'ssl') {
2115			$host = 'ssl://' . $this->host;
2116		} else {
2117			$host = $this->host;
2118		}
2119		$this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2120
2121		// open socket
2122		if($connection_timeout > 0){
2123			$this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2124		} else {
2125			$this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2126		}
2127
2128		// test pointer
2129		if(!$this->fp) {
2130			$msg = 'Couldn\'t open socket connection to server ' . $this->url;
2131			if ($this->errno) {
2132				$msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2133			} else {
2134				$msg .= ' prior to connect().  This is often a problem looking up the host name.';
2135			}
2136			$this->debug($msg);
2137			$this->setError($msg);
2138			return false;
2139		}
2140
2141		// set response timeout
2142		$this->debug('set response timeout to ' . $response_timeout);
2143		socket_set_timeout( $this->fp, $response_timeout);
2144
2145		$this->debug('socket connected');
2146		return true;
2147	  } else if ($this->scheme == 'https') {
2148		if (!extension_loaded('curl')) {
2149			$this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2150			return false;
2151		}
2152		$this->debug('connect using https');
2153		// init CURL
2154		$this->ch = curl_init();
2155		// set url
2156		$hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
2157		// add path
2158		$hostURL .= $this->path;
2159		curl_setopt($this->ch, CURLOPT_URL, $hostURL);
2160		// follow location headers (re-directs)
2161		curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
2162		// ask for headers in the response output
2163		curl_setopt($this->ch, CURLOPT_HEADER, 1);
2164		// ask for the response output as the return value
2165		curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
2166		// encode
2167		// We manage this ourselves through headers and encoding
2168//		if(function_exists('gzuncompress')){
2169//			curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
2170//		}
2171		// persistent connection
2172		if ($this->persistentConnection) {
2173			// The way we send data, we cannot use persistent connections, since
2174			// there will be some "junk" at the end of our request.
2175			//curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
2176			$this->persistentConnection = false;
2177			$this->outgoing_headers['Connection'] = 'close';
2178			$this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2179		}
2180		// set timeout
2181		if ($connection_timeout != 0) {
2182			curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
2183		}
2184		// TODO: cURL has added a connection timeout separate from the response timeout
2185		//if ($connection_timeout != 0) {
2186		//	curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2187		//}
2188		//if ($response_timeout != 0) {
2189		//	curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);
2190		//}
2191
2192		// recent versions of cURL turn on peer/host checking by default,
2193		// while PHP binaries are not compiled with a default location for the
2194		// CA cert bundle, so disable peer/host checking.
2195//curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2196		curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
2197		curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
2198
2199		// support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2200		if ($this->authtype == 'certificate') {
2201			if (isset($this->certRequest['cainfofile'])) {
2202				curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2203			}
2204			if (isset($this->certRequest['verifypeer'])) {
2205				curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2206			} else {
2207				curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
2208			}
2209			if (isset($this->certRequest['verifyhost'])) {
2210				curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2211			} else {
2212				curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
2213			}
2214			if (isset($this->certRequest['sslcertfile'])) {
2215				curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2216			}
2217			if (isset($this->certRequest['sslkeyfile'])) {
2218				curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2219			}
2220			if (isset($this->certRequest['passphrase'])) {
2221				curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $this->certRequest['passphrase']);
2222			}
2223		}
2224		$this->debug('cURL connection set up');
2225		return true;
2226	  } else {
2227		$this->setError('Unknown scheme ' . $this->scheme);
2228		$this->debug('Unknown scheme ' . $this->scheme);
2229		return false;
2230	  }
2231	}
2232
2233	/**
2234	* send the SOAP message via HTTP
2235	*
2236	* @param    string $data message data
2237	* @param    integer $timeout set connection timeout in seconds
2238	* @param	integer $response_timeout set response timeout in seconds
2239	* @param	array $cookies cookies to send
2240	* @return	string data
2241	* @access   public
2242	*/
2243	function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2244
2245		$this->debug('entered send() with data of length: '.strlen($data));
2246
2247		$this->tryagain = true;
2248		$tries = 0;
2249		while ($this->tryagain) {
2250			$this->tryagain = false;
2251			if ($tries++ < 2) {
2252				// make connnection
2253				if (!$this->connect($timeout, $response_timeout)){
2254					return false;
2255				}
2256
2257				// send request
2258				if (!$this->sendRequest($data, $cookies)){
2259					return false;
2260				}
2261
2262				// get response
2263				$respdata = $this->getResponse();
2264			} else {
2265				$this->setError('Too many tries to get an OK response');
2266			}
2267		}
2268		$this->debug('end of send()');
2269		return $respdata;
2270	}
2271
2272
2273	/**
2274	* send the SOAP message via HTTPS 1.0 using CURL
2275	*
2276	* @param    string $msg message data
2277	* @param    integer $timeout set connection timeout in seconds
2278	* @param	integer $response_timeout set response timeout in seconds
2279	* @param	array $cookies cookies to send
2280	* @return	string data
2281	* @access   public
2282	*/
2283	function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2284		return $this->send($data, $timeout, $response_timeout, $cookies);
2285	}
2286
2287	/**
2288	* if authenticating, set user credentials here
2289	*
2290	* @param    string $username
2291	* @param    string $password
2292	* @param	string $authtype (basic, digest, certificate)
2293	* @param	array $digestRequest (keys must be nonce, nc, realm, qop)
2294	* @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2295	* @access   public
2296	*/
2297	function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2298		$this->debug("Set credentials for authtype $authtype");
2299		// cf. RFC 2617
2300		if ($authtype == 'basic') {
2301			$this->outgoing_headers['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
2302		} elseif ($authtype == 'digest') {
2303			if (isset($digestRequest['nonce'])) {
2304				$digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2305
2306				// calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2307
2308				// A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2309				$A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2310
2311				// H(A1) = MD5(A1)
2312				$HA1 = md5($A1);
2313
2314				// A2 = Method ":" digest-uri-value
2315				$A2 = 'POST:' . $this->digest_uri;
2316
2317				// H(A2)
2318				$HA2 =  md5($A2);
2319
2320				// KD(secret, data) = H(concat(secret, ":", data))
2321				// if qop == auth:
2322				// request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2323				//                              ":" nc-value
2324				//                              ":" unq(cnonce-value)
2325				//                              ":" unq(qop-value)
2326				//                              ":" H(A2)
2327				//                            ) <">
2328				// if qop is missing,
2329				// request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2330
2331				$unhashedDigest = '';
2332				$nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2333				$cnonce = $nonce;
2334				if ($digestRequest['qop'] != '') {
2335					$unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2336				} else {
2337					$unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2338				}
2339
2340				$hashedDigest = md5($unhashedDigest);
2341
2342				$this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
2343			}
2344		} elseif ($authtype == 'certificate') {
2345			$this->certRequest = $certRequest;
2346		}
2347		$this->username = $username;
2348		$this->password = $password;
2349		$this->authtype = $authtype;
2350		$this->digestRequest = $digestRequest;
2351
2352		if (isset($this->outgoing_headers['Authorization'])) {
2353			$this->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
2354		} else {
2355			$this->debug('Authorization header not set');
2356		}
2357	}
2358
2359	/**
2360	* set the soapaction value
2361	*
2362	* @param    string $soapaction
2363	* @access   public
2364	*/
2365	function setSOAPAction($soapaction) {
2366		$this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
2367		$this->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']);
2368	}
2369
2370	/**
2371	* use http encoding
2372	*
2373	* @param    string $enc encoding style. supported values: gzip, deflate, or both
2374	* @access   public
2375	*/
2376	function setEncoding($enc='gzip, deflate') {
2377		if (function_exists('gzdeflate')) {
2378			$this->protocol_version = '1.1';
2379			$this->outgoing_headers['Accept-Encoding'] = $enc;
2380			$this->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']);
2381			if (!isset($this->outgoing_headers['Connection'])) {
2382				$this->outgoing_headers['Connection'] = 'close';
2383				$this->persistentConnection = false;
2384				$this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2385			}
2386			set_magic_quotes_runtime(0);
2387			// deprecated
2388			$this->encoding = $enc;
2389		}
2390	}
2391
2392	/**
2393	* set proxy info here
2394	*
2395	* @param    string $proxyhost
2396	* @param    string $proxyport
2397	* @param	string $proxyusername
2398	* @param	string $proxypassword
2399	* @access   public
2400	*/
2401	function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
2402		$this->uri = $this->url;
2403		$this->host = $proxyhost;
2404		$this->port = $proxyport;
2405		if ($proxyusername != '' && $proxypassword != '') {
2406			$this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
2407			$this->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']);
2408		}
2409	}
2410
2411	/**
2412	* decode a string that is encoded w/ "chunked' transfer encoding
2413 	* as defined in RFC2068 19.4.6
2414	*
2415	* @param    string $buffer
2416	* @param    string $lb
2417	* @returns	string
2418	* @access   public
2419	* @deprecated
2420	*/
2421	function decodeChunked($buffer, $lb){
2422		// length := 0
2423		$length = 0;
2424		$new = '';
2425
2426		// read chunk-size, chunk-extension (if any) and CRLF
2427		// get the position of the linebreak
2428		$chunkend = strpos($buffer, $lb);
2429		if ($chunkend == FALSE) {
2430			$this->debug('no linebreak found in decodeChunked');
2431			return $new;
2432		}
2433		$temp = substr($buffer,0,$chunkend);
2434		$chunk_size = hexdec( trim($temp) );
2435		$chunkstart = $chunkend + strlen($lb);
2436		// while (chunk-size > 0) {
2437		while ($chunk_size > 0) {
2438			$this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2439			$chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2440
2441			// Just in case we got a broken connection
2442		  	if ($chunkend == FALSE) {
2443		  	    $chunk = substr($buffer,$chunkstart);
2444				// append chunk-data to entity-body
2445		    	$new .= $chunk;
2446		  	    $length += strlen($chunk);
2447		  	    break;
2448			}
2449
2450		  	// read chunk-data and CRLF
2451		  	$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2452		  	// append chunk-data to entity-body
2453		  	$new .= $chunk;
2454		  	// length := length + chunk-size
2455		  	$length += strlen($chunk);
2456		  	// read chunk-size and CRLF
2457		  	$chunkstart = $chunkend + strlen($lb);
2458
2459		  	$chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2460			if ($chunkend == FALSE) {
2461				break; //Just in case we got a broken connection
2462			}
2463			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2464			$chunk_size = hexdec( trim($temp) );
2465			$chunkstart = $chunkend;
2466		}
2467		return $new;
2468	}
2469
2470	/*
2471	 *	Writes payload, including HTTP headers, to $this->outgoing_payload.
2472	 */
2473	function buildPayload($data, $cookie_str = '') {
2474		// add content-length header
2475		$this->outgoing_headers['Content-Length'] = strlen($data);
2476		$this->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']);
2477
2478		// start building outgoing payload:
2479		$req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2480		$this->debug("HTTP request: $req");
2481		$this->outgoing_payload = "$req\r\n";
2482
2483		// loop thru headers, serializing
2484		foreach($this->outgoing_headers as $k => $v){
2485			$hdr = $k.': '.$v;
2486			$this->debug("HTTP header: $hdr");
2487			$this->outgoing_payload .= "$hdr\r\n";
2488		}
2489
2490		// add any cookies
2491		if ($cookie_str != '') {
2492			$hdr = 'Cookie: '.$cookie_str;
2493			$this->debug("HTTP header: $hdr");
2494			$this->outgoing_payload .= "$hdr\r\n";
2495		}
2496
2497		// header/body separator
2498		$this->outgoing_payload .= "\r\n";
2499
2500		// add data
2501		$this->outgoing_payload .= $data;
2502	}
2503
2504	function sendRequest($data, $cookies = NULL) {
2505		// build cookie string
2506		$cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2507
2508		// build payload
2509		$this->buildPayload($data, $cookie_str);
2510
2511	  if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2512		// send payload
2513		if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2514			$this->setError('couldn\'t write message data to socket');
2515			$this->debug('couldn\'t write message data to socket');
2516			return false;
2517		}
2518		$this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2519		return true;
2520	  } else if ($this->scheme == 'https') {
2521		// set payload
2522		// TODO: cURL does say this should only be the verb, and in fact it
2523		// turns out that the URI and HTTP version are appended to this, which
2524		// some servers refuse to work with
2525		//curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2526		foreach($this->outgoing_headers as $k => $v){
2527			$curl_headers[] = "$k: $v";
2528		}
2529		if ($cookie_str != '') {
2530			$curl_headers[] = 'Cookie: ' . $cookie_str;
2531		}
2532		curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
2533		if ($this->request_method == "POST") {
2534	  		curl_setopt($this->ch, CURLOPT_POST, 1);
2535	  		curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
2536	  	} else {
2537	  	}
2538		$this->debug('set cURL payload');
2539		return true;
2540	  }
2541	}
2542
2543	function getResponse(){
2544		$this->incoming_payload = '';
2545
2546	  if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2547	    // loop until headers have been retrieved
2548	    $data = '';
2549	    while (!isset($lb)){
2550
2551			// We might EOF during header read.
2552			if(feof($this->fp)) {
2553				$this->incoming_payload = $data;
2554				$this->debug('found no headers before EOF after length ' . strlen($data));
2555				$this->debug("received before EOF:\n" . $data);
2556				$this->setError('server failed to send headers');
2557				return false;
2558			}
2559
2560			$tmp = fgets($this->fp, 256);
2561			$tmplen = strlen($tmp);
2562			$this->debug("read line of $tmplen bytes: " . trim($tmp));
2563
2564			if ($tmplen == 0) {
2565				$this->incoming_payload = $data;
2566				$this->debug('socket read of headers timed out after length ' . strlen($data));
2567				$this->debug("read before timeout: " . $data);
2568				$this->setError('socket read of headers timed out');
2569				return false;
2570			}
2571
2572			$data .= $tmp;
2573			$pos = strpos($data,"\r\n\r\n");
2574			if($pos > 1){
2575				$lb = "\r\n";
2576			} else {
2577				$pos = strpos($data,"\n\n");
2578				if($pos > 1){
2579					$lb = "\n";
2580				}
2581			}
2582			// remove 100 header
2583			if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2584				unset($lb);
2585				$data = '';
2586			}//
2587		}
2588		// store header data
2589		$this->incoming_payload .= $data;
2590		$this->debug('found end of headers after length ' . strlen($data));
2591		// process headers
2592		$header_data = trim(substr($data,0,$pos));
2593		$header_array = explode($lb,$header_data);
2594		$this->incoming_headers = array();
2595		$this->incoming_cookies = array();
2596		foreach($header_array as $header_line){
2597			$arr = explode(':',$header_line, 2);
2598			if(count($arr) > 1){
2599				$header_name = strtolower(trim($arr[0]));
2600				$this->incoming_headers[$header_name] = trim($arr[1]);
2601				if ($header_name == 'set-cookie') {
2602					// TODO: allow multiple cookies from parseCookie
2603					$cookie = $this->parseCookie(trim($arr[1]));
2604					if ($cookie) {
2605						$this->incoming_cookies[] = $cookie;
2606						$this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2607					} else {
2608						$this->debug('did not find cookie in ' . trim($arr[1]));
2609					}
2610    			}
2611			} else if (isset($header_name)) {
2612				// append continuation line to previous header
2613				$this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2614			}
2615		}
2616
2617		// loop until msg has been received
2618		if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2619			$content_length =  2147483647;	// ignore any content-length header
2620			$chunked = true;
2621			$this->debug("want to read chunked content");
2622		} elseif (isset($this->incoming_headers['content-length'])) {
2623			$content_length = $this->incoming_headers['content-length'];
2624			$chunked = false;
2625			$this->debug("want to read content of length $content_length");
2626		} else {
2627			$content_length =  2147483647;
2628			$chunked = false;
2629			$this->debug("want to read content to EOF");
2630		}
2631		$data = '';
2632		do {
2633			if ($chunked) {
2634				$tmp = fgets($this->fp, 256);
2635				$tmplen = strlen($tmp);
2636				$this->debug("read chunk line of $tmplen bytes");
2637				if ($tmplen == 0) {
2638					$this->incoming_payload = $data;
2639					$this->debug('socket read of chunk length timed out after length ' . strlen($data));
2640					$this->debug("read before timeout:\n" . $data);
2641					$this->setError('socket read of chunk length timed out');
2642					return false;
2643				}
2644				$content_length = hexdec(trim($tmp));
2645				$this->debug("chunk length $content_length");
2646			}
2647			$strlen = 0;
2648		    while (($strlen < $content_length) && (!feof($this->fp))) {
2649		    	$readlen = min(8192, $content_length - $strlen);
2650				$tmp = fread($this->fp, $readlen);
2651				$tmplen = strlen($tmp);
2652				$this->debug("read buffer of $tmplen bytes");
2653				if (($tmplen == 0) && (!feof($this->fp))) {
2654					$this->incoming_payload = $data;
2655					$this->debug('socket read of body timed out after length ' . strlen($data));
2656					$this->debug("read before timeout:\n" . $data);
2657					$this->setError('socket read of body timed out');
2658					return false;
2659				}
2660				$strlen += $tmplen;
2661				$data .= $tmp;
2662			}
2663			if ($chunked && ($content_length > 0)) {
2664				$tmp = fgets($this->fp, 256);
2665				$tmplen = strlen($tmp);
2666				$this->debug("read chunk terminator of $tmplen bytes");
2667				if ($tmplen == 0) {
2668					$this->incoming_payload = $data;
2669					$this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2670					$this->debug("read before timeout:\n" . $data);
2671					$this->setError('socket read of chunk terminator timed out');
2672					return false;
2673				}
2674			}
2675		} while ($chunked && ($content_length > 0) && (!feof($this->fp)));
2676		if (feof($this->fp)) {
2677			$this->debug('read to EOF');
2678		}
2679		$this->debug('read body of length ' . strlen($data));
2680		$this->incoming_payload .= $data;
2681		$this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2682
2683		// close filepointer
2684		if(
2685			(isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
2686			(! $this->persistentConnection) || feof($this->fp)){
2687			fclose($this->fp);
2688			$this->fp = false;
2689			$this->debug('closed socket');
2690		}
2691
2692		// connection was closed unexpectedly
2693		if($this->incoming_payload == ''){
2694			$this->setError('no response from server');
2695			return false;
2696		}
2697
2698		// decode transfer-encoding
2699//		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2700//			if(!$data = $this->decodeChunked($data, $lb)){
2701//				$this->setError('Decoding of chunked data failed');
2702//				return false;
2703//			}
2704			//print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2705			// set decoded payload
2706//			$this->incoming_payload = $header_data.$lb.$lb.$data;
2707//		}
2708
2709	  } else if ($this->scheme == 'https') {
2710		// send and receive
2711		$this->debug('send and receive with cURL');
2712		$this->incoming_payload = curl_exec($this->ch);
2713		$data = $this->incoming_payload;
2714
2715        $cErr = curl_error($this->ch);
2716		if ($cErr != '') {
2717        	$err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
2718        	// TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
2719			foreach(curl_getinfo($this->ch) as $k => $v){
2720				$err .= "$k: $v<br>";
2721			}
2722			$this->debug($err);
2723			$this->setError($err);
2724			curl_close($this->ch);
2725	    	return false;
2726		} else {
2727			//echo '<pre>';
2728			//var_dump(curl_getinfo($this->ch));
2729			//echo '</pre>';
2730		}
2731		// close curl
2732		$this->debug('No cURL error, closing cURL');
2733		curl_close($this->ch);
2734
2735		// remove 100 header(s)
2736		while (ereg('^HTTP/1.1 100',$data)) {
2737			if ($pos = strpos($data,"\r\n\r\n")) {
2738				$data = ltrim(substr($data,$pos));
2739			} elseif($pos = strpos($data,"\n\n") ) {
2740				$data = ltrim(substr($data,$pos));
2741			}
2742		}
2743
2744		// separate content from HTTP headers
2745		if ($pos = strpos($data,"\r\n\r\n")) {
2746			$lb = "\r\n";
2747		} elseif( $pos = strpos($data,"\n\n")) {
2748			$lb = "\n";
2749		} else {
2750			$this->debug('no proper separation of headers and document');
2751			$this->setError('no proper separation of headers and document');
2752			return false;
2753		}
2754		$header_data = trim(substr($data,0,$pos));
2755		$header_array = explode($lb,$header_data);
2756		$data = ltrim(substr($data,$pos));
2757		$this->debug('found proper separation of headers and document');
2758		$this->debug('cleaned data, stringlen: '.strlen($data));
2759		// clean headers
2760		foreach ($header_array as $header_line) {
2761			$arr = explode(':',$header_line,2);
2762			if(count($arr) > 1){
2763				$header_name = strtolower(trim($arr[0]));
2764				$this->incoming_headers[$header_name] = trim($arr[1]);
2765				if ($header_name == 'set-cookie') {
2766					// TODO: allow multiple cookies from parseCookie
2767					$cookie = $this->parseCookie(trim($arr[1]));
2768					if ($cookie) {
2769						$this->incoming_cookies[] = $cookie;
2770						$this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2771					} else {
2772						$this->debug('did not find cookie in ' . trim($arr[1]));
2773					}
2774    			}
2775			} else if (isset($header_name)) {
2776				// append continuation line to previous header
2777				$this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2778			}
2779		}
2780	  }
2781
2782		$arr = explode(' ', $header_array[0], 3);
2783		$http_version = $arr[0];
2784		$http_status = intval($arr[1]);
2785		$http_reason = count($arr) > 2 ? $arr[2] : '';
2786
2787 		// see if we need to resend the request with http digest authentication
2788 		if (isset($this->incoming_headers['location']) && $http_status == 301) {
2789 			$this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers['location']);
2790 			$this->setURL($this->incoming_headers['location']);
2791			$this->tryagain = true;
2792			return false;
2793		}
2794
2795 		// see if we need to resend the request with http digest authentication
2796 		if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
2797 			$this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
2798 			if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
2799 				$this->debug('Server wants digest authentication');
2800 				// remove "Digest " from our elements
2801 				$digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2802
2803 				// parse elements into array
2804 				$digestElements = explode(',', $digestString);
2805 				foreach ($digestElements as $val) {
2806 					$tempElement = explode('=', trim($val), 2);
2807 					$digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2808 				}
2809
2810				// should have (at least) qop, realm, nonce
2811 				if (isset($digestRequest['nonce'])) {
2812 					$this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2813 					$this->tryagain = true;
2814 					return false;
2815 				}
2816 			}
2817			$this->debug('HTTP authentication failed');
2818			$this->setError('HTTP authentication failed');
2819			return false;
2820 		}
2821
2822		if (
2823			($http_status >= 300 && $http_status <= 307) ||
2824			($http_status >= 400 && $http_status <= 417) ||
2825			($http_status >= 501 && $http_status <= 505)
2826		   ) {
2827			$this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
2828			return false;
2829		}
2830
2831		// decode content-encoding
2832		if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2833			if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2834    			// if decoding works, use it. else assume data wasn't gzencoded
2835    			if(function_exists('gzinflate')){
2836					//$timer->setMarker('starting decoding of gzip/deflated content');
2837					// IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2838					// this means there are no Zlib headers, although there should be
2839					$this->debug('The gzinflate function exists');
2840					$datalen = strlen($data);
2841					if ($this->incoming_headers['content-encoding'] == 'deflate') {
2842						if ($degzdata = @gzinflate($data)) {
2843	    					$data = $degzdata;
2844	    					$this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2845	    					if (strlen($data) < $datalen) {
2846	    						// test for the case that the payload has been compressed twice
2847		    					$this->debug('The inflated payload is smaller than the gzipped one; try again');
2848								if ($degzdata = @gzinflate($data)) {
2849			    					$data = $degzdata;
2850			    					$this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2851								}
2852	    					}
2853	    				} else {
2854	    					$this->debug('Error using gzinflate to inflate the payload');
2855	    					$this->setError('Error using gzinflate to inflate the payload');
2856	    				}
2857					} elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
2858						if ($degzdata = @gzinflate(substr($data, 10))) {	// do our best
2859							$data = $degzdata;
2860	    					$this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2861	    					if (strlen($data) < $datalen) {
2862	    						// test for the case that the payload has been compressed twice
2863		    					$this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2864								if ($degzdata = @gzinflate(substr($data, 10))) {
2865			    					$data = $degzdata;
2866			    					$this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2867								}
2868	    					}
2869	    				} else {
2870	    					$this->debug('Error using gzinflate to un-gzip the payload');
2871							$this->setError('Error using gzinflate to un-gzip the payload');
2872	    				}
2873					}
2874					//$timer->setMarker('finished decoding of gzip/deflated content');
2875					//print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2876					// set decoded payload
2877					$this->incoming_payload = $header_data.$lb.$lb.$data;
2878    			} else {
2879					$this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2880					$this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2881				}
2882			} else {
2883				$this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2884				$this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2885			}
2886		} else {
2887			$this->debug('No Content-Encoding header');
2888		}
2889
2890		if(strlen($data) == 0){
2891			$this->debug('no data after headers!');
2892			$this->setError('no data present after HTTP headers');
2893			return false;
2894		}
2895
2896		return $data;
2897	}
2898
2899	function setContentType($type, $charset = false) {
2900		$this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2901		$this->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']);
2902	}
2903
2904	function usePersistentConnection(){
2905		if (isset($this->outgoing_headers['Accept-Encoding'])) {
2906			return false;
2907		}
2908		$this->protocol_version = '1.1';
2909		$this->persistentConnection = true;
2910		$this->outgoing_headers['Connection'] = 'Keep-Alive';
2911		$this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2912		return true;
2913	}
2914
2915	/**
2916	 * parse an incoming Cookie into it's parts
2917	 *
2918	 * @param	string $cookie_str content of cookie
2919	 * @return	array with data of that cookie
2920	 * @access	private
2921	 */
2922	/*
2923	 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
2924	 */
2925	function parseCookie($cookie_str) {
2926		$cookie_str = str_replace('; ', ';', $cookie_str) . ';';
2927		$data = split(';', $cookie_str);
2928		$value_str = $data[0];
2929
2930		$cookie_param = 'domain=';
2931		$start = strpos($cookie_str, $cookie_param);
2932		if ($start > 0) {
2933			$domain = substr($cookie_str, $start + strlen($cookie_param));
2934			$domain = substr($domain, 0, strpos($domain, ';'));
2935		} else {
2936			$domain = '';
2937		}
2938
2939		$cookie_param = 'expires=';
2940		$start = strpos($cookie_str, $cookie_param);
2941		if ($start > 0) {
2942			$expires = substr($cookie_str, $start + strlen($cookie_param));
2943			$expires = substr($expires, 0, strpos($expires, ';'));
2944		} else {
2945			$expires = '';
2946		}
2947
2948		$cookie_param = 'path=';
2949		$start = strpos($cookie_str, $cookie_param);
2950		if ( $start > 0 ) {
2951			$path = substr($cookie_str, $start + strlen($cookie_param));
2952			$path = substr($path, 0, strpos($path, ';'));
2953		} else {
2954			$path = '/';
2955		}
2956
2957		$cookie_param = ';secure;';
2958		if (strpos($cookie_str, $cookie_param) !== FALSE) {
2959			$secure = true;
2960		} else {
2961			$secure = false;
2962		}
2963
2964		$sep_pos = strpos($value_str, '=');
2965
2966		if ($sep_pos) {
2967			$name = substr($value_str, 0, $sep_pos);
2968			$value = substr($value_str, $sep_pos + 1);
2969			$cookie= array(	'name' => $name,
2970			                'value' => $value,
2971							'domain' => $domain,
2972							'path' => $path,
2973							'expires' => $expires,
2974							'secure' => $secure
2975							);
2976			return $cookie;
2977		}
2978		return false;
2979	}
2980
2981	/**
2982	 * sort out cookies for the current request
2983	 *
2984	 * @param	array $cookies array with all cookies
2985	 * @param	boolean $secure is the send-content secure or not?
2986	 * @return	string for Cookie-HTTP-Header
2987	 * @access	private
2988	 */
2989	function getCookiesForRequest($cookies, $secure=false) {
2990		$cookie_str = '';
2991		if ((! is_null($cookies)) && (is_array($cookies))) {
2992			foreach ($cookies as $cookie) {
2993				if (! is_array($cookie)) {
2994					continue;
2995				}
2996	    		$this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
2997				if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
2998					if (strtotime($cookie['expires']) <= time()) {
2999						$this->debug('cookie has expired');
3000						continue;
3001					}
3002				}
3003				if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3004					$domain = preg_quote($cookie['domain']);
3005					if (! preg_match("'.*$domain$'i", $this->host)) {
3006						$this->debug('cookie has different domain');
3007						continue;
3008					}
3009				}
3010				if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3011					$path = preg_quote($cookie['path']);
3012					if (! preg_match("'^$path.*'i", $this->path)) {
3013						$this->debug('cookie is for a different path');
3014						continue;
3015					}
3016				}
3017				if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3018					$this->debug('cookie is secure, transport is not');
3019					continue;
3020				}
3021				$cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3022	    		$this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3023			}
3024		}
3025		return $cookie_str;
3026  }
3027}
3028
3029?><?php
3030
3031
3032
3033/**
3034*
3035* soap_server allows the user to create a SOAP server
3036* that is capable of receiving messages and returning responses
3037*
3038* NOTE: WSDL functionality is experimental
3039*
3040* @author   Dietrich Ayala <dietrich@ganx4.com>
3041* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
3042* @access   public
3043*/
3044class soap_server extends nusoap_base {
3045	/**
3046	 * HTTP headers of request
3047	 * @var array
3048	 * @access private
3049	 */
3050	var $headers = array();
3051	/**
3052	 * HTTP request
3053	 * @var string
3054	 * @access private
3055	 */
3056	var $request = '';
3057	/**
3058	 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3059	 * @var string
3060	 * @access public
3061	 */
3062	var $requestHeaders = '';
3063	/**
3064	 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3065	 * @var string
3066	 * @access public
3067	 */
3068	var $document = '';
3069	/**
3070	 * SOAP payload for request (text)
3071	 * @var string
3072	 * @access public
3073	 */
3074	var $requestSOAP = '';
3075	/**
3076	 * requested method namespace URI
3077	 * @var string
3078	 * @access private
3079	 */
3080	var $methodURI = '';
3081	/**
3082	 * name of method requested
3083	 * @var string
3084	 * @access private
3085	 */
3086	var $methodname = '';
3087	/**
3088	 * method parameters from request
3089	 * @var array
3090	 * @access private
3091	 */
3092	var $methodparams = array();
3093	/**
3094	 * SOAP Action from request
3095	 * @var string
3096	 * @access private
3097	 */
3098	var $SOAPAction = '';
3099	/**
3100	 * character set encoding of incoming (request) messages
3101	 * @var string
3102	 * @access public
3103	 */
3104	var $xml_encoding = '';
3105	/**
3106	 * toggles whether the parser decodes element content w/ utf8_decode()
3107	 * @var boolean
3108	 * @access public
3109	 */
3110    var $decode_utf8 = true;
3111
3112	/**
3113	 * HTTP headers of response
3114	 * @var array
3115	 * @access public
3116	 */
3117	var $outgoing_headers = array();
3118	/**
3119	 * HTTP response
3120	 * @var string
3121	 * @access private
3122	 */
3123	var $response = '';
3124	/**
3125	 * SOAP headers for response (text)
3126	 * @var string
3127	 * @access public
3128	 */
3129	var $responseHeaders = '';
3130	/**
3131	 * SOAP payload for response (text)
3132	 * @var string
3133	 * @access private
3134	 */
3135	var $responseSOAP = '';
3136	/**
3137	 * method return value to place in response
3138	 * @var mixed
3139	 * @access private
3140	 */
3141	var $methodreturn = false;
3142	/**
3143	 * whether $methodreturn is a string of literal XML
3144	 * @var boolean
3145	 * @access public
3146	 */
3147	var $methodreturnisliteralxml = false;
3148	/**
3149	 * SOAP fault for response (or false)
3150	 * @var mixed
3151	 * @access private
3152	 */
3153	var $fault = false;
3154	/**
3155	 * text indication of result (for debugging)
3156	 * @var string
3157	 * @access private
3158	 */
3159	var $result = 'successful';
3160
3161	/**
3162	 * assoc array of operations => opData; operations are added by the register()
3163	 * method or by parsing an external WSDL definition
3164	 * @var array
3165	 * @access private
3166	 */
3167	var $operations = array();
3168	/**
3169	 * wsdl instance (if one)
3170	 * @var mixed
3171	 * @access private
3172	 */
3173	var $wsdl = false;
3174	/**
3175	 * URL for WSDL (if one)
3176	 * @var mixed
3177	 * @access private
3178	 */
3179	var $externalWSDLURL = false;
3180	/**
3181	 * whether to append debug to response as XML comment
3182	 * @var boolean
3183	 * @access public
3184	 */
3185	var $debug_flag = false;
3186
3187
3188	/**
3189	* constructor
3190    * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3191	*
3192    * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3193	* @access   public
3194	*/
3195	function soap_server($wsdl=false){
3196		parent::nusoap_base();
3197		// turn on debugging?
3198		global $debug;
3199		global $HTTP_SERVER_VARS;
3200
3201		if (isset($_SERVER)) {
3202			$this->debug("_SERVER is defined:");
3203			$this->appendDebug($this->varDump($_SERVER));
3204		} elseif (isset($HTTP_SERVER_VARS)) {
3205			$this->debug("HTTP_SERVER_VARS is defined:");
3206			$this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3207		} else {
3208			$this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3209		}
3210
3211		if (isset($debug)) {
3212			$this->debug("In soap_server, set debug_flag=$debug based on global flag");
3213			$this->debug_flag = $debug;
3214		} elseif (isset($_SERVER['QUERY_STRING'])) {
3215			$qs = explode('&', $_SERVER['QUERY_STRING']);
3216			foreach ($qs as $v) {
3217				if (substr($v, 0, 6) == 'debug=') {
3218					$this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3219					$this->debug_flag = substr($v, 6);
3220				}
3221			}
3222		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3223			$qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3224			foreach ($qs as $v) {
3225				if (substr($v, 0, 6) == 'debug=') {
3226					$this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3227					$this->debug_flag = substr($v, 6);
3228				}
3229			}
3230		}
3231
3232		// wsdl
3233		if($wsdl){
3234			$this->debug("In soap_server, WSDL is specified");
3235			if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3236				$this->wsdl = $wsdl;
3237				$this->externalWSDLURL = $this->wsdl->wsdl;
3238				$this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3239			} else {
3240				$this->debug('Create wsdl from ' . $wsdl);
3241				$this->wsdl = new wsdl($wsdl);
3242				$this->externalWSDLURL = $wsdl;
3243			}
3244			$this->appendDebug($this->wsdl->getDebug());
3245			$this->wsdl->clearDebug();
3246			if($err = $this->wsdl->getError()){
3247				die('WSDL ERROR: '.$err);
3248			}
3249		}
3250	}
3251
3252	/**
3253	* processes request and returns response
3254	*
3255	* @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3256	* @access   public
3257	*/
3258	function service($data){
3259		global $HTTP_SERVER_VARS;
3260
3261		if (isset($_SERVER['QUERY_STRING'])) {
3262			$qs = $_SERVER['QUERY_STRING'];
3263		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3264			$qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3265		} else {
3266			$qs = '';
3267		}
3268		$this->debug("In service, query string=$qs");
3269
3270		if (ereg('wsdl', $qs) ){
3271			$this->debug("In service, this is a request for WSDL");
3272			if($this->externalWSDLURL){
3273              if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3274				header('Location: '.$this->externalWSDLURL);
3275              } else { // assume file
3276                header("Content-Type: text/xml\r\n");
3277                $fp = fopen($this->externalWSDLURL, 'r');
3278                fpassthru($fp);
3279              }
3280			} elseif ($this->wsdl) {
3281				header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3282				print $this->wsdl->serialize($this->debug_flag);
3283				if ($this->debug_flag) {
3284					$this->debug('wsdl:');
3285					$this->appendDebug($this->varDump($this->wsdl));
3286					print $this->getDebugAsXMLComment();
3287				}
3288			} else {
3289				header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3290				print "This service does not provide WSDL";
3291			}
3292		} elseif ($data == '' && $this->wsdl) {
3293			$this->debug("In service, there is no data, so return Web description");
3294			print $this->wsdl->webDescription();
3295		} else {
3296			$this->debug("In service, invoke the request");
3297			$this->parse_request($data);
3298			if (! $this->fault) {
3299				$this->invoke_method();
3300			}
3301			if (! $this->fault) {
3302				$this->serialize_return();
3303			}
3304			$this->send_response();
3305		}
3306	}
3307
3308	/**
3309	* parses HTTP request headers.
3310	*
3311	* The following fields are set by this function (when successful)
3312	*
3313	* headers
3314	* request
3315	* xml_encoding
3316	* SOAPAction
3317	*
3318	* @access   private
3319	*/
3320	function parse_http_headers() {
3321		global $HTTP_SERVER_VARS;
3322
3323		$this->request = '';
3324		$this->SOAPAction = '';
3325		if(function_exists('getallheaders')){
3326			$this->debug("In parse_http_headers, use getallheaders");
3327			$headers = getallheaders();
3328			foreach($headers as $k=>$v){
3329				$k = strtolower($k);
3330				$this->headers[$k] = $v;
3331				$this->request .= "$k: $v\r\n";
3332				$this->debug("$k: $v");
3333			}
3334			// get SOAPAction header
3335			if(isset($this->headers['soapaction'])){
3336				$this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3337			}
3338			// get the character encoding of the incoming request
3339			if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3340				$enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3341				if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3342					$this->xml_encoding = strtoupper($enc);
3343				} else {
3344					$this->xml_encoding = 'US-ASCII';
3345				}
3346			} else {
3347				// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3348				$this->xml_encoding = 'ISO-8859-1';
3349			}
3350		} elseif(isset($_SERVER) && is_array($_SERVER)){
3351			$this->debug("In parse_http_headers, use _SERVER");
3352			foreach ($_SERVER as $k => $v) {
3353				if (substr($k, 0, 5) == 'HTTP_') {
3354					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 	                                         $k = strtolower(substr($k, 5));
3355				} else {
3356					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 	                                         $k = strtolower($k);
3357				}
3358				if ($k == 'soapaction') {
3359					// get SOAPAction header
3360					$k = 'SOAPAction';
3361					$v = str_replace('"', '', $v);
3362					$v = str_replace('\\', '', $v);
3363					$this->SOAPAction = $v;
3364				} else if ($k == 'content-type') {
3365					// get the character encoding of the incoming request
3366					if (strpos($v, '=')) {
3367						$enc = substr(strstr($v, '='), 1);
3368						$enc = str_replace('"', '', $enc);
3369						$enc = str_replace('\\', '', $enc);
3370						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3371							$this->xml_encoding = strtoupper($enc);
3372						} else {
3373							$this->xml_encoding = 'US-ASCII';
3374						}
3375					} else {
3376						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3377						$this->xml_encoding = 'ISO-8859-1';
3378					}
3379				}
3380				$this->headers[$k] = $v;
3381				$this->request .= "$k: $v\r\n";
3382				$this->debug("$k: $v");
3383			}
3384		} elseif (is_array($HTTP_SERVER_VARS)) {
3385			$this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3386			foreach ($HTTP_SERVER_VARS as $k => $v) {
3387				if (substr($k, 0, 5) == 'HTTP_') {
3388					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 	                                         $k = strtolower(substr($k, 5));
3389				} else {
3390					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 	                                         $k = strtolower($k);
3391				}
3392				if ($k == 'soapaction') {
3393					// get SOAPAction header
3394					$k = 'SOAPAction';
3395					$v = str_replace('"', '', $v);
3396					$v = str_replace('\\', '', $v);
3397					$this->SOAPAction = $v;
3398				} else if ($k == 'content-type') {
3399					// get the character encoding of the incoming request
3400					if (strpos($v, '=')) {
3401						$enc = substr(strstr($v, '='), 1);
3402						$enc = str_replace('"', '', $enc);
3403						$enc = str_replace('\\', '', $enc);
3404						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3405							$this->xml_encoding = strtoupper($enc);
3406						} else {
3407							$this->xml_encoding = 'US-ASCII';
3408						}
3409					} else {
3410						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3411						$this->xml_encoding = 'ISO-8859-1';
3412					}
3413				}
3414				$this->headers[$k] = $v;
3415				$this->request .= "$k: $v\r\n";
3416				$this->debug("$k: $v");
3417			}
3418		} else {
3419			$this->debug("In parse_http_headers, HTTP headers not accessible");
3420			$this->setError("HTTP headers not accessible");
3421		}
3422	}
3423
3424	/**
3425	* parses a request
3426	*
3427	* The following fields are set by this function (when successful)
3428	*
3429	* headers
3430	* request
3431	* xml_encoding
3432	* SOAPAction
3433	* request
3434	* requestSOAP
3435	* methodURI
3436	* methodname
3437	* methodparams
3438	* requestHeaders
3439	* document
3440	*
3441	* This sets the fault field on error
3442	*
3443	* @param    string $data XML string
3444	* @access   private
3445	*/
3446	function parse_request($data='') {
3447		$this->debug('entering parse_request()');
3448		$this->parse_http_headers();
3449		$this->debug('got character encoding: '.$this->xml_encoding);
3450		// uncompress if necessary
3451		if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3452			$this->debug('got content encoding: ' . $this->headers['content-encoding']);
3453			if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3454		    	// if decoding works, use it. else assume data wasn't gzencoded
3455				if (function_exists('gzuncompress')) {
3456					if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3457						$data = $degzdata;
3458					} elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3459						$data = $degzdata;
3460					} else {
3461						$this->fault('Client', 'Errors occurred when trying to decode the data');
3462						return;
3463					}
3464				} else {
3465					$this->fault('Client', 'This Server does not support compressed data');
3466					return;
3467				}
3468			}
3469		}
3470		$this->request .= "\r\n".$data;
3471		$data = $this->parseRequest($this->headers, $data);
3472		$this->requestSOAP = $data;
3473		$this->debug('leaving parse_request');
3474	}
3475
3476	/**
3477	* invokes a PHP function for the requested SOAP method
3478	*
3479	* The following fields are set by this function (when successful)
3480	*
3481	* methodreturn
3482	*
3483	* Note that the PHP function that is called may also set the following
3484	* fields to affect the response sent to the client
3485	*
3486	* responseHeaders
3487	* outgoing_headers
3488	*
3489	* This sets the fault field on error
3490	*
3491	* @access   private
3492	*/
3493	function invoke_method() {
3494		$this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3495
3496		if ($this->wsdl) {
3497			if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3498				$this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3499				$this->appendDebug('opData=' . $this->varDump($this->opData));
3500			} elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3501				// Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3502				$this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3503				$this->appendDebug('opData=' . $this->varDump($this->opData));
3504				$this->methodname = $this->opData['name'];
3505			} else {
3506				$this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3507				$this->fault('Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3508				return;
3509			}
3510		} else {
3511			$this->debug('in invoke_method, no WSDL to validate method');
3512		}
3513
3514		// if a . is present in $this->methodname, we see if there is a class in scope,
3515		// which could be referred to. We will also distinguish between two deliminators,
3516		// to allow methods to be called a the class or an instance
3517		$class = '';
3518		$method = '';
3519		if (strpos($this->methodname, '..') > 0) {
3520			$delim = '..';
3521		} else if (strpos($this->methodname, '.') > 0) {
3522			$delim = '.';
3523		} else {
3524			$delim = '';
3525		}
3526
3527		if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
3528			class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
3529			// get the class and method name
3530			$class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3531			$method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
3532			$this->debug("in invoke_method, class=$class method=$method delim=$delim");
3533		}
3534
3535		// does method exist?
3536		if ($class == '') {
3537			if (!function_exists($this->methodname)) {
3538				$this->debug("in invoke_method, function '$this->methodname' not found!");
3539				$this->result = 'fault: method not found';
3540				$this->fault('Client',"method '$this->methodname' not defined in service");
3541				return;
3542			}
3543		} else {
3544			$method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
3545			if (!in_array($method_to_compare, get_class_methods($class))) {
3546				$this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3547				$this->result = 'fault: method not found';
3548				$this->fault('Client',"method '$this->methodname' not defined in service");
3549				return;
3550			}
3551		}
3552
3553		// evaluate message, getting back parameters
3554		// verify that request parameters match the method's signature
3555		if(! $this->verify_method($this->methodname,$this->methodparams)){
3556			// debug
3557			$this->debug('ERROR: request not verified against method signature');
3558			$this->result = 'fault: request failed validation against method signature';
3559			// return fault
3560			$this->fault('Client',"Operation '$this->methodname' not defined in service.");
3561			return;
3562		}
3563
3564		// if there are parameters to pass
3565		$this->debug('in invoke_method, params:');
3566		$this->appendDebug($this->varDump($this->methodparams));
3567		$this->debug("in invoke_method, calling '$this->methodname'");
3568		if (!function_exists('call_user_func_array')) {
3569			if ($class == '') {
3570				$this->debug('in invoke_method, calling function using eval()');
3571				$funcCall = "\$this->methodreturn = $this->methodname(";
3572			} else {
3573				if ($delim == '..') {
3574					$this->debug('in invoke_method, calling class method using eval()');
3575					$funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3576				} else {
3577					$this->debug('in invoke_method, calling instance method using eval()');
3578					// generate unique instance name
3579					$instname = "\$inst_".time();
3580					$funcCall = $instname." = new ".$class."(); ";
3581					$funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3582				}
3583			}
3584			if ($this->methodparams) {
3585				foreach ($this->methodparams as $param) {
3586					if (is_array($param)) {
3587						$this->fault('Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3588						return;
3589					}
3590					$funcCall .= "\"$param\",";
3591				}
3592				$funcCall = substr($funcCall, 0, -1);
3593			}
3594			$funcCall .= ');';
3595			$this->debug('in invoke_method, function call: '.$funcCall);
3596			@eval($funcCall);
3597		} else {
3598			if ($class == '') {
3599				$this->debug('in invoke_method, calling function using call_user_func_array()');
3600				$call_arg = "$this->methodname";	// straight assignment changes $this->methodname to lower case after call_user_func_array()
3601			} elseif ($delim == '..') {
3602				$this->debug('in invoke_method, calling class method using call_user_func_array()');
3603				$call_arg = array ($class, $method);
3604			} else {
3605				$this->debug('in invoke_method, calling instance method using call_user_func_array()');
3606				$instance = new $class ();
3607				$call_arg = array(&$instance, $method);
3608			}
3609			$this->methodreturn = call_user_func_array($call_arg, $this->methodparams);
3610		}
3611        $this->debug('in invoke_method, methodreturn:');
3612        $this->appendDebug($this->varDump($this->methodreturn));
3613		$this->debug("in invoke_method, called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn));
3614	}
3615
3616	/**
3617	* serializes the return value from a PHP function into a full SOAP Envelope
3618	*
3619	* The following fields are set by this function (when successful)
3620	*
3621	* responseSOAP
3622	*
3623	* This sets the fault field on error
3624	*
3625	* @access   private
3626	*/
3627	function serialize_return() {
3628		$this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
3629		// if fault
3630		if (isset($this->methodreturn) && (get_class($this->methodreturn) == 'soap_fault')) {
3631			$this->debug('got a fault object from method');
3632			$this->fault = $this->methodreturn;
3633			return;
3634		} elseif ($this->methodreturnisliteralxml) {
3635			$return_val = $this->methodreturn;
3636		// returned value(s)
3637		} else {
3638			$this->debug('got a(n) '.gettype($this->methodreturn).' from method');
3639			$this->debug('serializing return value');
3640			if($this->wsdl){
3641				// weak attempt at supporting multiple output params
3642				if(sizeof($this->opData['output']['parts']) > 1){
3643			    	$opParams = $this->methodreturn;
3644			    } else {
3645			    	// TODO: is this really necessary?
3646			    	$opParams = array($this->methodreturn);
3647			    }
3648			    $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
3649			    $this->appendDebug($this->wsdl->getDebug());
3650			    $this->wsdl->clearDebug();
3651				if($errstr = $this->wsdl->getError()){
3652					$this->debug('got wsdl error: '.$errstr);
3653					$this->fault('Server', 'unable to serialize result');
3654					return;
3655				}
3656			} else {
3657				if (isset($this->methodreturn)) {
3658					$return_val = $this->serialize_val($this->methodreturn, 'return');
3659				} else {
3660					$return_val = '';
3661					$this->debug('in absence of WSDL, assume void return for backward compatibility');
3662				}
3663			}
3664		}
3665		$this->debug('return value:');
3666		$this->appendDebug($this->varDump($return_val));
3667
3668		$this->debug('serializing response');
3669		if ($this->wsdl) {
3670			$this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
3671			if ($this->opData['style'] == 'rpc') {
3672				$this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
3673				if ($this->opData['output']['use'] == 'literal') {
3674					$payload = '<'.$this->methodname.'Response xmlns="'.$this->methodURI.'">'.$return_val.'</'.$this->methodname."Response>";
3675				} else {
3676					$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3677				}
3678			} else {
3679				$this->debug('style is not rpc for serialization: assume document');
3680				$payload = $return_val;
3681			}
3682		} else {
3683			$this->debug('do not have WSDL for serialization: assume rpc/encoded');
3684			$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3685		}
3686		$this->result = 'successful';
3687		if($this->wsdl){
3688			//if($this->debug_flag){
3689            	$this->appendDebug($this->wsdl->getDebug());
3690            //	}
3691			if (isset($opData['output']['encodingStyle'])) {
3692				$encodingStyle = $opData['output']['encodingStyle'];
3693			} else {
3694				$encodingStyle = '';
3695			}
3696			// Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
3697			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$encodingStyle);
3698		} else {
3699			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
3700		}
3701		$this->debug("Leaving serialize_return");
3702	}
3703
3704	/**
3705	* sends an HTTP response
3706	*
3707	* The following fields are set by this function (when successful)
3708	*
3709	* outgoing_headers
3710	* response
3711	*
3712	* @access   private
3713	*/
3714	function send_response() {
3715		$this->debug('Enter send_response');
3716		if ($this->fault) {
3717			$payload = $this->fault->serialize();
3718			$this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
3719			$this->outgoing_headers[] = "Status: 500 Internal Server Error";
3720		} else {
3721			$payload = $this->responseSOAP;
3722			// Some combinations of PHP+Web server allow the Status
3723			// to come through as a header.  Since OK is the default
3724			// just do nothing.
3725			// $this->outgoing_headers[] = "HTTP/1.0 200 OK";
3726			// $this->outgoing_headers[] = "Status: 200 OK";
3727		}
3728        // add debug data if in debug mode
3729		if(isset($this->debug_flag) && $this->debug_flag){
3730        	$payload .= $this->getDebugAsXMLComment();
3731        }
3732		$this->outgoing_headers[] = "Server: $this->title Server v$this->version";
3733		ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
3734		$this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
3735		// Let the Web server decide about this
3736		//$this->outgoing_headers[] = "Connection: Close\r\n";
3737		$payload = $this->getHTTPBody($payload);
3738		$type = $this->getHTTPContentType();
3739		$charset = $this->getHTTPContentTypeCharset();
3740		$this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
3741		//begin code to compress payload - by John
3742		// NOTE: there is no way to know whether the Web server will also compress
3743		// this data.
3744		if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
3745			if (strstr($this->headers['accept-encoding'], 'gzip')) {
3746				if (function_exists('gzencode')) {
3747					if (isset($this->debug_flag) && $this->debug_flag) {
3748						$payload .= "<!-- Content being gzipped -->";
3749					}
3750					$this->outgoing_headers[] = "Content-Encoding: gzip";
3751					$payload = gzencode($payload);
3752				} else {
3753					if (isset($this->debug_flag) && $this->debug_flag) {
3754						$payload .= "<!-- Content will not be gzipped: no gzencode -->";
3755					}
3756				}
3757			} elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
3758				// Note: MSIE requires gzdeflate output (no Zlib header and checksum),
3759				// instead of gzcompress output,
3760				// which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
3761				if (function_exists('gzdeflate')) {
3762					if (isset($this->debug_flag) && $this->debug_flag) {
3763						$payload .= "<!-- Content being deflated -->";
3764					}
3765					$this->outgoing_headers[] = "Content-Encoding: deflate";
3766					$payload = gzdeflate($payload);
3767				} else {
3768					if (isset($this->debug_flag) && $this->debug_flag) {
3769						$payload .= "<!-- Content will not be deflated: no gzcompress -->";
3770					}
3771				}
3772			}
3773		}
3774		//end code
3775		$this->outgoing_headers[] = "Content-Length: ".strlen($payload);
3776		reset($this->outgoing_headers);
3777		foreach($this->outgoing_headers as $hdr){
3778			header($hdr, false);
3779		}
3780		print $payload;
3781		$this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
3782	}
3783
3784	/**
3785	* takes the value that was created by parsing the request
3786	* and compares to the method's signature, if available.
3787	*
3788	* @param	string	$operation	The operation to be invoked
3789	* @param	array	$request	The array of parameter values
3790	* @return	boolean	Whether the operation was found
3791	* @access   private
3792	*/
3793	function verify_method($operation,$request){
3794		if(isset($this->wsdl) && is_object($this->wsdl)){
3795			if($this->wsdl->getOperationData($operation)){
3796				return true;
3797			}
3798	    } elseif(isset($this->operations[$operation])){
3799			return true;
3800		}
3801		return false;
3802	}
3803
3804	/**
3805	* processes SOAP message received from client
3806	*
3807	* @param	array	$headers	The HTTP headers
3808	* @param	string	$data		unprocessed request data from client
3809	* @return	mixed	value of the message, decoded into a PHP type
3810	* @access   private
3811	*/
3812    function parseRequest($headers, $data) {
3813		$this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
3814		if (!strstr($headers['content-type'], 'text/xml')) {
3815			$this->setError('Request not of type text/xml');
3816			return false;
3817		}
3818		if (strpos($headers['content-type'], '=')) {
3819			$enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
3820			$this->debug('Got response encoding: ' . $enc);
3821			if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3822				$this->xml_encoding = strtoupper($enc);
3823			} else {
3824				$this->xml_encoding = 'US-ASCII';
3825			}
3826		} else {
3827			// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3828			$this->xml_encoding = 'ISO-8859-1';
3829		}
3830		$this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
3831		// parse response, get soap parser obj
3832		$parser = new soap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
3833		// parser debug
3834		$this->debug("parser debug: \n".$parser->getDebug());
3835		// if fault occurred during message parsing
3836		if($err = $parser->getError()){
3837			$this->result = 'fault: error in msg parsing: '.$err;
3838			$this->fault('Client',"error in msg parsing:\n".$err);
3839		// else successfully parsed request into soapval object
3840		} else {
3841			// get/set methodname
3842			$this->methodURI = $parser->root_struct_namespace;
3843			$this->methodname = $parser->root_struct_name;
3844			$this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
3845			$this->debug('calling parser->get_response()');
3846			$this->methodparams = $parser->get_response();
3847			// get SOAP headers
3848			$this->requestHeaders = $parser->getHeaders();
3849            // add document for doclit support
3850            $this->document = $parser->document;
3851		}
3852	 }
3853
3854	/**
3855	* gets the HTTP body for the current response.
3856	*
3857	* @param string $soapmsg The SOAP payload
3858	* @return string The HTTP body, which includes the SOAP payload
3859	* @access private
3860	*/
3861	function getHTTPBody($soapmsg) {
3862		return $soapmsg;
3863	}
3864
3865	/**
3866	* gets the HTTP content type for the current response.
3867	*
3868	* Note: getHTTPBody must be called before this.
3869	*
3870	* @return string the HTTP content type for the current response.
3871	* @access private
3872	*/
3873	function getHTTPContentType() {
3874		return 'text/xml';
3875	}
3876
3877	/**
3878	* gets the HTTP content type charset for the current response.
3879	* returns false for non-text content types.
3880	*
3881	* Note: getHTTPBody must be called before this.
3882	*
3883	* @return string the HTTP content type charset for the current response.
3884	* @access private
3885	*/
3886	function getHTTPContentTypeCharset() {
3887		return $this->soap_defencoding;
3888	}
3889
3890	/**
3891	* add a method to the dispatch map (this has been replaced by the register method)
3892	*
3893	* @param    string $methodname
3894	* @param    string $in array of input values
3895	* @param    string $out array of output values
3896	* @access   public
3897	* @deprecated
3898	*/
3899	function add_to_map($methodname,$in,$out){
3900			$this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
3901	}
3902
3903	/**
3904	* register a service function with the server
3905	*
3906	* @param    string $name the name of the PHP function, class.method or class..method
3907	* @param    array $in assoc array of input values: key = param name, value = param type
3908	* @param    array $out assoc array of output values: key = param name, value = param type
3909	* @param	mixed $namespace the element namespace for the method or false
3910	* @param	mixed $soapaction the soapaction for the method or false
3911	* @param	mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
3912	* @param	mixed $use optional (encoded|literal) or false
3913	* @param	string $documentation optional Description to include in WSDL
3914	* @param	string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
3915	* @access   public
3916	*/
3917	function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
3918		global $HTTP_SERVER_VARS;
3919
3920		if($this->externalWSDLURL){
3921			die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
3922		}
3923		if (! $name) {
3924			die('You must specify a name when you register an operation');
3925		}
3926		if (!is_array($in)) {
3927			die('You must provide an array for operation inputs');
3928		}
3929		if (!is_array($out)) {
3930			die('You must provide an array for operation outputs');
3931		}
3932		if(false == $namespace) {
3933		}
3934		if(false == $soapaction) {
3935			if (isset($_SERVER)) {
3936				$SERVER_NAME = $_SERVER['SERVER_NAME'];
3937				$SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
3938			} elseif (isset($HTTP_SERVER_VARS)) {
3939				$SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
3940				$SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
3941			} else {
3942				$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
3943			}
3944			$soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
3945		}
3946		if(false == $style) {
3947			$style = "rpc";
3948		}
3949		if(false == $use) {
3950			$use = "encoded";
3951		}
3952		if ($use == 'encoded' && $encodingStyle = '') {
3953			$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3954		}
3955
3956		$this->operations[$name] = array(
3957	    'name' => $name,
3958	    'in' => $in,
3959	    'out' => $out,
3960	    'namespace' => $namespace,
3961	    'soapaction' => $soapaction,
3962	    'style' => $style);
3963        if($this->wsdl){
3964        	$this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
3965	    }
3966		return true;
3967	}
3968
3969	/**
3970	* Specify a fault to be returned to the client.
3971	* This also acts as a flag to the server that a fault has occured.
3972	*
3973	* @param	string $faultcode
3974	* @param	string $faultstring
3975	* @param	string $faultactor
3976	* @param	string $faultdetail
3977	* @access   public
3978	*/
3979	function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
3980		if ($faultdetail == '' && $this->debug_flag) {
3981			$faultdetail = $this->getDebug();
3982		}
3983		$this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
3984		$this->fault->soap_defencoding = $this->soap_defencoding;
3985	}
3986
3987    /**
3988    * Sets up wsdl object.
3989    * Acts as a flag to enable internal WSDL generation
3990    *
3991    * @param string $serviceName, name of the service
3992    * @param mixed $namespace optional 'tns' service namespace or false
3993    * @param mixed $endpoint optional URL of service endpoint or false
3994    * @param string $style optional (rpc|document) WSDL style (also specified by operation)
3995    * @param string $transport optional SOAP transport
3996    * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
3997    */
3998    function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
3999    {
4000    	global $HTTP_SERVER_VARS;
4001
4002		if (isset($_SERVER)) {
4003			$SERVER_NAME = $_SERVER['SERVER_NAME'];
4004			$SERVER_PORT = $_SERVER['SERVER_PORT'];
4005			$SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4006			$HTTPS = $_SERVER['HTTPS'];
4007		} elseif (isset($HTTP_SERVER_VARS)) {
4008			$SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4009			$SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4010			$SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4011			$HTTPS = $HTTP_SERVER_VARS['HTTPS'];
4012		} else {
4013			$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4014		}
4015		if ($SERVER_PORT == 80) {
4016			$SERVER_PORT = '';
4017		} else {
4018			$SERVER_PORT = ':' . $SERVER_PORT;
4019		}
4020        if(false == $namespace) {
4021            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4022        }
4023
4024        if(false == $endpoint) {
4025        	if ($HTTPS == '1' || $HTTPS == 'on') {
4026        		$SCHEME = 'https';
4027        	} else {
4028        		$SCHEME = 'http';
4029        	}
4030            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4031        }
4032
4033        if(false == $schemaTargetNamespace) {
4034            $schemaTargetNamespace = $namespace;
4035        }
4036
4037		$this->wsdl = new wsdl;
4038		$this->wsdl->serviceName = $serviceName;
4039        $this->wsdl->endpoint = $endpoint;
4040		$this->wsdl->namespaces['tns'] = $namespace;
4041		$this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4042		$this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4043		if ($schemaTargetNamespace != $namespace) {
4044			$this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4045		}
4046        $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
4047        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4048        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4049        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4050        $this->wsdl->bindings[$serviceName.'Binding'] = array(
4051        	'name'=>$serviceName.'Binding',
4052            'style'=>$style,
4053            'transport'=>$transport,
4054            'portType'=>$serviceName.'PortType');
4055        $this->wsdl->ports[$serviceName.'Port'] = array(
4056        	'binding'=>$serviceName.'Binding',
4057            'location'=>$endpoint,
4058            'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4059    }
4060}
4061
4062
4063
4064?><?php
4065
4066
4067
4068/**
4069* parses a WSDL file, allows access to it's data, other utility methods
4070*
4071* @author   Dietrich Ayala <dietrich@ganx4.com>
4072* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
4073* @access public
4074*/
4075class wsdl extends nusoap_base {
4076	// URL or filename of the root of this WSDL
4077    var $wsdl;
4078    // define internal arrays of bindings, ports, operations, messages, etc.
4079    var $schemas = array();
4080    var $currentSchema;
4081    var $message = array();
4082    var $complexTypes = array();
4083    var $messages = array();
4084    var $currentMessage;
4085    var $currentOperation;
4086    var $portTypes = array();
4087    var $currentPortType;
4088    var $bindings = array();
4089    var $currentBinding;
4090    var $ports = array();
4091    var $currentPort;
4092    var $opData = array();
4093    var $status = '';
4094    var $documentation = false;
4095    var $endpoint = '';
4096    // array of wsdl docs to import
4097    var $import = array();
4098    // parser vars
4099    var $parser;
4100    var $position = 0;
4101    var $depth = 0;
4102    var $depth_array = array();
4103	// for getting wsdl
4104	var $proxyhost = '';
4105    var $proxyport = '';
4106	var $proxyusername = '';
4107	var $proxypassword = '';
4108	var $timeout = 0;
4109	var $response_timeout = 30;
4110
4111    /**
4112     * constructor
4113     *
4114     * @param string $wsdl WSDL document URL
4115	 * @param string $proxyhost
4116	 * @param string $proxyport
4117	 * @param string $proxyusername
4118	 * @param string $proxypassword
4119	 * @param integer $timeout set the connection timeout
4120	 * @param integer $response_timeout set the response timeout
4121     * @access public
4122     */
4123    function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
4124		parent::nusoap_base();
4125        $this->wsdl = $wsdl;
4126        $this->proxyhost = $proxyhost;
4127        $this->proxyport = $proxyport;
4128		$this->proxyusername = $proxyusername;
4129		$this->proxypassword = $proxypassword;
4130		$this->timeout = $timeout;
4131		$this->response_timeout = $response_timeout;
4132
4133        // parse wsdl file
4134        if ($wsdl != "") {
4135            $this->debug('initial wsdl URL: ' . $wsdl);
4136            $this->parseWSDL($wsdl);
4137        }
4138        // imports
4139        // TODO: handle imports more properly, grabbing them in-line and nesting them
4140        	$imported_urls = array();
4141        	$imported = 1;
4142        	while ($imported > 0) {
4143        		$imported = 0;
4144        		// Schema imports
4145        		foreach ($this->schemas as $ns => $list) {
4146        			foreach ($list as $xs) {
4147						$wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
4148			            foreach ($xs->imports as $ns2 => $list2) {
4149			                for ($ii = 0; $ii < count($list2); $ii++) {
4150			                	if (! $list2[$ii]['loaded']) {
4151			                		$this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4152			                		$url = $list2[$ii]['location'];
4153									if ($url != '') {
4154										$urlparts = parse_url($url);
4155										if (!isset($urlparts['host'])) {
4156											$url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4157													substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4158										}
4159										if (! in_array($url, $imported_urls)) {
4160						                	$this->parseWSDL($url);
4161					                		$imported++;
4162					                		$imported_urls[] = $url;
4163					                	}
4164									} else {
4165										$this->debug("Unexpected scenario: empty URL for unloaded import");
4166									}
4167								}
4168							}
4169			            }
4170        			}
4171        		}
4172        		// WSDL imports
4173				$wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
4174	            foreach ($this->import as $ns => $list) {
4175	                for ($ii = 0; $ii < count($list); $ii++) {
4176	                	if (! $list[$ii]['loaded']) {
4177	                		$this->import[$ns][$ii]['loaded'] = true;
4178	                		$url = $list[$ii]['location'];
4179							if ($url != '') {
4180								$urlparts = parse_url($url);
4181								if (!isset($urlparts['host'])) {
4182									$url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4183											substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4184								}
4185								if (! in_array($url, $imported_urls)) {
4186				                	$this->parseWSDL($url);
4187			                		$imported++;
4188			                		$imported_urls[] = $url;
4189			                	}
4190							} else {
4191								$this->debug("Unexpected scenario: empty URL for unloaded import");
4192							}
4193						}
4194					}
4195	            }
4196			}
4197        // add new data to operation data
4198        foreach($this->bindings as $binding => $bindingData) {
4199            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4200                foreach($bindingData['operations'] as $operation => $data) {
4201                    $this->debug('post-parse data gathering for ' . $operation);
4202                    $this->bindings[$binding]['operations'][$operation]['input'] =
4203						isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4204						array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4205						$this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4206                    $this->bindings[$binding]['operations'][$operation]['output'] =
4207						isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4208						array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4209						$this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4210                    if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4211						$this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4212					}
4213					if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4214                   		$this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4215                    }
4216					if (isset($bindingData['style'])) {
4217                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4218                    }
4219                    $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4220                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4221                    $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4222                }
4223            }
4224        }
4225    }
4226
4227    /**
4228     * parses the wsdl document
4229     *
4230     * @param string $wsdl path or URL
4231     * @access private
4232     */
4233    function parseWSDL($wsdl = '')
4234    {
4235        if ($wsdl == '') {
4236            $this->debug('no wsdl passed to parseWSDL()!!');
4237            $this->setError('no wsdl passed to parseWSDL()!!');
4238            return false;
4239        }
4240
4241        // parse $wsdl for url format
4242        $wsdl_props = parse_url($wsdl);
4243
4244        if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4245            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4246        	// get wsdl
4247	        $tr = new soap_transport_http($wsdl);
4248			$tr->request_method = 'GET';
4249			$tr->useSOAPAction = false;
4250			if($this->proxyhost && $this->proxyport){
4251				$tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4252			}
4253			$tr->setEncoding('gzip, deflate');
4254			$wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4255			//$this->debug("WSDL request\n" . $tr->outgoing_payload);
4256			//$this->debug("WSDL response\n" . $tr->incoming_payload);
4257			$this->appendDebug($tr->getDebug());
4258			// catch errors
4259			if($err = $tr->getError() ){
4260				$errstr = 'HTTP ERROR: '.$err;
4261				$this->debug($errstr);
4262	            $this->setError($errstr);
4263				unset($tr);
4264	            return false;
4265			}
4266			unset($tr);
4267			$this->debug("got WSDL URL");
4268        } else {
4269            // $wsdl is not http(s), so treat it as a file URL or plain file path
4270        	if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4271        		$path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4272        	} else {
4273        		$path = $wsdl;
4274        	}
4275            $this->debug('getting WSDL file ' . $path);
4276            if ($fp = @fopen($path, 'r')) {
4277                $wsdl_string = '';
4278                while ($data = fread($fp, 32768)) {
4279                    $wsdl_string .= $data;
4280                }
4281                fclose($fp);
4282            } else {
4283            	$errstr = "Bad path to WSDL file $path";
4284            	$this->debug($errstr);
4285                $this->setError($errstr);
4286                return false;
4287            }
4288        }
4289        $this->debug('Parse WSDL');
4290        // end new code added
4291        // Create an XML parser.
4292        $this->parser = xml_parser_create();
4293        // Set the options for parsing the XML data.
4294        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4295        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4296        // Set the object for the parser.
4297        xml_set_object($this->parser, $this);
4298        // Set the element handlers for the parser.
4299        xml_set_element_handler($this->parser, 'start_element', 'end_element');
4300        xml_set_character_data_handler($this->parser, 'character_data');
4301        // Parse the XML file.
4302        if (!xml_parse($this->parser, $wsdl_string, true)) {
4303            // Display an error message.
4304            $errstr = sprintf(
4305				'XML error parsing WSDL from %s on line %d: %s',
4306				$wsdl,
4307                xml_get_current_line_number($this->parser),
4308                xml_error_string(xml_get_error_code($this->parser))
4309                );
4310            $this->debug($errstr);
4311			$this->debug("XML payload:\n" . $wsdl_string);
4312            $this->setError($errstr);
4313            return false;
4314        }
4315		// free the parser
4316        xml_parser_free($this->parser);
4317        $this->debug('Parsing WSDL done');
4318		// catch wsdl parse errors
4319		if($this->getError()){
4320			return false;
4321		}
4322        return true;
4323    }
4324
4325    /**
4326     * start-element handler
4327     *
4328     * @param string $parser XML parser object
4329     * @param string $name element name
4330     * @param string $attrs associative array of attributes
4331     * @access private
4332     */
4333    function start_element($parser, $name, $attrs)
4334    {
4335        if ($this->status == 'schema') {
4336            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4337            $this->appendDebug($this->currentSchema->getDebug());
4338            $this->currentSchema->clearDebug();
4339        } elseif (ereg('schema$', $name)) {
4340        	$this->debug('Parsing WSDL schema');
4341            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4342            $this->status = 'schema';
4343            $this->currentSchema = new xmlschema('', '', $this->namespaces);
4344            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4345            $this->appendDebug($this->currentSchema->getDebug());
4346            $this->currentSchema->clearDebug();
4347        } else {
4348            // position in the total number of elements, starting from 0
4349            $pos = $this->position++;
4350            $depth = $this->depth++;
4351            // set self as current value for this depth
4352            $this->depth_array[$depth] = $pos;
4353            $this->message[$pos] = array('cdata' => '');
4354            // process attributes
4355            if (count($attrs) > 0) {
4356				// register namespace declarations
4357                foreach($attrs as $k => $v) {
4358                    if (ereg("^xmlns", $k)) {
4359                        if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4360                            $this->namespaces[$ns_prefix] = $v;
4361                        } else {
4362                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4363                        }
4364                        if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4365                            $this->XMLSchemaVersion = $v;
4366                            $this->namespaces['xsi'] = $v . '-instance';
4367                        }
4368                    }
4369                }
4370                // expand each attribute prefix to its namespace
4371                foreach($attrs as $k => $v) {
4372                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4373                    if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4374                        $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4375                    }
4376                    $eAttrs[$k] = $v;
4377                }
4378                $attrs = $eAttrs;
4379            } else {
4380                $attrs = array();
4381            }
4382            // get element prefix, namespace and name
4383            if (ereg(':', $name)) {
4384                // get ns prefix
4385                $prefix = substr($name, 0, strpos($name, ':'));
4386                // get ns
4387                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4388                // get unqualified name
4389                $name = substr(strstr($name, ':'), 1);
4390            }
4391			// process attributes, expanding any prefixes to namespaces
4392            // find status, register data
4393            switch ($this->status) {
4394                case 'message':
4395                    if ($name == 'part') {
4396			            if (isset($attrs['type'])) {
4397		                    $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4398		                    $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4399            			}
4400			            if (isset($attrs['element'])) {
4401		                    $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4402			                $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
4403			            }
4404        			}
4405        			break;
4406			    case 'portType':
4407			        switch ($name) {
4408			            case 'operation':
4409			                $this->currentPortOperation = $attrs['name'];
4410			                $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4411			                if (isset($attrs['parameterOrder'])) {
4412			                	$this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4413			        		}
4414			        		break;
4415					    case 'documentation':
4416					        $this->documentation = true;
4417					        break;
4418					    // merge input/output data
4419					    default:
4420					        $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4421					        $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4422					        break;
4423					}
4424			    	break;
4425				case 'binding':
4426				    switch ($name) {
4427				        case 'binding':
4428				            // get ns prefix
4429				            if (isset($attrs['style'])) {
4430				            $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4431					    	}
4432					    	$this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4433					    	break;
4434						case 'header':
4435						    $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4436						    break;
4437						case 'operation':
4438						    if (isset($attrs['soapAction'])) {
4439						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4440						    }
4441						    if (isset($attrs['style'])) {
4442						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4443						    }
4444						    if (isset($attrs['name'])) {
4445						        $this->currentOperation = $attrs['name'];
4446						        $this->debug("current binding operation: $this->currentOperation");
4447						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4448						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4449						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4450						    }
4451						    break;
4452						case 'input':
4453						    $this->opStatus = 'input';
4454						    break;
4455						case 'output':
4456						    $this->opStatus = 'output';
4457						    break;
4458						case 'body':
4459						    if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
4460						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
4461						    } else {
4462						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
4463						    }
4464						    break;
4465					}
4466					break;
4467				case 'service':
4468					switch ($name) {
4469					    case 'port':
4470					        $this->currentPort = $attrs['name'];
4471					        $this->debug('current port: ' . $this->currentPort);
4472					        $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
4473
4474					        break;
4475					    case 'address':
4476					        $this->ports[$this->currentPort]['location'] = $attrs['location'];
4477					        $this->ports[$this->currentPort]['bindingType'] = $namespace;
4478					        $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
4479					        $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
4480					        break;
4481					}
4482					break;
4483			}
4484		// set status
4485		switch ($name) {
4486			case 'import':
4487			    if (isset($attrs['location'])) {
4488                    $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
4489                    $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
4490				} else {
4491                    $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
4492					if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
4493						$this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
4494					}
4495                    $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
4496				}
4497				break;
4498			//wait for schema
4499			//case 'types':
4500			//	$this->status = 'schema';
4501			//	break;
4502			case 'message':
4503				$this->status = 'message';
4504				$this->messages[$attrs['name']] = array();
4505				$this->currentMessage = $attrs['name'];
4506				break;
4507			case 'portType':
4508				$this->status = 'portType';
4509				$this->portTypes[$attrs['name']] = array();
4510				$this->currentPortType = $attrs['name'];
4511				break;
4512			case "binding":
4513				if (isset($attrs['name'])) {
4514				// get binding name
4515					if (strpos($attrs['name'], ':')) {
4516			    		$this->currentBinding = $this->getLocalPart($attrs['name']);
4517					} else {
4518			    		$this->currentBinding = $attrs['name'];
4519					}
4520					$this->status = 'binding';
4521					$this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
4522					$this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
4523				}
4524				break;
4525			case 'service':
4526				$this->serviceName = $attrs['name'];
4527				$this->status = 'service';
4528				$this->debug('current service: ' . $this->serviceName);
4529				break;
4530			case 'definitions':
4531				foreach ($attrs as $name => $value) {
4532					$this->wsdl_info[$name] = $value;
4533				}
4534				break;
4535			}
4536		}
4537	}
4538
4539	/**
4540	* end-element handler
4541	*
4542	* @param string $parser XML parser object
4543	* @param string $name element name
4544	* @access private
4545	*/
4546	function end_element($parser, $name){
4547		// unset schema status
4548		if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
4549			$this->status = "";
4550            $this->appendDebug($this->currentSchema->getDebug());
4551            $this->currentSchema->clearDebug();
4552			$this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
4553        	$this->debug('Parsing WSDL schema done');
4554		}
4555		if ($this->status == 'schema') {
4556			$this->currentSchema->schemaEndElement($parser, $name);
4557		} else {
4558			// bring depth down a notch
4559			$this->depth--;
4560		}
4561		// end documentation
4562		if ($this->documentation) {
4563			//TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
4564			//$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
4565			$this->documentation = false;
4566		}
4567	}
4568
4569	/**
4570	 * element content handler
4571	 *
4572	 * @param string $parser XML parser object
4573	 * @param string $data element content
4574	 * @access private
4575	 */
4576	function character_data($parser, $data)
4577	{
4578		$pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
4579		if (isset($this->message[$pos]['cdata'])) {
4580			$this->message[$pos]['cdata'] .= $data;
4581		}
4582		if ($this->documentation) {
4583			$this->documentation .= $data;
4584		}
4585	}
4586
4587	function getBindingData($binding)
4588	{
4589		if (is_array($this->bindings[$binding])) {
4590			return $this->bindings[$binding];
4591		}
4592	}
4593
4594	/**
4595	 * returns an assoc array of operation names => operation data
4596	 *
4597	 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
4598	 * @return array
4599	 * @access public
4600	 */
4601	function getOperations($bindingType = 'soap')
4602	{
4603		$ops = array();
4604		if ($bindingType == 'soap') {
4605			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4606		}
4607		// loop thru ports
4608		foreach($this->ports as $port => $portData) {
4609			// binding type of port matches parameter
4610			if ($portData['bindingType'] == $bindingType) {
4611				//$this->debug("getOperations for port $port");
4612				//$this->debug("port data: " . $this->varDump($portData));
4613				//$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
4614				// merge bindings
4615				if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
4616					$ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
4617				}
4618			}
4619		}
4620		return $ops;
4621	}
4622
4623	/**
4624	 * returns an associative array of data necessary for calling an operation
4625	 *
4626	 * @param string $operation , name of operation
4627	 * @param string $bindingType , type of binding eg: soap
4628	 * @return array
4629	 * @access public
4630	 */
4631	function getOperationData($operation, $bindingType = 'soap')
4632	{
4633		if ($bindingType == 'soap') {
4634			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4635		}
4636		// loop thru ports
4637		foreach($this->ports as $port => $portData) {
4638			// binding type of port matches parameter
4639			if ($portData['bindingType'] == $bindingType) {
4640				// get binding
4641				//foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4642				foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
4643					// note that we could/should also check the namespace here
4644					if ($operation == $bOperation) {
4645						$opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
4646					    return $opData;
4647					}
4648				}
4649			}
4650		}
4651	}
4652
4653	/**
4654	 * returns an associative array of data necessary for calling an operation
4655	 *
4656	 * @param string $soapAction soapAction for operation
4657	 * @param string $bindingType type of binding eg: soap
4658	 * @return array
4659	 * @access public
4660	 */
4661	function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
4662		if ($bindingType == 'soap') {
4663			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4664		}
4665		// loop thru ports
4666		foreach($this->ports as $port => $portData) {
4667			// binding type of port matches parameter
4668			if ($portData['bindingType'] == $bindingType) {
4669				// loop through operations for the binding
4670				foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4671					if ($opData['soapAction'] == $soapAction) {
4672					    return $opData;
4673					}
4674				}
4675			}
4676		}
4677	}
4678
4679	/**
4680    * returns an array of information about a given type
4681    * returns false if no type exists by the given name
4682    *
4683	*	 typeDef = array(
4684	*	 'elements' => array(), // refs to elements array
4685	*	'restrictionBase' => '',
4686	*	'phpType' => '',
4687	*	'order' => '(sequence|all)',
4688	*	'attrs' => array() // refs to attributes array
4689	*	)
4690    *
4691    * @param $type string the type
4692    * @param $ns string namespace (not prefix) of the type
4693    * @return mixed
4694    * @access public
4695    * @see xmlschema
4696    */
4697	function getTypeDef($type, $ns) {
4698		$this->debug("in getTypeDef: type=$type, ns=$ns");
4699		if ((! $ns) && isset($this->namespaces['tns'])) {
4700			$ns = $this->namespaces['tns'];
4701			$this->debug("in getTypeDef: type namespace forced to $ns");
4702		}
4703		if (isset($this->schemas[$ns])) {
4704			$this->debug("in getTypeDef: have schema for namespace $ns");
4705			for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
4706				$xs = &$this->schemas[$ns][$i];
4707				$t = $xs->getTypeDef($type);
4708				$this->appendDebug($xs->getDebug());
4709				$xs->clearDebug();
4710				if ($t) {
4711					if (!isset($t['phpType'])) {
4712						// get info for type to tack onto the element
4713						$uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
4714						$ns = substr($t['type'], 0, strrpos($t['type'], ':'));
4715						$etype = $this->getTypeDef($uqType, $ns);
4716						if ($etype) {
4717							$this->debug("found type for [element] $type:");
4718							$this->debug($this->varDump($etype));
4719							if (isset($etype['phpType'])) {
4720								$t['phpType'] = $etype['phpType'];
4721							}
4722							if (isset($etype['elements'])) {
4723								$t['elements'] = $etype['elements'];
4724							}
4725							if (isset($etype['attrs'])) {
4726								$t['attrs'] = $etype['attrs'];
4727							}
4728						}
4729					}
4730					return $t;
4731				}
4732			}
4733		} else {
4734			$this->debug("in getTypeDef: do not have schema for namespace $ns");
4735		}
4736		return false;
4737	}
4738
4739    /**
4740    * prints html description of services
4741    *
4742    * @access private
4743    */
4744    function webDescription(){
4745    	global $HTTP_SERVER_VARS;
4746
4747		if (isset($_SERVER)) {
4748			$PHP_SELF = $_SERVER['PHP_SELF'];
4749		} elseif (isset($HTTP_SERVER_VARS)) {
4750			$PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
4751		} else {
4752			$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4753		}
4754
4755		$b = '
4756		<html><head><title>NuSOAP: '.$this->serviceName.'</title>
4757		<style type="text/css">
4758		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
4759		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
4760		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
4761		    ul      { margin-top: 10px; margin-left: 20px; }
4762		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
4763		    .content{
4764			margin-left: 0px; padding-bottom: 2em; }
4765		    .nav {
4766			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
4767			margin-top: 10px; margin-left: 0px; color: #000000;
4768			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
4769		    .title {
4770			font-family: arial; font-size: 26px; color: #ffffff;
4771			background-color: #999999; width: 105%; margin-left: 0px;
4772			padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
4773		    .hidden {
4774			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
4775			font-family: arial; overflow: hidden; width: 600;
4776			padding: 20px; font-size: 10px; background-color: #999999;
4777			layer-background-color:#FFFFFF; }
4778		    a,a:active  { color: charcoal; font-weight: bold; }
4779		    a:visited   { color: #666666; font-weight: bold; }
4780		    a:hover     { color: cc3300; font-weight: bold; }
4781		</style>
4782		<script language="JavaScript" type="text/javascript">
4783		<!--
4784		// POP-UP CAPTIONS...
4785		function lib_bwcheck(){ //Browsercheck (needed)
4786		    this.ver=navigator.appVersion
4787		    this.agent=navigator.userAgent
4788		    this.dom=document.getElementById?1:0
4789		    this.opera5=this.agent.indexOf("Opera 5")>-1
4790		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
4791		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
4792		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
4793		    this.ie=this.ie4||this.ie5||this.ie6
4794		    this.mac=this.agent.indexOf("Mac")>-1
4795		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
4796		    this.ns4=(document.layers && !this.dom)?1:0;
4797		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
4798		    return this
4799		}
4800		var bw = new lib_bwcheck()
4801		//Makes crossbrowser object.
4802		function makeObj(obj){
4803		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
4804		    if(!this.evnt) return false
4805		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
4806		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
4807		    this.writeIt=b_writeIt;
4808		    return this
4809		}
4810		// A unit of measure that will be added when setting the position of a layer.
4811		//var px = bw.ns4||window.opera?"":"px";
4812		function b_writeIt(text){
4813		    if (bw.ns4){this.wref.write(text);this.wref.close()}
4814		    else this.wref.innerHTML = text
4815		}
4816		//Shows the messages
4817		var oDesc;
4818		function popup(divid){
4819		    if(oDesc = new makeObj(divid)){
4820			oDesc.css.visibility = "visible"
4821		    }
4822		}
4823		function popout(){ // Hides message
4824		    if(oDesc) oDesc.css.visibility = "hidden"
4825		}
4826		//-->
4827		</script>
4828		</head>
4829		<body>
4830		<div class=content>
4831			<br><br>
4832			<div class=title>'.$this->serviceName.'</div>
4833			<div class=nav>
4834				<p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
4835				Click on an operation name to view it&apos;s details.</p>
4836				<ul>';
4837				foreach($this->getOperations() as $op => $data){
4838				    $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
4839				    // create hidden div
4840				    $b .= "<div id='$op' class='hidden'>
4841				    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
4842				    foreach($data as $donnie => $marie){ // loop through opdata
4843						if($donnie == 'input' || $donnie == 'output'){ // show input/output data
4844						    $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
4845						    foreach($marie as $captain => $tenille){ // loop through data
4846								if($captain == 'parts'){ // loop thru parts
4847								    $b .= "&nbsp;&nbsp;$captain:<br>";
4848					                //if(is_array($tenille)){
4849								    	foreach($tenille as $joanie => $chachi){
4850											$b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
4851								    	}
4852					        		//}
4853								} else {
4854								    $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
4855								}
4856						    }
4857						} else {
4858						    $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
4859						}
4860				    }
4861					$b .= '</div>';
4862				}
4863				$b .= '
4864				<ul>
4865			</div>
4866		</div></body></html>';
4867		return $b;
4868    }
4869
4870	/**
4871	* serialize the parsed wsdl
4872	*
4873	* @param mixed $debug whether to put debug=1 in endpoint URL
4874	* @return string serialization of WSDL
4875	* @access public
4876	*/
4877	function serialize($debug = 0)
4878	{
4879		$xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
4880		$xml .= "\n<definitions";
4881		foreach($this->namespaces as $k => $v) {
4882			$xml .= " xmlns:$k=\"$v\"";
4883		}
4884		// 10.9.02 - add poulter fix for wsdl and tns declarations
4885		if (isset($this->namespaces['wsdl'])) {
4886			$xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
4887		}
4888		if (isset($this->namespaces['tns'])) {
4889			$xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
4890		}
4891		$xml .= '>';
4892		// imports
4893		if (sizeof($this->import) > 0) {
4894			foreach($this->import as $ns => $list) {
4895				foreach ($list as $ii) {
4896					if ($ii['location'] != '') {
4897						$xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
4898					} else {
4899						$xml .= '<import namespace="' . $ns . '" />';
4900					}
4901				}
4902			}
4903		}
4904		// types
4905		if (count($this->schemas)>=1) {
4906			$xml .= "\n<types>";
4907			foreach ($this->schemas as $ns => $list) {
4908				foreach ($list as $xs) {
4909					$xml .= $xs->serializeSchema();
4910				}
4911			}
4912			$xml .= '</types>';
4913		}
4914		// messages
4915		if (count($this->messages) >= 1) {
4916			foreach($this->messages as $msgName => $msgParts) {
4917				$xml .= "\n<message name=\"" . $msgName . '">';
4918				if(is_array($msgParts)){
4919					foreach($msgParts as $partName => $partType) {
4920						// print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
4921						if (strpos($partType, ':')) {
4922						    $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
4923						} elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
4924						    // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
4925						    $typePrefix = 'xsd';
4926						} else {
4927						    foreach($this->typemap as $ns => $types) {
4928						        if (isset($types[$partType])) {
4929						            $typePrefix = $this->getPrefixFromNamespace($ns);
4930						        }
4931						    }
4932						    if (!isset($typePrefix)) {
4933						        die("$partType has no namespace!");
4934						    }
4935						}
4936						$ns = $this->getNamespaceFromPrefix($typePrefix);
4937						$typeDef = $this->getTypeDef($this->getLocalPart($partType), $ns);
4938						if ($typeDef['typeClass'] == 'element') {
4939							$elementortype = 'element';
4940						} else {
4941							$elementortype = 'type';
4942						}
4943						$xml .= '<part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
4944					}
4945				}
4946				$xml .= '</message>';
4947			}
4948		}
4949		// bindings & porttypes
4950		if (count($this->bindings) >= 1) {
4951			$binding_xml = '';
4952			$portType_xml = '';
4953			foreach($this->bindings as $bindingName => $attrs) {
4954				$binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
4955				$binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
4956				$portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
4957				foreach($attrs['operations'] as $opName => $opParts) {
4958					$binding_xml .= '<operation name="' . $opName . '">';
4959					$binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
4960					if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
4961						$enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
4962					} else {
4963						$enc_style = '';
4964					}
4965					$binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
4966					if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
4967						$enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
4968					} else {
4969						$enc_style = '';
4970					}
4971					$binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
4972					$binding_xml .= '</operation>';
4973					$portType_xml .= '<operation name="' . $opParts['name'] . '"';
4974					if (isset($opParts['parameterOrder'])) {
4975					    $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
4976					}
4977					$portType_xml .= '>';
4978					if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
4979						$portType_xml .= '<documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
4980					}
4981					$portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
4982					$portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
4983					$portType_xml .= '</operation>';
4984				}
4985				$portType_xml .= '</portType>';
4986				$binding_xml .= '</binding>';
4987			}
4988			$xml .= $portType_xml . $binding_xml;
4989		}
4990		// services
4991		$xml .= "\n<service name=\"" . $this->serviceName . '">';
4992		if (count($this->ports) >= 1) {
4993			foreach($this->ports as $pName => $attrs) {
4994				$xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
4995				$xml .= '<soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
4996				$xml .= '</port>';
4997			}
4998		}
4999		$xml .= '</service>';
5000		return $xml . "\n</definitions>";
5001	}
5002
5003	/**
5004	 * serialize PHP values according to a WSDL message definition
5005	 *
5006	 * TODO
5007	 * - multi-ref serialization
5008	 * - validate PHP values against type definitions, return errors if invalid
5009	 *
5010	 * @param string $operation operation name
5011	 * @param string $direction (input|output)
5012	 * @param mixed $parameters parameter value(s)
5013	 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5014	 * @access public
5015	 */
5016	function serializeRPCParameters($operation, $direction, $parameters)
5017	{
5018		$this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5019		$this->appendDebug('parameters=' . $this->varDump($parameters));
5020
5021		if ($direction != 'input' && $direction != 'output') {
5022			$this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5023			$this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5024			return false;
5025		}
5026		if (!$opData = $this->getOperationData($operation)) {
5027			$this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5028			$this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5029			return false;
5030		}
5031		$this->debug('opData:');
5032		$this->appendDebug($this->varDump($opData));
5033
5034		// Get encoding style for output and set to current
5035		$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5036		if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5037			$encodingStyle = $opData['output']['encodingStyle'];
5038			$enc_style = $encodingStyle;
5039		}
5040
5041		// set input params
5042		$xml = '';
5043		if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5044
5045			$use = $opData[$direction]['use'];
5046			$this->debug('have ' . count($opData[$direction]['parts']) . ' part(s) to serialize');
5047			if (is_array($parameters)) {
5048				$parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5049				$this->debug('have ' . count($parameters) . ' parameter(s) provided as ' . $parametersArrayType . ' to serialize');
5050				foreach($opData[$direction]['parts'] as $name => $type) {
5051					$this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5052					// Track encoding style
5053					if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5054						$encodingStyle = $opData[$direction]['encodingStyle'];
5055						$enc_style = $encodingStyle;
5056					} else {
5057						$enc_style = false;
5058					}
5059					// NOTE: add error handling here
5060					// if serializeType returns false, then catch global error and fault
5061					if ($parametersArrayType == 'arraySimple') {
5062						$p = array_shift($parameters);
5063						$this->debug('calling serializeType w/indexed param');
5064						$xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5065					} elseif (isset($parameters[$name])) {
5066						$this->debug('calling serializeType w/named param');
5067						$xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5068					} else {
5069						// TODO: only send nillable
5070						$this->debug('calling serializeType w/null param');
5071						$xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5072					}
5073				}
5074			} else {
5075				$this->debug('no parameters passed.');
5076			}
5077		}
5078		$this->debug("serializeRPCParameters returning: $xml");
5079		return $xml;
5080	}
5081
5082	/**
5083	 * serialize a PHP value according to a WSDL message definition
5084	 *
5085	 * TODO
5086	 * - multi-ref serialization
5087	 * - validate PHP values against type definitions, return errors if invalid
5088	 *
5089	 * @param string $ type name
5090	 * @param mixed $ param value
5091	 * @return mixed new param or false if initial value didn't validate
5092	 * @access public
5093	 * @deprecated
5094	 */
5095	function serializeParameters($operation, $direction, $parameters)
5096	{
5097		$this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5098		$this->appendDebug('parameters=' . $this->varDump($parameters));
5099
5100		if ($direction != 'input' && $direction != 'output') {
5101			$this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5102			$this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5103			return false;
5104		}
5105		if (!$opData = $this->getOperationData($operation)) {
5106			$this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5107			$this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5108			return false;
5109		}
5110		$this->debug('opData:');
5111		$this->appendDebug($this->varDump($opData));
5112
5113		// Get encoding style for output and set to current
5114		$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5115		if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5116			$encodingStyle = $opData['output']['encodingStyle'];
5117			$enc_style = $encodingStyle;
5118		}
5119
5120		// set input params
5121		$xml = '';
5122		if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5123
5124			$use = $opData[$direction]['use'];
5125			$this->debug("use=$use");
5126			$this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5127			if (is_array($parameters)) {
5128				$parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5129				$this->debug('have ' . $parametersArrayType . ' parameters');
5130				foreach($opData[$direction]['parts'] as $name => $type) {
5131					$this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5132					// Track encoding style
5133					if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5134						$encodingStyle = $opData[$direction]['encodingStyle'];
5135						$enc_style = $encodingStyle;
5136					} else {
5137						$enc_style = false;
5138					}
5139					// NOTE: add error handling here
5140					// if serializeType returns false, then catch global error and fault
5141					if ($parametersArrayType == 'arraySimple') {
5142						$p = array_shift($parameters);
5143						$this->debug('calling serializeType w/indexed param');
5144						$xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5145					} elseif (isset($parameters[$name])) {
5146						$this->debug('calling serializeType w/named param');
5147						$xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5148					} else {
5149						// TODO: only send nillable
5150						$this->debug('calling serializeType w/null param');
5151						$xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5152					}
5153				}
5154			} else {
5155				$this->debug('no parameters passed.');
5156			}
5157		}
5158		$this->debug("serializeParameters returning: $xml");
5159		return $xml;
5160	}
5161
5162	/**
5163	 * serializes a PHP value according a given type definition
5164	 *
5165	 * @param string $name name of value (part or element)
5166	 * @param string $type XML schema type of value (type or element)
5167	 * @param mixed $value a native PHP value (parameter value)
5168	 * @param string $use use for part (encoded|literal)
5169	 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5170	 * @param boolean $unqualified a kludge for what should be XML namespace form handling
5171	 * @return string value serialized as an XML string
5172	 * @access private
5173	 */
5174	function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5175	{
5176		$this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5177		$this->appendDebug("value=" . $this->varDump($value));
5178		if($use == 'encoded' && $encodingStyle) {
5179			$encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5180		}
5181
5182		// if a soapval has been supplied, let its type override the WSDL
5183    	if (is_object($value) && get_class($value) == 'soapval') {
5184    		if ($value->type_ns) {
5185    			$type = $value->type_ns . ':' . $value->type;
5186		    	$forceType = true;
5187		    	$this->debug("in serializeType: soapval overrides type to $type");
5188    		} elseif ($value->type) {
5189	    		$type = $value->type;
5190		    	$forceType = true;
5191		    	$this->debug("in serializeType: soapval overrides type to $type");
5192	    	} else {
5193	    		$forceType = false;
5194		    	$this->debug("in serializeType: soapval does not override type");
5195	    	}
5196	    	$attrs = $value->attributes;
5197	    	$value = $value->value;
5198	    	$this->debug("in serializeType: soapval overrides value to $value");
5199	    	if ($attrs) {
5200	    		if (!is_array($value)) {
5201	    			$value['!'] = $value;
5202	    		}
5203	    		foreach ($attrs as $n => $v) {
5204	    			$value['!' . $n] = $v;
5205	    		}
5206		    	$this->debug("in serializeType: soapval provides attributes");
5207		    }
5208        } else {
5209        	$forceType = false;
5210        }
5211
5212		$xml = '';
5213		if (strpos($type, ':')) {
5214			$uqType = substr($type, strrpos($type, ':') + 1);
5215			$ns = substr($type, 0, strrpos($type, ':'));
5216			$this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5217			if ($this->getNamespaceFromPrefix($ns)) {
5218				$ns = $this->getNamespaceFromPrefix($ns);
5219				$this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5220			}
5221
5222			if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5223				$this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5224				if ($unqualified  && $use == 'literal') {
5225					$elementNS = " xmlns=\"\"";
5226				} else {
5227					$elementNS = '';
5228				}
5229				if (is_null($value)) {
5230					if ($use == 'literal') {
5231						// TODO: depends on minOccurs
5232						$xml = "<$name$elementNS/>";
5233					} else {
5234						// TODO: depends on nillable, which should be checked before calling this method
5235						$xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5236					}
5237					$this->debug("in serializeType: returning: $xml");
5238					return $xml;
5239				}
5240		    	if ($uqType == 'boolean') {
5241		    		if ((is_string($value) && $value == 'false') || (! $value)) {
5242						$value = 'false';
5243					} else {
5244						$value = 'true';
5245					}
5246				}
5247				if ($uqType == 'string' && gettype($value) == 'string') {
5248					$value = $this->expandEntities($value);
5249				}
5250				if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5251					$value = sprintf("%.0lf", $value);
5252				}
5253				// it's a scalar
5254				// TODO: what about null/nil values?
5255				// check type isn't a custom type extending xmlschema namespace
5256				if (!$this->getTypeDef($uqType, $ns)) {
5257					if ($use == 'literal') {
5258						if ($forceType) {
5259							$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5260						} else {
5261							$xml = "<$name$elementNS>$value</$name>";
5262						}
5263					} else {
5264						$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5265					}
5266					$this->debug("in serializeType: returning: $xml");
5267					return $xml;
5268				}
5269				$this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5270			} else if ($ns == 'http://xml.apache.org/xml-soap') {
5271				$this->debug('in serializeType: appears to be Apache SOAP type');
5272				if ($uqType == 'Map') {
5273					$tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5274					if (! $tt_prefix) {
5275						$this->debug('in serializeType: Add namespace for Apache SOAP type');
5276						$tt_prefix = 'ns' . rand(1000, 9999);
5277						$this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5278						// force this to be added to usedNamespaces
5279						$tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5280					}
5281					$contents = '';
5282					foreach($value as $k => $v) {
5283						$this->debug("serializing map element: key $k, value $v");
5284						$contents .= '<item>';
5285						$contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5286						$contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5287						$contents .= '</item>';
5288					}
5289					if ($use == 'literal') {
5290						if ($forceType) {
5291							$xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5292						} else {
5293							$xml = "<$name>$contents</$name>";
5294						}
5295					} else {
5296						$xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
5297					}
5298					$this->debug("in serializeType: returning: $xml");
5299					return $xml;
5300				}
5301				$this->debug('in serializeType: Apache SOAP type, but only support Map');
5302			}
5303		} else {
5304			// TODO: should the type be compared to types in XSD, and the namespace
5305			// set to XSD if the type matches?
5306			$this->debug("in serializeType: No namespace for type $type");
5307			$ns = '';
5308			$uqType = $type;
5309		}
5310		if(!$typeDef = $this->getTypeDef($uqType, $ns)){
5311			$this->setError("$type ($uqType) is not a supported type.");
5312			$this->debug("in serializeType: $type ($uqType) is not a supported type.");
5313			return false;
5314		} else {
5315			$this->debug("in serializeType: found typeDef");
5316			$this->appendDebug('typeDef=' . $this->varDump($typeDef));
5317		}
5318		$phpType = $typeDef['phpType'];
5319		$this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
5320		// if php type == struct, map value to the <all> element names
5321		if ($phpType == 'struct') {
5322			if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
5323				$elementName = $uqType;
5324				if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5325					$elementNS = " xmlns=\"$ns\"";
5326				} else {
5327					$elementNS = " xmlns=\"\"";
5328				}
5329			} else {
5330				$elementName = $name;
5331				if ($unqualified) {
5332					$elementNS = " xmlns=\"\"";
5333				} else {
5334					$elementNS = '';
5335				}
5336			}
5337			if (is_null($value)) {
5338				if ($use == 'literal') {
5339					// TODO: depends on minOccurs
5340					$xml = "<$elementName$elementNS/>";
5341				} else {
5342					$xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5343				}
5344				$this->debug("in serializeType: returning: $xml");
5345				return $xml;
5346			}
5347			if (is_object($value)) {
5348				$value = get_object_vars($value);
5349			}
5350			if (is_array($value)) {
5351				$elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5352				if ($use == 'literal') {
5353					if ($forceType) {
5354						$xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
5355					} else {
5356						$xml = "<$elementName$elementNS$elementAttrs>";
5357					}
5358				} else {
5359					$xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
5360				}
5361
5362				$xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5363				$xml .= "</$elementName>";
5364			} else {
5365				$this->debug("in serializeType: phpType is struct, but value is not an array");
5366				$this->setError("phpType is struct, but value is not an array: see debug output for details");
5367				$xml = '';
5368			}
5369		} elseif ($phpType == 'array') {
5370			if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5371				$elementNS = " xmlns=\"$ns\"";
5372			} else {
5373				if ($unqualified) {
5374					$elementNS = " xmlns=\"\"";
5375				} else {
5376					$elementNS = '';
5377				}
5378			}
5379			if (is_null($value)) {
5380				if ($use == 'literal') {
5381					// TODO: depends on minOccurs
5382					$xml = "<$name$elementNS/>";
5383				} else {
5384					$xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
5385						$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5386						":Array\" " .
5387						$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5388						':arrayType="' .
5389						$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
5390						':' .
5391						$this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
5392				}
5393				$this->debug("in serializeType: returning: $xml");
5394				return $xml;
5395			}
5396			if (isset($typeDef['multidimensional'])) {
5397				$nv = array();
5398				foreach($value as $v) {
5399					$cols = ',' . sizeof($v);
5400					$nv = array_merge($nv, $v);
5401				}
5402				$value = $nv;
5403			} else {
5404				$cols = '';
5405			}
5406			if (is_array($value) && sizeof($value) >= 1) {
5407				$rows = sizeof($value);
5408				$contents = '';
5409				foreach($value as $k => $v) {
5410					$this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
5411					//if (strpos($typeDef['arrayType'], ':') ) {
5412					if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
5413					    $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
5414					} else {
5415					    $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
5416					}
5417				}
5418			} else {
5419				$rows = 0;
5420				$contents = null;
5421			}
5422			// TODO: for now, an empty value will be serialized as a zero element
5423			// array.  Revisit this when coding the handling of null/nil values.
5424			if ($use == 'literal') {
5425				$xml = "<$name$elementNS>"
5426					.$contents
5427					."</$name>";
5428			} else {
5429				$xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
5430					$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
5431					.':arrayType="'
5432					.$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
5433					.":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
5434					.$contents
5435					."</$name>";
5436			}
5437		} elseif ($phpType == 'scalar') {
5438			if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5439				$elementNS = " xmlns=\"$ns\"";
5440			} else {
5441				if ($unqualified) {
5442					$elementNS = " xmlns=\"\"";
5443				} else {
5444					$elementNS = '';
5445				}
5446			}
5447			if ($use == 'literal') {
5448				if ($forceType) {
5449					$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5450				} else {
5451					$xml = "<$name$elementNS>$value</$name>";
5452				}
5453			} else {
5454				$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5455			}
5456		}
5457		$this->debug("in serializeType: returning: $xml");
5458		return $xml;
5459	}
5460
5461	/**
5462	 * serializes the attributes for a complexType
5463	 *
5464	 * @param array $typeDef our internal representation of an XML schema type (or element)
5465	 * @param mixed $value a native PHP value (parameter value)
5466	 * @param string $ns the namespace of the type
5467	 * @param string $uqType the local part of the type
5468	 * @return string value serialized as an XML string
5469	 * @access private
5470	 */
5471	function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
5472		$xml = '';
5473		if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
5474			$this->debug("serialize attributes for XML Schema type $ns:$uqType");
5475			if (is_array($value)) {
5476				$xvalue = $value;
5477			} elseif (is_object($value)) {
5478				$xvalue = get_object_vars($value);
5479			} else {
5480				$this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5481				$xvalue = array();
5482			}
5483			foreach ($typeDef['attrs'] as $aName => $attrs) {
5484				if (isset($xvalue['!' . $aName])) {
5485					$xname = '!' . $aName;
5486					$this->debug("value provided for attribute $aName with key $xname");
5487				} elseif (isset($xvalue[$aName])) {
5488					$xname = $aName;
5489					$this->debug("value provided for attribute $aName with key $xname");
5490				} elseif (isset($attrs['default'])) {
5491					$xname = '!' . $aName;
5492					$xvalue[$xname] = $attrs['default'];
5493					$this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
5494				} else {
5495					$xname = '';
5496					$this->debug("no value provided for attribute $aName");
5497				}
5498				if ($xname) {
5499					$xml .=  " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
5500				}
5501			}
5502		} else {
5503			$this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
5504		}
5505		if (isset($typeDef['extensionBase'])) {
5506			$ns = $this->getPrefix($typeDef['extensionBase']);
5507			$uqType = $this->getLocalPart($typeDef['extensionBase']);
5508			if ($this->getNamespaceFromPrefix($ns)) {
5509				$ns = $this->getNamespaceFromPrefix($ns);
5510			}
5511			if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5512				$this->debug("serialize attributes for extension base $ns:$uqType");
5513				$xml .= $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5514			} else {
5515				$this->debug("extension base $ns:$uqType is not a supported type");
5516			}
5517		}
5518		return $xml;
5519	}
5520
5521	/**
5522	 * serializes the elements for a complexType
5523	 *
5524	 * @param array $typeDef our internal representation of an XML schema type (or element)
5525	 * @param mixed $value a native PHP value (parameter value)
5526	 * @param string $ns the namespace of the type
5527	 * @param string $uqType the local part of the type
5528	 * @param string $use use for part (encoded|literal)
5529	 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5530	 * @return string value serialized as an XML string
5531	 * @access private
5532	 */
5533	function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
5534		$xml = '';
5535		if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5536			$this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
5537			if (is_array($value)) {
5538				$xvalue = $value;
5539			} elseif (is_object($value)) {
5540				$xvalue = get_object_vars($value);
5541			} else {
5542				$this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5543				$xvalue = array();
5544			}
5545			// toggle whether all elements are present - ideally should validate against schema
5546			if (count($typeDef['elements']) != count($xvalue)){
5547				$optionals = true;
5548			}
5549			foreach ($typeDef['elements'] as $eName => $attrs) {
5550				if (!isset($xvalue[$eName])) {
5551					if (isset($attrs['default'])) {
5552						$xvalue[$eName] = $attrs['default'];
5553						$this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
5554					}
5555				}
5556				// if user took advantage of a minOccurs=0, then only serialize named parameters
5557				if (isset($optionals)
5558				    && (!isset($xvalue[$eName]))
5559					&& ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
5560					){
5561					if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
5562						$this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
5563					}
5564					// do nothing
5565					$this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
5566				} else {
5567					// get value
5568					if (isset($xvalue[$eName])) {
5569					    $v = $xvalue[$eName];
5570					} else {
5571					    $v = null;
5572					}
5573					if (isset($attrs['form'])) {
5574						$unqualified = ($attrs['form'] == 'unqualified');
5575					} else {
5576						$unqualified = false;
5577					}
5578					if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
5579						$vv = $v;
5580						foreach ($vv as $k => $v) {
5581							if (isset($attrs['type']) || isset($attrs['ref'])) {
5582								// serialize schema-defined type
5583							    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5584							} else {
5585								// serialize generic type (can this ever really happen?)
5586							    $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5587							    $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5588							}
5589						}
5590					} else {
5591						if (isset($attrs['type']) || isset($attrs['ref'])) {
5592							// serialize schema-defined type
5593						    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5594						} else {
5595							// serialize generic type (can this ever really happen?)
5596						    $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5597						    $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5598						}
5599					}
5600				}
5601			}
5602		} else {
5603			$this->debug("no elements to serialize for XML Schema type $ns:$uqType");
5604		}
5605		if (isset($typeDef['extensionBase'])) {
5606			$ns = $this->getPrefix($typeDef['extensionBase']);
5607			$uqType = $this->getLocalPart($typeDef['extensionBase']);
5608			if ($this->getNamespaceFromPrefix($ns)) {
5609				$ns = $this->getNamespaceFromPrefix($ns);
5610			}
5611			if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5612				$this->debug("serialize elements for extension base $ns:$uqType");
5613				$xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5614			} else {
5615				$this->debug("extension base $ns:$uqType is not a supported type");
5616			}
5617		}
5618		return $xml;
5619	}
5620
5621	/**
5622	* adds an XML Schema complex type to the WSDL types
5623	*
5624	* @param string	name
5625	* @param string typeClass (complexType|simpleType|attribute)
5626	* @param string phpType: currently supported are array and struct (php assoc array)
5627	* @param string compositor (all|sequence|choice)
5628	* @param string restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5629	* @param array elements = array ( name => array(name=>'',type=>'') )
5630	* @param array attrs = 	array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
5631	* @param string arrayType: namespace:name (xsd:string)
5632	* @see xmlschema
5633	* @access public
5634	*/
5635	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
5636		if (count($elements) > 0) {
5637	    	foreach($elements as $n => $e){
5638	            // expand each element
5639	            foreach ($e as $k => $v) {
5640		            $k = strpos($k,':') ? $this->expandQname($k) : $k;
5641		            $v = strpos($v,':') ? $this->expandQname($v) : $v;
5642		            $ee[$k] = $v;
5643		    	}
5644	    		$eElements[$n] = $ee;
5645	    	}
5646	    	$elements = $eElements;
5647		}
5648
5649		if (count($attrs) > 0) {
5650	    	foreach($attrs as $n => $a){
5651	            // expand each attribute
5652	            foreach ($a as $k => $v) {
5653		            $k = strpos($k,':') ? $this->expandQname($k) : $k;
5654		            $v = strpos($v,':') ? $this->expandQname($v) : $v;
5655		            $aa[$k] = $v;
5656		    	}
5657	    		$eAttrs[$n] = $aa;
5658	    	}
5659	    	$attrs = $eAttrs;
5660		}
5661
5662		$restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
5663		$arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
5664
5665		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5666		$this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
5667	}
5668
5669	/**
5670	* adds an XML Schema simple type to the WSDL types
5671	*
5672	* @param string $name
5673	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5674	* @param string $typeClass (should always be simpleType)
5675	* @param string $phpType (should always be scalar)
5676	* @param array $enumeration array of values
5677	* @see xmlschema
5678	* @access public
5679	*/
5680	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
5681		$restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
5682
5683		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5684		$this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
5685	}
5686
5687	/**
5688	* adds an element to the WSDL types
5689	*
5690	* @param array $attrs attributes that must include name and type
5691	* @see xmlschema
5692	* @access public
5693	*/
5694	function addElement($attrs) {
5695		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5696		$this->schemas[$typens][0]->addElement($attrs);
5697	}
5698
5699	/**
5700	* register an operation with the server
5701	*
5702	* @param string $name operation (method) name
5703	* @param array $in assoc array of input values: key = param name, value = param type
5704	* @param array $out assoc array of output values: key = param name, value = param type
5705	* @param string $namespace optional The namespace for the operation
5706	* @param string $soapaction optional The soapaction for the operation
5707	* @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
5708	* @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
5709	* @param string $documentation optional The description to include in the WSDL
5710	* @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
5711	* @access public
5712	*/
5713	function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
5714		if ($use == 'encoded' && $encodingStyle == '') {
5715			$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5716		}
5717
5718		if ($style == 'document') {
5719			$elements = array();
5720			foreach ($in as $n => $t) {
5721				$elements[$n] = array('name' => $n, 'type' => $t);
5722			}
5723			$this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
5724			$this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
5725			$in = array('parameters' => 'tns:' . $name);
5726
5727			$elements = array();
5728			foreach ($out as $n => $t) {
5729				$elements[$n] = array('name' => $n, 'type' => $t);
5730			}
5731			$this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
5732			$this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType'));
5733			$out = array('parameters' => 'tns:' . $name . 'Response');
5734		}
5735
5736		// get binding
5737		$this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
5738		array(
5739		'name' => $name,
5740		'binding' => $this->serviceName . 'Binding',
5741		'endpoint' => $this->endpoint,
5742		'soapAction' => $soapaction,
5743		'style' => $style,
5744		'input' => array(
5745			'use' => $use,
5746			'namespace' => $namespace,
5747			'encodingStyle' => $encodingStyle,
5748			'message' => $name . 'Request',
5749			'parts' => $in),
5750		'output' => array(
5751			'use' => $use,
5752			'namespace' => $namespace,
5753			'encodingStyle' => $encodingStyle,
5754			'message' => $name . 'Response',
5755			'parts' => $out),
5756		'namespace' => $namespace,
5757		'transport' => 'http://schemas.xmlsoap.org/soap/http',
5758		'documentation' => $documentation);
5759		// add portTypes
5760		// add messages
5761		if($in)
5762		{
5763			foreach($in as $pName => $pType)
5764			{
5765				if(strpos($pType,':')) {
5766					$pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5767				}
5768				$this->messages[$name.'Request'][$pName] = $pType;
5769			}
5770		} else {
5771            $this->messages[$name.'Request']= '0';
5772        }
5773		if($out)
5774		{
5775			foreach($out as $pName => $pType)
5776			{
5777				if(strpos($pType,':')) {
5778					$pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5779				}
5780				$this->messages[$name.'Response'][$pName] = $pType;
5781			}
5782		} else {
5783            $this->messages[$name.'Response']= '0';
5784        }
5785		return true;
5786	}
5787}
5788?><?php
5789
5790
5791
5792/**
5793*
5794* soap_parser class parses SOAP XML messages into native PHP values
5795*
5796* @author   Dietrich Ayala <dietrich@ganx4.com>
5797* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
5798* @access   public
5799*/
5800class soap_parser extends nusoap_base {
5801
5802	var $xml = '';
5803	var $xml_encoding = '';
5804	var $method = '';
5805	var $root_struct = '';
5806	var $root_struct_name = '';
5807	var $root_struct_namespace = '';
5808	var $root_header = '';
5809    var $document = '';			// incoming SOAP body (text)
5810	// determines where in the message we are (envelope,header,body,method)
5811	var $status = '';
5812	var $position = 0;
5813	var $depth = 0;
5814	var $default_namespace = '';
5815	var $namespaces = array();
5816	var $message = array();
5817    var $parent = '';
5818	var $fault = false;
5819	var $fault_code = '';
5820	var $fault_str = '';
5821	var $fault_detail = '';
5822	var $depth_array = array();
5823	var $debug_flag = true;
5824	var $soapresponse = NULL;
5825	var $responseHeaders = '';	// incoming SOAP headers (text)
5826	var $body_position = 0;
5827	// for multiref parsing:
5828	// array of id => pos
5829	var $ids = array();
5830	// array of id => hrefs => pos
5831	var $multirefs = array();
5832	// toggle for auto-decoding element content
5833	var $decode_utf8 = false;
5834
5835	/**
5836	* constructor that actually does the parsing
5837	*
5838	* @param    string $xml SOAP message
5839	* @param    string $encoding character encoding scheme of message
5840	* @param    string $method method for which XML is parsed (unused?)
5841	* @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
5842	* @access   public
5843	*/
5844	function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
5845		parent::nusoap_base();
5846		$this->xml = $xml;
5847		$this->xml_encoding = $encoding;
5848		$this->method = $method;
5849		$this->decode_utf8 = $decode_utf8;
5850
5851		// Check whether content has been read.
5852		if(!empty($xml)){
5853			// Check XML encoding
5854			$pos_xml = strpos($xml, '<?xml');
5855			if ($pos_xml !== FALSE) {
5856				$xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
5857				if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
5858					$xml_encoding = $res[1];
5859					if (strtoupper($xml_encoding) != $encoding) {
5860						$err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
5861						$this->debug($err);
5862						if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
5863							$this->setError($err);
5864							return;
5865						}
5866						// when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
5867					} else {
5868						$this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
5869					}
5870				} else {
5871					$this->debug('No encoding specified in XML declaration');
5872				}
5873			} else {
5874				$this->debug('No XML declaration');
5875			}
5876			$this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
5877			// Create an XML parser - why not xml_parser_create_ns?
5878			$this->parser = xml_parser_create($this->xml_encoding);
5879			// Set the options for parsing the XML data.
5880			//xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
5881			xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
5882			xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
5883			// Set the object for the parser.
5884			xml_set_object($this->parser, $this);
5885			// Set the element handlers for the parser.
5886			xml_set_element_handler($this->parser, 'start_element','end_element');
5887			xml_set_character_data_handler($this->parser,'character_data');
5888
5889			// Parse the XML file.
5890			if(!xml_parse($this->parser,$xml,true)){
5891			    // Display an error message.
5892			    $err = sprintf('XML error parsing SOAP payload on line %d: %s',
5893			    xml_get_current_line_number($this->parser),
5894			    xml_error_string(xml_get_error_code($this->parser)));
5895				$this->debug($err);
5896				$this->debug("XML payload:\n" . $xml);
5897				$this->setError($err);
5898			} else {
5899				$this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
5900				// get final value
5901				$this->soapresponse = $this->message[$this->root_struct]['result'];
5902				// get header value: no, because this is documented as XML string
5903//				if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
5904//					$this->responseHeaders = $this->message[$this->root_header]['result'];
5905//				}
5906				// resolve hrefs/ids
5907				if(sizeof($this->multirefs) > 0){
5908					foreach($this->multirefs as $id => $hrefs){
5909						$this->debug('resolving multirefs for id: '.$id);
5910						$idVal = $this->buildVal($this->ids[$id]);
5911						if (is_array($idVal) && isset($idVal['!id'])) {
5912							unset($idVal['!id']);
5913						}
5914						foreach($hrefs as $refPos => $ref){
5915							$this->debug('resolving href at pos '.$refPos);
5916							$this->multirefs[$id][$refPos] = $idVal;
5917						}
5918					}
5919				}
5920			}
5921			xml_parser_free($this->parser);
5922		} else {
5923			$this->debug('xml was empty, didn\'t parse!');
5924			$this->setError('xml was empty, didn\'t parse!');
5925		}
5926	}
5927
5928	/**
5929	* start-element handler
5930	*
5931	* @param    resource $parser XML parser object
5932	* @param    string $name element name
5933	* @param    array $attrs associative array of attributes
5934	* @access   private
5935	*/
5936	function start_element($parser, $name, $attrs) {
5937		// position in a total number of elements, starting from 0
5938		// update class level pos
5939		$pos = $this->position++;
5940		// and set mine
5941		$this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
5942		// depth = how many levels removed from root?
5943		// set mine as current global depth and increment global depth value
5944		$this->message[$pos]['depth'] = $this->depth++;
5945
5946		// else add self as child to whoever the current parent is
5947		if($pos != 0){
5948			$this->message[$this->parent]['children'] .= '|'.$pos;
5949		}
5950		// set my parent
5951		$this->message[$pos]['parent'] = $this->parent;
5952		// set self as current parent
5953		$this->parent = $pos;
5954		// set self as current value for this depth
5955		$this->depth_array[$this->depth] = $pos;
5956		// get element prefix
5957		if(strpos($name,':')){
5958			// get ns prefix
5959			$prefix = substr($name,0,strpos($name,':'));
5960			// get unqualified name
5961			$name = substr(strstr($name,':'),1);
5962		}
5963		// set status
5964		if($name == 'Envelope'){
5965			$this->status = 'envelope';
5966		} elseif($name == 'Header'){
5967			$this->root_header = $pos;
5968			$this->status = 'header';
5969		} elseif($name == 'Body'){
5970			$this->status = 'body';
5971			$this->body_position = $pos;
5972		// set method
5973		} elseif($this->status == 'body' && $pos == ($this->body_position+1)){
5974			$this->status = 'method';
5975			$this->root_struct_name = $name;
5976			$this->root_struct = $pos;
5977			$this->message[$pos]['type'] = 'struct';
5978			$this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
5979		}
5980		// set my status
5981		$this->message[$pos]['status'] = $this->status;
5982		// set name
5983		$this->message[$pos]['name'] = htmlspecialchars($name);
5984		// set attrs
5985		$this->message[$pos]['attrs'] = $attrs;
5986
5987		// loop through atts, logging ns and type declarations
5988        $attstr = '';
5989		foreach($attrs as $key => $value){
5990        	$key_prefix = $this->getPrefix($key);
5991			$key_localpart = $this->getLocalPart($key);
5992			// if ns declarations, add to class level array of valid namespaces
5993            if($key_prefix == 'xmlns'){
5994				if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
5995					$this->XMLSchemaVersion = $value;
5996					$this->namespaces['xsd'] = $this->XMLSchemaVersion;
5997					$this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
5998				}
5999                $this->namespaces[$key_localpart] = $value;
6000				// set method namespace
6001				if($name == $this->root_struct_name){
6002					$this->methodNamespace = $value;
6003				}
6004			// if it's a type declaration, set type
6005            } elseif($key_localpart == 'type'){
6006            	$value_prefix = $this->getPrefix($value);
6007                $value_localpart = $this->getLocalPart($value);
6008				$this->message[$pos]['type'] = $value_localpart;
6009				$this->message[$pos]['typePrefix'] = $value_prefix;
6010                if(isset($this->namespaces[$value_prefix])){
6011                	$this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6012                } else if(isset($attrs['xmlns:'.$value_prefix])) {
6013					$this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6014                }
6015				// should do something here with the namespace of specified type?
6016			} elseif($key_localpart == 'arrayType'){
6017				$this->message[$pos]['type'] = 'array';
6018				/* do arrayType ereg here
6019				[1]    arrayTypeValue    ::=    atype asize
6020				[2]    atype    ::=    QName rank*
6021				[3]    rank    ::=    '[' (',')* ']'
6022				[4]    asize    ::=    '[' length~ ']'
6023				[5]    length    ::=    nextDimension* Digit+
6024				[6]    nextDimension    ::=    Digit+ ','
6025				*/
6026				$expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
6027				if(ereg($expr,$value,$regs)){
6028					$this->message[$pos]['typePrefix'] = $regs[1];
6029					$this->message[$pos]['arrayTypePrefix'] = $regs[1];
6030	                if (isset($this->namespaces[$regs[1]])) {
6031	                	$this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6032	                } else if (isset($attrs['xmlns:'.$regs[1]])) {
6033						$this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6034	                }
6035					$this->message[$pos]['arrayType'] = $regs[2];
6036					$this->message[$pos]['arraySize'] = $regs[3];
6037					$this->message[$pos]['arrayCols'] = $regs[4];
6038				}
6039			// specifies nil value (or not)
6040			} elseif ($key_localpart == 'nil'){
6041				$this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6042			// some other attribute
6043			} elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6044				$this->message[$pos]['xattrs']['!' . $key] = $value;
6045			}
6046
6047			if ($key == 'xmlns') {
6048				$this->default_namespace = $value;
6049			}
6050			// log id
6051			if($key == 'id'){
6052				$this->ids[$value] = $pos;
6053			}
6054			// root
6055			if($key_localpart == 'root' && $value == 1){
6056				$this->status = 'method';
6057				$this->root_struct_name = $name;
6058				$this->root_struct = $pos;
6059				$this->debug("found root struct $this->root_struct_name, pos $pos");
6060			}
6061            // for doclit
6062            $attstr .= " $key=\"$value\"";
6063		}
6064        // get namespace - must be done after namespace atts are processed
6065		if(isset($prefix)){
6066			$this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6067			$this->default_namespace = $this->namespaces[$prefix];
6068		} else {
6069			$this->message[$pos]['namespace'] = $this->default_namespace;
6070		}
6071        if($this->status == 'header'){
6072        	if ($this->root_header != $pos) {
6073	        	$this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6074	        }
6075        } elseif($this->root_struct_name != ''){
6076        	$this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6077        }
6078	}
6079
6080	/**
6081	* end-element handler
6082	*
6083	* @param    resource $parser XML parser object
6084	* @param    string $name element name
6085	* @access   private
6086	*/
6087	function end_element($parser, $name) {
6088		// position of current element is equal to the last value left in depth_array for my depth
6089		$pos = $this->depth_array[$this->depth--];
6090
6091        // get element prefix
6092		if(strpos($name,':')){
6093			// get ns prefix
6094			$prefix = substr($name,0,strpos($name,':'));
6095			// get unqualified name
6096			$name = substr(strstr($name,':'),1);
6097		}
6098
6099		// build to native type
6100		if(isset($this->body_position) && $pos > $this->body_position){
6101			// deal w/ multirefs
6102			if(isset($this->message[$pos]['attrs']['href'])){
6103				// get id
6104				$id = substr($this->message[$pos]['attrs']['href'],1);
6105				// add placeholder to href array
6106				$this->multirefs[$id][$pos] = 'placeholder';
6107				// add set a reference to it as the result value
6108				$this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6109            // build complexType values
6110			} elseif($this->message[$pos]['children'] != ''){
6111				// if result has already been generated (struct/array)
6112				if(!isset($this->message[$pos]['result'])){
6113					$this->message[$pos]['result'] = $this->buildVal($pos);
6114				}
6115			// build complexType values of attributes and possibly simpleContent
6116			} elseif (isset($this->message[$pos]['xattrs'])) {
6117				if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6118					$this->message[$pos]['xattrs']['!'] = null;
6119				} elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6120	            	if (isset($this->message[$pos]['type'])) {
6121						$this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6122					} else {
6123						$parent = $this->message[$pos]['parent'];
6124						if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6125							$this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6126						} else {
6127							$this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6128						}
6129					}
6130				}
6131				$this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6132			// set value of simpleType (or nil complexType)
6133			} else {
6134            	//$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6135				if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6136					$this->message[$pos]['xattrs']['!'] = null;
6137				} elseif (isset($this->message[$pos]['type'])) {
6138					$this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6139				} else {
6140					$parent = $this->message[$pos]['parent'];
6141					if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6142						$this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6143					} else {
6144						$this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6145					}
6146				}
6147
6148				/* add value to parent's result, if parent is struct/array
6149				$parent = $this->message[$pos]['parent'];
6150				if($this->message[$parent]['type'] != 'map'){
6151					if(strtolower($this->message[$parent]['type']) == 'array'){
6152						$this->message[$parent]['result'][] = $this->message[$pos]['result'];
6153					} else {
6154						$this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6155					}
6156				}
6157				*/
6158			}
6159		}
6160
6161        // for doclit
6162        if($this->status == 'header'){
6163        	if ($this->root_header != $pos) {
6164	        	$this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6165	        }
6166        } elseif($pos >= $this->root_struct){
6167        	$this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6168        }
6169		// switch status
6170		if($pos == $this->root_struct){
6171			$this->status = 'body';
6172			$this->root_struct_namespace = $this->message[$pos]['namespace'];
6173		} elseif($name == 'Body'){
6174			$this->status = 'envelope';
6175		 } elseif($name == 'Header'){
6176			$this->status = 'envelope';
6177		} elseif($name == 'Envelope'){
6178			//
6179		}
6180		// set parent back to my parent
6181		$this->parent = $this->message[$pos]['parent'];
6182	}
6183
6184	/**
6185	* element content handler
6186	*
6187	* @param    resource $parser XML parser object
6188	* @param    string $data element content
6189	* @access   private
6190	*/
6191	function character_data($parser, $data){
6192		$pos = $this->depth_array[$this->depth];
6193		if ($this->xml_encoding=='UTF-8'){
6194			// TODO: add an option to disable this for folks who want
6195			// raw UTF-8 that, e.g., might not map to iso-8859-1
6196			// TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6197			if($this->decode_utf8){
6198				$data = utf8_decode($data);
6199			}
6200		}
6201        $this->message[$pos]['cdata'] .= $data;
6202        // for doclit
6203        if($this->status == 'header'){
6204        	$this->responseHeaders .= $data;
6205        } else {
6206        	$this->document .= $data;
6207        }
6208	}
6209
6210	/**
6211	* get the parsed message
6212	*
6213	* @return	mixed
6214	* @access   public
6215	*/
6216	function get_response(){
6217		return $this->soapresponse;
6218	}
6219
6220	/**
6221	* get the parsed headers
6222	*
6223	* @return	string XML or empty if no headers
6224	* @access   public
6225	*/
6226	function getHeaders(){
6227	    return $this->responseHeaders;
6228	}
6229
6230	/**
6231	* decodes simple types into PHP variables
6232	*
6233	* @param    string $value value to decode
6234	* @param    string $type XML type to decode
6235	* @param    string $typens XML type namespace to decode
6236	* @return	mixed PHP value
6237	* @access   private
6238	*/
6239	function decodeSimple($value, $type, $typens) {
6240		// TODO: use the namespace!
6241		if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
6242			return (string) $value;
6243		}
6244		if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
6245			return (int) $value;
6246		}
6247		if ($type == 'float' || $type == 'double' || $type == 'decimal') {
6248			return (double) $value;
6249		}
6250		if ($type == 'boolean') {
6251			if (strtolower($value) == 'false' || strtolower($value) == 'f') {
6252				return false;
6253			}
6254			return (boolean) $value;
6255		}
6256		if ($type == 'base64' || $type == 'base64Binary') {
6257			$this->debug('Decode base64 value');
6258			return base64_decode($value);
6259		}
6260		// obscure numeric types
6261		if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
6262			|| $type == 'nonNegativeInteger' || $type == 'positiveInteger'
6263			|| $type == 'unsignedInt'
6264			|| $type == 'unsignedShort' || $type == 'unsignedByte') {
6265			return (int) $value;
6266		}
6267		// bogus: parser treats array with no elements as a simple type
6268		if ($type == 'array') {
6269			return array();
6270		}
6271		// everything else
6272		return (string) $value;
6273	}
6274
6275	/**
6276	* builds response structures for compound values (arrays/structs)
6277	* and scalars
6278	*
6279	* @param    integer $pos position in node tree
6280	* @return	mixed	PHP value
6281	* @access   private
6282	*/
6283	function buildVal($pos){
6284		if(!isset($this->message[$pos]['type'])){
6285			$this->message[$pos]['type'] = '';
6286		}
6287		$this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
6288		// if there are children...
6289		if($this->message[$pos]['children'] != ''){
6290			$this->debug('in buildVal, there are children');
6291			$children = explode('|',$this->message[$pos]['children']);
6292			array_shift($children); // knock off empty
6293			// md array
6294			if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
6295            	$r=0; // rowcount
6296            	$c=0; // colcount
6297            	foreach($children as $child_pos){
6298					$this->debug("in buildVal, got an MD array element: $r, $c");
6299					$params[$r][] = $this->message[$child_pos]['result'];
6300				    $c++;
6301				    if($c == $this->message[$pos]['arrayCols']){
6302				    	$c = 0;
6303						$r++;
6304				    }
6305                }
6306            // array
6307			} elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
6308                $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
6309                foreach($children as $child_pos){
6310                	$params[] = &$this->message[$child_pos]['result'];
6311                }
6312            // apache Map type: java hashtable
6313            } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
6314                $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
6315                foreach($children as $child_pos){
6316                	$kv = explode("|",$this->message[$child_pos]['children']);
6317                   	$params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
6318                }
6319            // generic compound type
6320            //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
6321		    } else {
6322	    		// Apache Vector type: treat as an array
6323                $this->debug('in buildVal, adding Java Vector '.$this->message[$pos]['name']);
6324				if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
6325					$notstruct = 1;
6326				} else {
6327					$notstruct = 0;
6328	            }
6329            	//
6330            	foreach($children as $child_pos){
6331            		if($notstruct){
6332            			$params[] = &$this->message[$child_pos]['result'];
6333            		} else {
6334            			if (isset($params[$this->message[$child_pos]['name']])) {
6335            				// de-serialize repeated element name into an array
6336            				if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
6337            					$params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
6338            				}
6339            				$params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
6340            			} else {
6341					    	$params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
6342					    }
6343                	}
6344                }
6345			}
6346			if (isset($this->message[$pos]['xattrs'])) {
6347                $this->debug('in buildVal, handling attributes');
6348				foreach ($this->message[$pos]['xattrs'] as $n => $v) {
6349					$params[$n] = $v;
6350				}
6351			}
6352			// handle simpleContent
6353			if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6354                $this->debug('in buildVal, handling simpleContent');
6355            	if (isset($this->message[$pos]['type'])) {
6356					$params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6357				} else {
6358					$parent = $this->message[$pos]['parent'];
6359					if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6360						$params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6361					} else {
6362						$params['!'] = $this->message[$pos]['cdata'];
6363					}
6364				}
6365			}
6366			return is_array($params) ? $params : array();
6367		} else {
6368        	$this->debug('in buildVal, no children, building scalar');
6369			$cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
6370        	if (isset($this->message[$pos]['type'])) {
6371				return $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6372			}
6373			$parent = $this->message[$pos]['parent'];
6374			if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6375				return $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6376			}
6377           	return $this->message[$pos]['cdata'];
6378		}
6379	}
6380}
6381
6382
6383
6384?><?php
6385
6386
6387
6388/**
6389*
6390* soapclient higher level class for easy usage.
6391*
6392* usage:
6393*
6394* // instantiate client with server info
6395* $soapclient = new soapclient( string path [ ,boolean wsdl] );
6396*
6397* // call method, get results
6398* echo $soapclient->call( string methodname [ ,array parameters] );
6399*
6400* // bye bye client
6401* unset($soapclient);
6402*
6403* @author   Dietrich Ayala <dietrich@ganx4.com>
6404* @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
6405* @access   public
6406*/
6407class soapclient extends nusoap_base  {
6408
6409	var $username = '';
6410	var $password = '';
6411	var $authtype = '';
6412	var $certRequest = array();
6413	var $requestHeaders = false;	// SOAP headers in request (text)
6414	var $responseHeaders = '';		// SOAP headers from response (incomplete namespace resolution) (text)
6415	var $document = '';				// SOAP body response portion (incomplete namespace resolution) (text)
6416	var $endpoint;
6417	var $forceEndpoint = '';		// overrides WSDL endpoint
6418    var $proxyhost = '';
6419    var $proxyport = '';
6420	var $proxyusername = '';
6421	var $proxypassword = '';
6422    var $xml_encoding = '';			// character set encoding of incoming (response) messages
6423	var $http_encoding = false;
6424	var $timeout = 0;				// HTTP connection timeout
6425	var $response_timeout = 30;		// HTTP response timeout
6426	var $endpointType = '';			// soap|wsdl, empty for WSDL initialization error
6427	var $persistentConnection = false;
6428	var $defaultRpcParams = false;	// This is no longer used
6429	var $request = '';				// HTTP request
6430	var $response = '';				// HTTP response
6431	var $responseData = '';			// SOAP payload of response
6432	var $cookies = array();			// Cookies from response or for request
6433    var $decode_utf8 = true;		// toggles whether the parser decodes element content w/ utf8_decode()
6434	var $operations = array();		// WSDL operations, empty for WSDL initialization error
6435
6436	/*
6437	 * fault related variables
6438	 */
6439	/**
6440	 * @var      fault
6441	 * @access   public
6442	 */
6443	var $fault;
6444	/**
6445	 * @var      faultcode
6446	 * @access   public
6447	 */
6448	var $faultcode;
6449	/**
6450	 * @var      faultstring
6451	 * @access   public
6452	 */
6453	var $faultstring;
6454	/**
6455	 * @var      faultdetail
6456	 * @access   public
6457	 */
6458	var $faultdetail;
6459
6460	/**
6461	* constructor
6462	*
6463	* @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
6464	* @param    bool $wsdl optional, set to true if using WSDL
6465	* @param	int $portName optional portName in WSDL document
6466	* @param    string $proxyhost
6467	* @param    string $proxyport
6468	* @param	string $proxyusername
6469	* @param	string $proxypassword
6470	* @param	integer $timeout set the connection timeout
6471	* @param	integer $response_timeout set the response timeout
6472	* @access   public
6473	*/
6474	function soapclient($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
6475		parent::nusoap_base();
6476		$this->endpoint = $endpoint;
6477		$this->proxyhost = $proxyhost;
6478		$this->proxyport = $proxyport;
6479		$this->proxyusername = $proxyusername;
6480		$this->proxypassword = $proxypassword;
6481		$this->timeout = $timeout;
6482		$this->response_timeout = $response_timeout;
6483
6484		// make values
6485		if($wsdl){
6486			if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
6487				$this->wsdl = $endpoint;
6488				$this->endpoint = $this->wsdl->wsdl;
6489				$this->wsdlFile = $this->endpoint;
6490				$this->debug('existing wsdl instance created from ' . $this->endpoint);
6491			} else {
6492				$this->wsdlFile = $this->endpoint;
6493
6494				// instantiate wsdl object and parse wsdl file
6495				$this->debug('instantiating wsdl class with doc: '.$endpoint);
6496				$this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout);
6497			}
6498			$this->appendDebug($this->wsdl->getDebug());
6499			$this->wsdl->clearDebug();
6500			// catch errors
6501			if($errstr = $this->wsdl->getError()){
6502				$this->debug('got wsdl error: '.$errstr);
6503				$this->setError('wsdl error: '.$errstr);
6504			} elseif($this->operations = $this->wsdl->getOperations()){
6505				$this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
6506				$this->endpointType = 'wsdl';
6507			} else {
6508				$this->debug( 'getOperations returned false');
6509				$this->setError('no operations defined in the WSDL document!');
6510			}
6511		} else {
6512			$this->debug("instantiate SOAP with endpoint at $endpoint");
6513			$this->endpointType = 'soap';
6514		}
6515	}
6516
6517	/**
6518	* calls method, returns PHP native type
6519	*
6520	* @param    string $method SOAP server URL or path
6521	* @param    mixed $params An array, associative or simple, of the parameters
6522	*			              for the method call, or a string that is the XML
6523	*			              for the call.  For rpc style, this call will
6524	*			              wrap the XML in a tag named after the method, as
6525	*			              well as the SOAP Envelope and Body.  For document
6526	*			              style, this will only wrap with the Envelope and Body.
6527	*			              IMPORTANT: when using an array with document style,
6528	*			              in which case there
6529	*                         is really one parameter, the root of the fragment
6530	*                         used in the call, which encloses what programmers
6531	*                         normally think of parameters.  A parameter array
6532	*                         *must* include the wrapper.
6533	* @param	string $namespace optional method namespace (WSDL can override)
6534	* @param	string $soapAction optional SOAPAction value (WSDL can override)
6535	* @param	mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
6536	* @param	boolean $rpcParams optional (no longer used)
6537	* @param	string	$style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
6538	* @param	string	$use optional (encoded|literal) the use when serializing parameters (WSDL can override)
6539	* @return	mixed	response from SOAP call
6540	* @access   public
6541	*/
6542	function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
6543		$this->operation = $operation;
6544		$this->fault = false;
6545		$this->setError('');
6546		$this->request = '';
6547		$this->response = '';
6548		$this->responseData = '';
6549		$this->faultstring = '';
6550		$this->faultcode = '';
6551		$this->opData = array();
6552
6553		$this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
6554		$this->appendDebug('params=' . $this->varDump($params));
6555		$this->appendDebug('headers=' . $this->varDump($headers));
6556		if ($headers) {
6557			$this->requestHeaders = $headers;
6558		}
6559		// serialize parameters
6560		if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
6561			// use WSDL for operation
6562			$this->opData = $opData;
6563			$this->debug("found operation");
6564			$this->appendDebug('opData=' . $this->varDump($opData));
6565			if (isset($opData['soapAction'])) {
6566				$soapAction = $opData['soapAction'];
6567			}
6568			if (! $this->forceEndpoint) {
6569				$this->endpoint = $opData['endpoint'];
6570			} else {
6571				$this->endpoint = $this->forceEndpoint;
6572			}
6573			$namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] :	$namespace;
6574			$style = $opData['style'];
6575			$use = $opData['input']['use'];
6576			// add ns to ns array
6577			if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
6578				$nsPrefix = 'ns' . rand(1000, 9999);
6579				$this->wsdl->namespaces[$nsPrefix] = $namespace;
6580			}
6581            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
6582			// serialize payload
6583			if (is_string($params)) {
6584				$this->debug("serializing param string for WSDL operation $operation");
6585				$payload = $params;
6586			} elseif (is_array($params)) {
6587				$this->debug("serializing param array for WSDL operation $operation");
6588				$payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
6589			} else {
6590				$this->debug('params must be array or string');
6591				$this->setError('params must be array or string');
6592				return false;
6593			}
6594            $usedNamespaces = $this->wsdl->usedNamespaces;
6595			if (isset($opData['input']['encodingStyle'])) {
6596				$encodingStyle = $opData['input']['encodingStyle'];
6597			} else {
6598				$encodingStyle = '';
6599			}
6600			$this->appendDebug($this->wsdl->getDebug());
6601			$this->wsdl->clearDebug();
6602			if ($errstr = $this->wsdl->getError()) {
6603				$this->debug('got wsdl error: '.$errstr);
6604				$this->setError('wsdl error: '.$errstr);
6605				return false;
6606			}
6607		} elseif($this->endpointType == 'wsdl') {
6608			// operation not in WSDL
6609			$this->appendDebug($this->wsdl->getDebug());
6610			$this->wsdl->clearDebug();
6611			$this->setError( 'operation '.$operation.' not present.');
6612			$this->debug("operation '$operation' not present.");
6613			return false;
6614		} else {
6615			// no WSDL
6616			//$this->namespaces['ns1'] = $namespace;
6617			$nsPrefix = 'ns' . rand(1000, 9999);
6618			// serialize
6619			$payload = '';
6620			if (is_string($params)) {
6621				$this->debug("serializing param string for operation $operation");
6622				$payload = $params;
6623			} elseif (is_array($params)) {
6624				$this->debug("serializing param array for operation $operation");
6625				foreach($params as $k => $v){
6626					$payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
6627				}
6628			} else {
6629				$this->debug('params must be array or string');
6630				$this->setError('params must be array or string');
6631				return false;
6632			}
6633			$usedNamespaces = array();
6634			if ($use == 'encoded') {
6635				$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6636			} else {
6637				$encodingStyle = '';
6638			}
6639		}
6640		// wrap RPC calls with method element
6641		if ($style == 'rpc') {
6642			if ($use == 'literal') {
6643				$this->debug("wrapping RPC request with literal method element");
6644				if ($namespace) {
6645					$payload = "<$operation xmlns=\"$namespace\">" . $payload . "</$operation>";
6646				} else {
6647					$payload = "<$operation>" . $payload . "</$operation>";
6648				}
6649			} else {
6650				$this->debug("wrapping RPC request with encoded method element");
6651				if ($namespace) {
6652					$payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
6653								$payload .
6654								"</$nsPrefix:$operation>";
6655				} else {
6656					$payload = "<$operation>" .
6657								$payload .
6658								"</$operation>";
6659				}
6660			}
6661		}
6662		// serialize envelope
6663		$soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
6664		$this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
6665		$this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
6666		// send
6667		$return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
6668		if($errstr = $this->getError()){
6669			$this->debug('Error: '.$errstr);
6670			return false;
6671		} else {
6672			$this->return = $return;
6673			$this->debug('sent message successfully and got a(n) '.gettype($return));
6674           	$this->appendDebug('return=' . $this->varDump($return));
6675
6676			// fault?
6677			if(is_array($return) && isset($return['faultcode'])){
6678				$this->debug('got fault');
6679				$this->setError($return['faultcode'].': '.$return['faultstring']);
6680				$this->fault = true;
6681				foreach($return as $k => $v){
6682					$this->$k = $v;
6683					$this->debug("$k = $v<br>");
6684				}
6685				return $return;
6686			} elseif ($style == 'document') {
6687				// NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
6688				// we are only going to return the first part here...sorry about that
6689				return $return;
6690			} else {
6691				// array of return values
6692				if(is_array($return)){
6693					// multiple 'out' parameters, which we return wrapped up
6694					// in the array
6695					if(sizeof($return) > 1){
6696						return $return;
6697					}
6698					// single 'out' parameter (normally the return value)
6699					$return = array_shift($return);
6700					$this->debug('return shifted value: ');
6701					$this->appendDebug($this->varDump($return));
6702           			return $return;
6703				// nothing returned (ie, echoVoid)
6704				} else {
6705					return "";
6706				}
6707			}
6708		}
6709	}
6710
6711	/**
6712	* get available data pertaining to an operation
6713	*
6714	* @param    string $operation operation name
6715	* @return	array array of data pertaining to the operation
6716	* @access   public
6717	*/
6718	function getOperationData($operation){
6719		if(isset($this->operations[$operation])){
6720			return $this->operations[$operation];
6721		}
6722		$this->debug("No data for operation: $operation");
6723	}
6724
6725    /**
6726    * send the SOAP message
6727    *
6728    * Note: if the operation has multiple return values
6729    * the return value of this method will be an array
6730    * of those values.
6731    *
6732	* @param    string $msg a SOAPx4 soapmsg object
6733	* @param    string $soapaction SOAPAction value
6734	* @param    integer $timeout set connection timeout in seconds
6735	* @param	integer $response_timeout set response timeout in seconds
6736	* @return	mixed native PHP types.
6737	* @access   private
6738	*/
6739	function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
6740		$this->checkCookies();
6741		// detect transport
6742		switch(true){
6743			// http(s)
6744			case ereg('^http',$this->endpoint):
6745				$this->debug('transporting via HTTP');
6746				if($this->persistentConnection == true && is_object($this->persistentConnection)){
6747					$http =& $this->persistentConnection;
6748				} else {
6749					$http = new soap_transport_http($this->endpoint);
6750					if ($this->persistentConnection) {
6751						$http->usePersistentConnection();
6752					}
6753				}
6754				$http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
6755				$http->setSOAPAction($soapaction);
6756				if($this->proxyhost && $this->proxyport){
6757					$http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
6758				}
6759                if($this->authtype != '') {
6760					$http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
6761				}
6762				if($this->http_encoding != ''){
6763					$http->setEncoding($this->http_encoding);
6764				}
6765				$this->debug('sending message, length='.strlen($msg));
6766				if(ereg('^http:',$this->endpoint)){
6767				//if(strpos($this->endpoint,'http:')){
6768					$this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
6769				} elseif(ereg('^https',$this->endpoint)){
6770				//} elseif(strpos($this->endpoint,'https:')){
6771					//if(phpversion() == '4.3.0-dev'){
6772						//$response = $http->send($msg,$timeout,$response_timeout);
6773                   		//$this->request = $http->outgoing_payload;
6774						//$this->response = $http->incoming_payload;
6775					//} else
6776					$this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
6777				} else {
6778					$this->setError('no http/s in endpoint url');
6779				}
6780				$this->request = $http->outgoing_payload;
6781				$this->response = $http->incoming_payload;
6782				$this->appendDebug($http->getDebug());
6783				$this->UpdateCookies($http->incoming_cookies);
6784
6785				// save transport object if using persistent connections
6786				if ($this->persistentConnection) {
6787					$http->clearDebug();
6788					if (!is_object($this->persistentConnection)) {
6789						$this->persistentConnection = $http;
6790					}
6791				}
6792
6793				if($err = $http->getError()){
6794					$this->setError('HTTP Error: '.$err);
6795					return false;
6796				} elseif($this->getError()){
6797					return false;
6798				} else {
6799					$this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
6800					return $this->parseResponse($http->incoming_headers, $this->responseData);
6801				}
6802			break;
6803			default:
6804				$this->setError('no transport found, or selected transport is not yet supported!');
6805			return false;
6806			break;
6807		}
6808	}
6809
6810	/**
6811	* processes SOAP message returned from server
6812	*
6813	* @param	array	$headers	The HTTP headers
6814	* @param	string	$data		unprocessed response data from server
6815	* @return	mixed	value of the message, decoded into a PHP type
6816	* @access   private
6817	*/
6818    function parseResponse($headers, $data) {
6819		$this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
6820		if (!strstr($headers['content-type'], 'text/xml')) {
6821			$this->setError('Response not of type text/xml');
6822			return false;
6823		}
6824		if (strpos($headers['content-type'], '=')) {
6825			$enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
6826			$this->debug('Got response encoding: ' . $enc);
6827			if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
6828				$this->xml_encoding = strtoupper($enc);
6829			} else {
6830				$this->xml_encoding = 'US-ASCII';
6831			}
6832		} else {
6833			// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
6834			$this->xml_encoding = 'ISO-8859-1';
6835		}
6836		$this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
6837		$parser = new soap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
6838		// add parser debug data to our debug
6839		$this->appendDebug($parser->getDebug());
6840		// if parse errors
6841		if($errstr = $parser->getError()){
6842			$this->setError( $errstr);
6843			// destroy the parser object
6844			unset($parser);
6845			return false;
6846		} else {
6847			// get SOAP headers
6848			$this->responseHeaders = $parser->getHeaders();
6849			// get decoded message
6850			$return = $parser->get_response();
6851            // add document for doclit support
6852            $this->document = $parser->document;
6853			// destroy the parser object
6854			unset($parser);
6855			// return decode message
6856			return $return;
6857		}
6858	 }
6859
6860	/**
6861	* sets the SOAP endpoint, which can override WSDL
6862	*
6863	* @param	$endpoint string The endpoint URL to use, or empty string or false to prevent override
6864	* @access   public
6865	*/
6866	function setEndpoint($endpoint) {
6867		$this->forceEndpoint = $endpoint;
6868	}
6869
6870	/**
6871	* set the SOAP headers
6872	*
6873	* @param	$headers mixed String of XML with SOAP header content, or array of soapval objects for SOAP headers
6874	* @access   public
6875	*/
6876	function setHeaders($headers){
6877		$this->requestHeaders = $headers;
6878	}
6879
6880	/**
6881	* get the SOAP response headers (namespace resolution incomplete)
6882	*
6883	* @return	string
6884	* @access   public
6885	*/
6886	function getHeaders(){
6887		return $this->responseHeaders;
6888	}
6889
6890	/**
6891	* set proxy info here
6892	*
6893	* @param    string $proxyhost
6894	* @param    string $proxyport
6895	* @param	string $proxyusername
6896	* @param	string $proxypassword
6897	* @access   public
6898	*/
6899	function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
6900		$this->proxyhost = $proxyhost;
6901		$this->proxyport = $proxyport;
6902		$this->proxyusername = $proxyusername;
6903		$this->proxypassword = $proxypassword;
6904	}
6905
6906	/**
6907	* if authenticating, set user credentials here
6908	*
6909	* @param    string $username
6910	* @param    string $password
6911	* @param	string $authtype (basic|digest|certificate)
6912	* @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
6913	* @access   public
6914	*/
6915	function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
6916		$this->username = $username;
6917		$this->password = $password;
6918		$this->authtype = $authtype;
6919		$this->certRequest = $certRequest;
6920	}
6921
6922	/**
6923	* use HTTP encoding
6924	*
6925	* @param    string $enc
6926	* @access   public
6927	*/
6928	function setHTTPEncoding($enc='gzip, deflate'){
6929		$this->http_encoding = $enc;
6930	}
6931
6932	/**
6933	* use HTTP persistent connections if possible
6934	*
6935	* @access   public
6936	*/
6937	function useHTTPPersistentConnection(){
6938		$this->persistentConnection = true;
6939	}
6940
6941	/**
6942	* gets the default RPC parameter setting.
6943	* If true, default is that call params are like RPC even for document style.
6944	* Each call() can override this value.
6945	*
6946	* This is no longer used.
6947	*
6948	* @return boolean
6949	* @access public
6950	* @deprecated
6951	*/
6952	function getDefaultRpcParams() {
6953		return $this->defaultRpcParams;
6954	}
6955
6956	/**
6957	* sets the default RPC parameter setting.
6958	* If true, default is that call params are like RPC even for document style
6959	* Each call() can override this value.
6960	*
6961	* This is no longer used.
6962	*
6963	* @param    boolean $rpcParams
6964	* @access public
6965	* @deprecated
6966	*/
6967	function setDefaultRpcParams($rpcParams) {
6968		$this->defaultRpcParams = $rpcParams;
6969	}
6970
6971	/**
6972	* dynamically creates an instance of a proxy class,
6973	* allowing user to directly call methods from wsdl
6974	*
6975	* @return   object soap_proxy object
6976	* @access   public
6977	*/
6978	function getProxy(){
6979		$r = rand();
6980		$evalStr = $this->_getProxyClassCode($r);
6981		//$this->debug("proxy class: $evalStr";
6982		// eval the class
6983		eval($evalStr);
6984		// instantiate proxy object
6985		eval("\$proxy = new soap_proxy_$r('');");
6986		// transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
6987		$proxy->endpointType = 'wsdl';
6988		$proxy->wsdlFile = $this->wsdlFile;
6989		$proxy->wsdl = $this->wsdl;
6990		$proxy->operations = $this->operations;
6991		$proxy->defaultRpcParams = $this->defaultRpcParams;
6992		// transfer other state
6993		$proxy->username = $this->username;
6994		$proxy->password = $this->password;
6995		$proxy->authtype = $this->authtype;
6996		$proxy->proxyhost = $this->proxyhost;
6997		$proxy->proxyport = $this->proxyport;
6998		$proxy->proxyusername = $this->proxyusername;
6999		$proxy->proxypassword = $this->proxypassword;
7000		$proxy->timeout = $this->timeout;
7001		$proxy->response_timeout = $this->response_timeout;
7002		$proxy->http_encoding = $this->http_encoding;
7003		$proxy->persistentConnection = $this->persistentConnection;
7004		$proxy->requestHeaders = $this->requestHeaders;
7005		$proxy->soap_defencoding = $this->soap_defencoding;
7006		$proxy->endpoint = $this->endpoint;
7007		$proxy->forceEndpoint = $this->forceEndpoint;
7008		return $proxy;
7009	}
7010
7011	/**
7012	* dynamically creates proxy class code
7013	*
7014	* @return   string PHP/NuSOAP code for the proxy class
7015	* @access   private
7016	*/
7017	function _getProxyClassCode($r) {
7018		if ($this->endpointType != 'wsdl') {
7019			$evalStr = 'A proxy can only be created for a WSDL client';
7020			$this->setError($evalStr);
7021			return $evalStr;
7022		}
7023		$evalStr = '';
7024		foreach ($this->operations as $operation => $opData) {
7025			if ($operation != '') {
7026				// create param string and param comment string
7027				if (sizeof($opData['input']['parts']) > 0) {
7028					$paramStr = '';
7029					$paramArrayStr = '';
7030					$paramCommentStr = '';
7031					foreach ($opData['input']['parts'] as $name => $type) {
7032						$paramStr .= "\$$name, ";
7033						$paramArrayStr .= "'$name' => \$$name, ";
7034						$paramCommentStr .= "$type \$$name, ";
7035					}
7036					$paramStr = substr($paramStr, 0, strlen($paramStr)-2);
7037					$paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
7038					$paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
7039				} else {
7040					$paramStr = '';
7041					$paramCommentStr = 'void';
7042				}
7043				$opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
7044				$evalStr .= "// $paramCommentStr
7045	function " . str_replace('.', '__', $operation) . "($paramStr) {
7046		\$params = array($paramArrayStr);
7047		return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
7048	}
7049	";
7050				unset($paramStr);
7051				unset($paramCommentStr);
7052			}
7053		}
7054		$evalStr = 'class soap_proxy_'.$r.' extends soapclient {
7055	'.$evalStr.'
7056}';
7057		return $evalStr;
7058	}
7059
7060	/**
7061	* dynamically creates proxy class code
7062	*
7063	* @return   string PHP/NuSOAP code for the proxy class
7064	* @access   public
7065	*/
7066	function getProxyClassCode() {
7067		$r = rand();
7068		return $this->_getProxyClassCode($r);
7069	}
7070
7071	/**
7072	* gets the HTTP body for the current request.
7073	*
7074	* @param string $soapmsg The SOAP payload
7075	* @return string The HTTP body, which includes the SOAP payload
7076	* @access private
7077	*/
7078	function getHTTPBody($soapmsg) {
7079		return $soapmsg;
7080	}
7081
7082	/**
7083	* gets the HTTP content type for the current request.
7084	*
7085	* Note: getHTTPBody must be called before this.
7086	*
7087	* @return string the HTTP content type for the current request.
7088	* @access private
7089	*/
7090	function getHTTPContentType() {
7091		return 'text/xml';
7092	}
7093
7094	/**
7095	* gets the HTTP content type charset for the current request.
7096	* returns false for non-text content types.
7097	*
7098	* Note: getHTTPBody must be called before this.
7099	*
7100	* @return string the HTTP content type charset for the current request.
7101	* @access private
7102	*/
7103	function getHTTPContentTypeCharset() {
7104		return $this->soap_defencoding;
7105	}
7106
7107	/*
7108	* whether or not parser should decode utf8 element content
7109    *
7110    * @return   always returns true
7111    * @access   public
7112    */
7113    function decodeUTF8($bool){
7114		$this->decode_utf8 = $bool;
7115		return true;
7116    }
7117
7118	/**
7119	 * adds a new Cookie into $this->cookies array
7120	 *
7121	 * @param	string $name Cookie Name
7122	 * @param	string $value Cookie Value
7123	 * @return	if cookie-set was successful returns true, else false
7124	 * @access	public
7125	 */
7126	function setCookie($name, $value) {
7127		if (strlen($name) == 0) {
7128			return false;
7129		}
7130		$this->cookies[] = array('name' => $name, 'value' => $value);
7131		return true;
7132	}
7133
7134	/**
7135	 * gets all Cookies
7136	 *
7137	 * @return   array with all internal cookies
7138	 * @access   public
7139	 */
7140	function getCookies() {
7141		return $this->cookies;
7142	}
7143
7144	/**
7145	 * checks all Cookies and delete those which are expired
7146	 *
7147	 * @return   always return true
7148	 * @access   private
7149	 */
7150	function checkCookies() {
7151		if (sizeof($this->cookies) == 0) {
7152			return true;
7153		}
7154		$this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
7155		$curr_cookies = $this->cookies;
7156		$this->cookies = array();
7157		foreach ($curr_cookies as $cookie) {
7158			if (! is_array($cookie)) {
7159				$this->debug('Remove cookie that is not an array');
7160				continue;
7161			}
7162			if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
7163				if (strtotime($cookie['expires']) > time()) {
7164					$this->cookies[] = $cookie;
7165				} else {
7166					$this->debug('Remove expired cookie ' . $cookie['name']);
7167				}
7168			} else {
7169				$this->cookies[] = $cookie;
7170			}
7171		}
7172		$this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
7173		return true;
7174	}
7175
7176	/**
7177	 * updates the current cookies with a new set
7178	 *
7179	 * @param	array $cookies new cookies with which to update current ones
7180	 * @return	always return true
7181	 * @access	private
7182	 */
7183	function UpdateCookies($cookies) {
7184		if (sizeof($this->cookies) == 0) {
7185			// no existing cookies: take whatever is new
7186			if (sizeof($cookies) > 0) {
7187				$this->debug('Setting new cookie(s)');
7188				$this->cookies = $cookies;
7189			}
7190			return true;
7191		}
7192		if (sizeof($cookies) == 0) {
7193			// no new cookies: keep what we've got
7194			return true;
7195		}
7196		// merge
7197		foreach ($cookies as $newCookie) {
7198			if (!is_array($newCookie)) {
7199				continue;
7200			}
7201			if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
7202				continue;
7203			}
7204			$newName = $newCookie['name'];
7205
7206			$found = false;
7207			for ($i = 0; $i < count($this->cookies); $i++) {
7208				$cookie = $this->cookies[$i];
7209				if (!is_array($cookie)) {
7210					continue;
7211				}
7212				if (!isset($cookie['name'])) {
7213					continue;
7214				}
7215				if ($newName != $cookie['name']) {
7216					continue;
7217				}
7218				$newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
7219				$domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
7220				if ($newDomain != $domain) {
7221					continue;
7222				}
7223				$newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
7224				$path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
7225				if ($newPath != $path) {
7226					continue;
7227				}
7228				$this->cookies[$i] = $newCookie;
7229				$found = true;
7230				$this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
7231				break;
7232			}
7233			if (! $found) {
7234				$this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
7235				$this->cookies[] = $newCookie;
7236			}
7237		}
7238		return true;
7239	}
7240}
7241?>
7242