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