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