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