1<?php
2
3/*
4$Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
5
6NuSOAP - Web Services Toolkit for PHP
7
8Copyright (c) 2002 NuSphere Corporation
9
10This library is free software; you can redistribute it and/or
11modify it under the terms of the GNU Lesser General Public
12License as published by the Free Software Foundation; either
13version 2.1 of the License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18Lesser General Public License for more details.
19
20You should have received a copy of the GNU Lesser General Public
21License along with this library; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24The NuSOAP project home is:
25http://sourceforge.net/projects/nusoap/
26
27The primary support for NuSOAP is the mailing list:
28nusoap-general@lists.sourceforge.net
29
30If you have any questions or comments, please email:
31
32Dietrich Ayala
33dietrich@ganx4.com
34http://dietrich.ganx4.com/nusoap
35
36NuSphere Corporation
37http://www.nusphere.com
38
39*/
40
41/*
42 *	Some of the standards implmented in whole or part by NuSOAP:
43 *
44 *	SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
45 *	WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
46 *	SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
47 *	XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
48 *	Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
49 *	XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
50 *	RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
51 *	RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
52 *	RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
53 */
54
55/* load classes
56
57// necessary classes
58require_once('class.soapclient.php');
59require_once('class.soap_val.php');
60require_once('class.soap_parser.php');
61require_once('class.soap_fault.php');
62
63// transport classes
64require_once('class.soap_transport_http.php');
65
66// optional add-on classes
67require_once('class.xmlschema.php');
68require_once('class.wsdl.php');
69
70// server class
71require_once('class.soap_server.php');*/
72
73// class variable emulation
74// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
75$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
76
77/**
78*
79* nusoap_base
80*
81* @author   Dietrich Ayala <dietrich@ganx4.com>
82* @author   Scott Nichol <snichol@users.sourceforge.net>
83* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
84* @access   public
85*/
86class nusoap_base {
87	/**
88	 * Identification for HTTP headers.
89	 *
90	 * @var string
91	 * @access private
92	 */
93	var $title = 'NuSOAP';
94	/**
95	 * Version for HTTP headers.
96	 *
97	 * @var string
98	 * @access private
99	 */
100	var $version = '0.7.3';
101	/**
102	 * CVS revision for HTTP headers.
103	 *
104	 * @var string
105	 * @access private
106	 */
107	var $revision = '$Revision: 1.114 $';
108    /**
109     * Current error string (manipulated by getError/setError)
110	 *
111	 * @var string
112	 * @access private
113	 */
114	var $error_str = '';
115    /**
116     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
117	 *
118	 * @var string
119	 * @access private
120	 */
121    var $debug_str = '';
122    /**
123	 * toggles automatic encoding of special characters as entities
124	 * (should always be true, I think)
125	 *
126	 * @var boolean
127	 * @access private
128	 */
129	var $charencoding = true;
130	/**
131	 * the debug level for this instance
132	 *
133	 * @var	integer
134	 * @access private
135	 */
136	var $debugLevel;
137
138    /**
139	* set schema version
140	*
141	* @var      string
142	* @access   public
143	*/
144	var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
145
146    /**
147	* charset encoding for outgoing messages
148	*
149	* @var      string
150	* @access   public
151	*/
152    var $soap_defencoding = 'ISO-8859-1';
153	//var $soap_defencoding = 'UTF-8';
154
155	/**
156	* namespaces in an array of prefix => uri
157	*
158	* this is "seeded" by a set of constants, but it may be altered by code
159	*
160	* @var      array
161	* @access   public
162	*/
163	var $namespaces = array(
164		'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
165		'xsd' => 'http://www.w3.org/2001/XMLSchema',
166		'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
167		'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
168		);
169
170	/**
171	* namespaces used in the current context, e.g. during serialization
172	*
173	* @var      array
174	* @access   private
175	*/
176	var $usedNamespaces = array();
177
178	/**
179	* XML Schema types in an array of uri => (array of xml type => php type)
180	* is this legacy yet?
181	* no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
182	* @var      array
183	* @access   public
184	*/
185	var $typemap = array(
186	'http://www.w3.org/2001/XMLSchema' => array(
187		'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
188		'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
189		'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
190		// abstract "any" types
191		'anyType'=>'string','anySimpleType'=>'string',
192		// derived datatypes
193		'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
194		'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
195		'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
196		'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
197	'http://www.w3.org/2000/10/XMLSchema' => array(
198		'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
199		'float'=>'double','dateTime'=>'string',
200		'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
201	'http://www.w3.org/1999/XMLSchema' => array(
202		'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
203		'float'=>'double','dateTime'=>'string',
204		'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
205	'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
206	'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
207    'http://xml.apache.org/xml-soap' => array('Map')
208	);
209
210	/**
211	* XML entities to convert
212	*
213	* @var      array
214	* @access   public
215	* @deprecated
216	* @see	expandEntities
217	*/
218	var $xmlEntities = array('quot' => '"','amp' => '&',
219		'lt' => '<','gt' => '>','apos' => "'");
220
221	/**
222	* constructor
223	*
224	* @access	public
225	*/
226	function nusoap_base() {
227		$this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
228	}
229
230	/**
231	* gets the global debug level, which applies to future instances
232	*
233	* @return	integer	Debug level 0-9, where 0 turns off
234	* @access	public
235	*/
236	function getGlobalDebugLevel() {
237		return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
238	}
239
240	/**
241	* sets the global debug level, which applies to future instances
242	*
243	* @param	int	$level	Debug level 0-9, where 0 turns off
244	* @access	public
245	*/
246	function setGlobalDebugLevel($level) {
247		$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
248	}
249
250	/**
251	* gets the debug level for this instance
252	*
253	* @return	int	Debug level 0-9, where 0 turns off
254	* @access	public
255	*/
256	function getDebugLevel() {
257		return $this->debugLevel;
258	}
259
260	/**
261	* sets the debug level for this instance
262	*
263	* @param	int	$level	Debug level 0-9, where 0 turns off
264	* @access	public
265	*/
266	function setDebugLevel($level) {
267		$this->debugLevel = $level;
268	}
269
270	/**
271	* adds debug data to the instance debug string with formatting
272	*
273	* @param    string $string debug data
274	* @access   private
275	*/
276	function debug($string){
277		if ($this->debugLevel > 0) {
278			$this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
279		}
280	}
281
282	/**
283	* adds debug data to the instance debug string without formatting
284	*
285	* @param    string $string debug data
286	* @access   public
287	*/
288	function appendDebug($string){
289		if ($this->debugLevel > 0) {
290			// it would be nice to use a memory stream here to use
291			// memory more efficiently
292			$this->debug_str .= $string;
293		}
294	}
295
296	/**
297	* clears the current debug data for this instance
298	*
299	* @access   public
300	*/
301	function clearDebug() {
302		// it would be nice to use a memory stream here to use
303		// memory more efficiently
304		$this->debug_str = '';
305	}
306
307	/**
308	* gets the current debug data for this instance
309	*
310	* @return   debug data
311	* @access   public
312	*/
313	function &getDebug() {
314		// it would be nice to use a memory stream here to use
315		// memory more efficiently
316		return $this->debug_str;
317	}
318
319	/**
320	* gets the current debug data for this instance as an XML comment
321	* this may change the contents of the debug data
322	*
323	* @return   debug data as an XML comment
324	* @access   public
325	*/
326	function &getDebugAsXMLComment() {
327		// it would be nice to use a memory stream here to use
328		// memory more efficiently
329		while (strpos($this->debug_str, '--')) {
330			$this->debug_str = str_replace('--', '- -', $this->debug_str);
331		}
332		$ret = "<!--\n" . $this->debug_str . "\n-->";
333    	return $ret;
334	}
335
336	/**
337	* expands entities, e.g. changes '<' to '&lt;'.
338	*
339	* @param	string	$val	The string in which to expand entities.
340	* @access	private
341	*/
342	function expandEntities($val) {
343		if ($this->charencoding) {
344	    	$val = str_replace('&', '&amp;', $val);
345	    	$val = str_replace("'", '&apos;', $val);
346	    	$val = str_replace('"', '&quot;', $val);
347	    	$val = str_replace('<', '&lt;', $val);
348	    	$val = str_replace('>', '&gt;', $val);
349	    }
350	    return $val;
351	}
352
353	/**
354	* returns error string if present
355	*
356	* @return   mixed error string or false
357	* @access   public
358	*/
359	function getError(){
360		if($this->error_str != ''){
361			return $this->error_str;
362		}
363		return false;
364	}
365
366	/**
367	* sets error string
368	*
369	* @return   boolean $string error string
370	* @access   private
371	*/
372	function setError($str){
373		$this->error_str = $str;
374	}
375
376	/**
377	* detect if array is a simple array or a struct (associative array)
378	*
379	* @param	mixed	$val	The PHP array
380	* @return	string	(arraySimple|arrayStruct)
381	* @access	private
382	*/
383	function isArraySimpleOrStruct($val) {
384        $keyList = array_keys($val);
385		foreach ($keyList as $keyListValue) {
386			if (!is_int($keyListValue)) {
387				return 'arrayStruct';
388			}
389		}
390		return 'arraySimple';
391	}
392
393	/**
394	* serializes PHP values in accordance w/ section 5. Type information is
395	* not serialized if $use == 'literal'.
396	*
397	* @param	mixed	$val	The value to serialize
398	* @param	string	$name	The name (local part) of the XML element
399	* @param	string	$type	The XML schema type (local part) for the element
400	* @param	string	$name_ns	The namespace for the name of the XML element
401	* @param	string	$type_ns	The namespace for the type of the element
402	* @param	array	$attributes	The attributes to serialize as name=>value pairs
403	* @param	string	$use	The WSDL "use" (encoded|literal)
404	* @param	boolean	$soapval	Whether this is called from soapval.
405	* @return	string	The serialized element, possibly with child elements
406    * @access	public
407	*/
408	function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
409		$this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
410		$this->appendDebug('value=' . $this->varDump($val));
411		$this->appendDebug('attributes=' . $this->varDump($attributes));
412
413    	if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
414    		$this->debug("serialize_val: serialize soapval");
415        	$xml = $val->serialize($use);
416			$this->appendDebug($val->getDebug());
417			$val->clearDebug();
418			$this->debug("serialize_val of soapval returning $xml");
419			return $xml;
420        }
421		// force valid name if necessary
422		if (is_numeric($name)) {
423			$name = '__numeric_' . $name;
424		} elseif (! $name) {
425			$name = 'noname';
426		}
427		// if name has ns, add ns prefix to name
428		$xmlns = '';
429        if($name_ns){
430			$prefix = 'nu'.rand(1000,9999);
431			$name = $prefix.':'.$name;
432			$xmlns .= " xmlns:$prefix=\"$name_ns\"";
433		}
434		// if type is prefixed, create type prefix
435		if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
436			// need to fix this. shouldn't default to xsd if no ns specified
437		    // w/o checking against typemap
438			$type_prefix = 'xsd';
439		} elseif($type_ns){
440			$type_prefix = 'ns'.rand(1000,9999);
441			$xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
442		}
443		// serialize attributes if present
444		$atts = '';
445		if($attributes){
446			foreach($attributes as $k => $v){
447				$atts .= " $k=\"".$this->expandEntities($v).'"';
448			}
449		}
450		// serialize null value
451		if (is_null($val)) {
452    		$this->debug("serialize_val: serialize null");
453			if ($use == 'literal') {
454				// TODO: depends on minOccurs
455				$xml = "<$name$xmlns$atts/>";
456				$this->debug("serialize_val returning $xml");
457	        	return $xml;
458        	} else {
459				if (isset($type) && isset($type_prefix)) {
460					$type_str = " xsi:type=\"$type_prefix:$type\"";
461				} else {
462					$type_str = '';
463				}
464				$xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
465				$this->debug("serialize_val returning $xml");
466	        	return $xml;
467        	}
468		}
469        // serialize if an xsd built-in primitive type
470        if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
471    		$this->debug("serialize_val: serialize xsd built-in primitive type");
472        	if (is_bool($val)) {
473        		if ($type == 'boolean') {
474	        		$val = $val ? 'true' : 'false';
475	        	} elseif (! $val) {
476	        		$val = 0;
477	        	}
478			} else if (is_string($val)) {
479				$val = $this->expandEntities($val);
480			}
481			if ($use == 'literal') {
482				$xml = "<$name$xmlns$atts>$val</$name>";
483				$this->debug("serialize_val returning $xml");
484	        	return $xml;
485        	} else {
486				$xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
487				$this->debug("serialize_val returning $xml");
488	        	return $xml;
489        	}
490        }
491		// detect type and serialize
492		$xml = '';
493		switch(true) {
494			case (is_bool($val) || $type == 'boolean'):
495		   		$this->debug("serialize_val: serialize boolean");
496        		if ($type == 'boolean') {
497	        		$val = $val ? 'true' : 'false';
498	        	} elseif (! $val) {
499	        		$val = 0;
500	        	}
501				if ($use == 'literal') {
502					$xml .= "<$name$xmlns$atts>$val</$name>";
503				} else {
504					$xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
505				}
506				break;
507			case (is_int($val) || is_long($val) || $type == 'int'):
508		   		$this->debug("serialize_val: serialize int");
509				if ($use == 'literal') {
510					$xml .= "<$name$xmlns$atts>$val</$name>";
511				} else {
512					$xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
513				}
514				break;
515			case (is_float($val)|| is_double($val) || $type == 'float'):
516		   		$this->debug("serialize_val: serialize float");
517				if ($use == 'literal') {
518					$xml .= "<$name$xmlns$atts>$val</$name>";
519				} else {
520					$xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
521				}
522				break;
523			case (is_string($val) || $type == 'string'):
524		   		$this->debug("serialize_val: serialize string");
525				$val = $this->expandEntities($val);
526				if ($use == 'literal') {
527					$xml .= "<$name$xmlns$atts>$val</$name>";
528				} else {
529					$xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
530				}
531				break;
532			case is_object($val):
533		   		$this->debug("serialize_val: serialize object");
534		    	if (get_class($val) == 'soapval') {
535		    		$this->debug("serialize_val: serialize soapval object");
536		        	$pXml = $val->serialize($use);
537					$this->appendDebug($val->getDebug());
538					$val->clearDebug();
539		        } else {
540					if (! $name) {
541						$name = get_class($val);
542						$this->debug("In serialize_val, used class name $name as element name");
543					} else {
544						$this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
545					}
546					foreach(get_object_vars($val) as $k => $v){
547						$pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
548					}
549				}
550				if(isset($type) && isset($type_prefix)){
551					$type_str = " xsi:type=\"$type_prefix:$type\"";
552				} else {
553					$type_str = '';
554				}
555				if ($use == 'literal') {
556					$xml .= "<$name$xmlns$atts>$pXml</$name>";
557				} else {
558					$xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
559				}
560				break;
561			break;
562			case (is_array($val) || $type):
563				// detect if struct or array
564				$valueType = $this->isArraySimpleOrStruct($val);
565                if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
566			   		$this->debug("serialize_val: serialize array");
567					$i = 0;
568					if(is_array($val) && count($val)> 0){
569						foreach($val as $v){
570	                    	if(is_object($v) && get_class($v) ==  'soapval'){
571								$tt_ns = $v->type_ns;
572								$tt = $v->type;
573							} elseif (is_array($v)) {
574								$tt = $this->isArraySimpleOrStruct($v);
575							} else {
576								$tt = gettype($v);
577	                        }
578							$array_types[$tt] = 1;
579							// TODO: for literal, the name should be $name
580							$xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
581							++$i;
582						}
583						if(count($array_types) > 1){
584							$array_typename = 'xsd:anyType';
585						} elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
586							if ($tt == 'integer') {
587								$tt = 'int';
588							}
589							$array_typename = 'xsd:'.$tt;
590						} elseif(isset($tt) && $tt == 'arraySimple'){
591							$array_typename = 'SOAP-ENC:Array';
592						} elseif(isset($tt) && $tt == 'arrayStruct'){
593							$array_typename = 'unnamed_struct_use_soapval';
594						} else {
595							// if type is prefixed, create type prefix
596							if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
597								 $array_typename = 'xsd:' . $tt;
598							} elseif ($tt_ns) {
599								$tt_prefix = 'ns' . rand(1000, 9999);
600								$array_typename = "$tt_prefix:$tt";
601								$xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
602							} else {
603								$array_typename = $tt;
604							}
605						}
606						$array_type = $i;
607						if ($use == 'literal') {
608							$type_str = '';
609						} else if (isset($type) && isset($type_prefix)) {
610							$type_str = " xsi:type=\"$type_prefix:$type\"";
611						} else {
612							$type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
613						}
614					// empty array
615					} else {
616						if ($use == 'literal') {
617							$type_str = '';
618						} else if (isset($type) && isset($type_prefix)) {
619							$type_str = " xsi:type=\"$type_prefix:$type\"";
620						} else {
621							$type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
622						}
623					}
624					// TODO: for array in literal, there is no wrapper here
625					$xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
626				} else {
627					// got a struct
628			   		$this->debug("serialize_val: serialize struct");
629					if(isset($type) && isset($type_prefix)){
630						$type_str = " xsi:type=\"$type_prefix:$type\"";
631					} else {
632						$type_str = '';
633					}
634					if ($use == 'literal') {
635						$xml .= "<$name$xmlns$atts>";
636					} else {
637						$xml .= "<$name$xmlns$type_str$atts>";
638					}
639					foreach($val as $k => $v){
640						// Apache Map
641						if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
642							$xml .= '<item>';
643							$xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
644							$xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
645							$xml .= '</item>';
646						} else {
647							$xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
648						}
649					}
650					$xml .= "</$name>";
651				}
652				break;
653			default:
654		   		$this->debug("serialize_val: serialize unknown");
655				$xml .= 'not detected, got '.gettype($val).' for '.$val;
656				break;
657		}
658		$this->debug("serialize_val returning $xml");
659		return $xml;
660	}
661
662    /**
663    * serializes a message
664    *
665    * @param string $body the XML of the SOAP body
666    * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
667    * @param array $namespaces optional the namespaces used in generating the body and headers
668    * @param string $style optional (rpc|document)
669    * @param string $use optional (encoded|literal)
670    * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
671    * @return string the message
672    * @access public
673    */
674    function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
675    // TODO: add an option to automatically run utf8_encode on $body and $headers
676    // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
677    // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
678
679	$this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
680	$this->debug("headers:");
681	$this->appendDebug($this->varDump($headers));
682	$this->debug("namespaces:");
683	$this->appendDebug($this->varDump($namespaces));
684
685	// serialize namespaces
686    $ns_string = '';
687	foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
688		$ns_string .= " xmlns:$k=\"$v\"";
689	}
690	if($encodingStyle) {
691		$ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
692	}
693
694	// serialize headers
695	if($headers){
696		if (is_array($headers)) {
697			$xml = '';
698			foreach ($headers as $k => $v) {
699				if (is_object($v) && get_class($v) == 'soapval') {
700					$xml .= $this->serialize_val($v, false, false, false, false, false, $use);
701				} else {
702					$xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
703				}
704			}
705			$headers = $xml;
706			$this->debug("In serializeEnvelope, serialized array of headers to $headers");
707		}
708		$headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
709	}
710	// serialize envelope
711	return
712	'<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
713	'<SOAP-ENV:Envelope'.$ns_string.">".
714	$headers.
715	"<SOAP-ENV:Body>".
716		$body.
717	"</SOAP-ENV:Body>".
718	"</SOAP-ENV:Envelope>";
719    }
720
721	/**
722	 * formats a string to be inserted into an HTML stream
723	 *
724	 * @param string $str The string to format
725	 * @return string The formatted string
726	 * @access public
727	 * @deprecated
728	 */
729    function formatDump($str){
730		$str = htmlspecialchars($str);
731		return nl2br($str);
732    }
733
734	/**
735	* contracts (changes namespace to prefix) a qualified name
736	*
737	* @param    string $qname qname
738	* @return	string contracted qname
739	* @access   private
740	*/
741	function contractQname($qname){
742		// get element namespace
743		//$this->xdebug("Contract $qname");
744		if (strrpos($qname, ':')) {
745			// get unqualified name
746			$name = substr($qname, strrpos($qname, ':') + 1);
747			// get ns
748			$ns = substr($qname, 0, strrpos($qname, ':'));
749			$p = $this->getPrefixFromNamespace($ns);
750			if ($p) {
751				return $p . ':' . $name;
752			}
753			return $qname;
754		} else {
755			return $qname;
756		}
757	}
758
759	/**
760	* expands (changes prefix to namespace) a qualified name
761	*
762	* @param    string $qname qname
763	* @return	string expanded qname
764	* @access   private
765	*/
766	function expandQname($qname){
767		// get element prefix
768		if(strpos($qname,':') && !ereg('^http://',$qname)){
769			// get unqualified name
770			$name = substr(strstr($qname,':'),1);
771			// get ns prefix
772			$prefix = substr($qname,0,strpos($qname,':'));
773			if(isset($this->namespaces[$prefix])){
774				return $this->namespaces[$prefix].':'.$name;
775			} else {
776				return $qname;
777			}
778		} else {
779			return $qname;
780		}
781	}
782
783    /**
784    * returns the local part of a prefixed string
785    * returns the original string, if not prefixed
786    *
787    * @param string $str The prefixed string
788    * @return string The local part
789    * @access public
790    */
791	function getLocalPart($str){
792		if($sstr = strrchr($str,':')){
793			// get unqualified name
794			return substr( $sstr, 1 );
795		} else {
796			return $str;
797		}
798	}
799
800	/**
801    * returns the prefix part of a prefixed string
802    * returns false, if not prefixed
803    *
804    * @param string $str The prefixed string
805    * @return mixed The prefix or false if there is no prefix
806    * @access public
807    */
808	function getPrefix($str){
809		if($pos = strrpos($str,':')){
810			// get prefix
811			return substr($str,0,$pos);
812		}
813		return false;
814	}
815
816	/**
817    * pass it a prefix, it returns a namespace
818    *
819    * @param string $prefix The prefix
820    * @return mixed The namespace, false if no namespace has the specified prefix
821    * @access public
822    */
823	function getNamespaceFromPrefix($prefix){
824		if (isset($this->namespaces[$prefix])) {
825			return $this->namespaces[$prefix];
826		}
827		//$this->setError("No namespace registered for prefix '$prefix'");
828		return false;
829	}
830
831	/**
832    * returns the prefix for a given namespace (or prefix)
833    * or false if no prefixes registered for the given namespace
834    *
835    * @param string $ns The namespace
836    * @return mixed The prefix, false if the namespace has no prefixes
837    * @access public
838    */
839	function getPrefixFromNamespace($ns) {
840		foreach ($this->namespaces as $p => $n) {
841			if ($ns == $n || $ns == $p) {
842			    $this->usedNamespaces[$p] = $n;
843				return $p;
844			}
845		}
846		return false;
847	}
848
849	/**
850    * returns the time in ODBC canonical form with microseconds
851    *
852    * @return string The time in ODBC canonical form with microseconds
853    * @access public
854    */
855	function getmicrotime() {
856		if (function_exists('gettimeofday')) {
857			$tod = gettimeofday();
858			$sec = $tod['sec'];
859			$usec = $tod['usec'];
860		} else {
861			$sec = time();
862			$usec = 0;
863		}
864		return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
865	}
866
867	/**
868	 * Returns a string with the output of var_dump
869	 *
870	 * @param mixed $data The variable to var_dump
871	 * @return string The output of var_dump
872	 * @access public
873	 */
874    function varDump($data) {
875		ob_start();
876		var_dump($data);
877		$ret_val = ob_get_contents();
878		ob_end_clean();
879		return $ret_val;
880	}
881
882	/**
883	* represents the object as a string
884	*
885	* @return	string
886	* @access   public
887	*/
888	function __toString() {
889		return $this->varDump($this);
890	}
891}
892
893// XML Schema Datatype Helper Functions
894
895//xsd:dateTime helpers
896
897/**
898* convert unix timestamp to ISO 8601 compliant date string
899*
900* @param    string $timestamp Unix time stamp
901* @param	boolean $utc Whether the time stamp is UTC or local
902* @access   public
903*/
904function timestamp_to_iso8601($timestamp,$utc=true){
905	$datestr = date('Y-m-d\TH:i:sO',$timestamp);
906	if($utc){
907		$eregStr =
908		'([0-9]{4})-'.	// centuries & years CCYY-
909		'([0-9]{2})-'.	// months MM-
910		'([0-9]{2})'.	// days DD
911		'T'.			// separator T
912		'([0-9]{2}):'.	// hours hh:
913		'([0-9]{2}):'.	// minutes mm:
914		'([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
915		'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
916
917		if(ereg($eregStr,$datestr,$regs)){
918			return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
919		}
920		return false;
921	} else {
922		return $datestr;
923	}
924}
925
926/**
927* convert ISO 8601 compliant date string to unix timestamp
928*
929* @param    string $datestr ISO 8601 compliant date string
930* @access   public
931*/
932function iso8601_to_timestamp($datestr){
933	$eregStr =
934	'([0-9]{4})-'.	// centuries & years CCYY-
935	'([0-9]{2})-'.	// months MM-
936	'([0-9]{2})'.	// days DD
937	'T'.			// separator T
938	'([0-9]{2}):'.	// hours hh:
939	'([0-9]{2}):'.	// minutes mm:
940	'([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
941	'(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
942	if(ereg($eregStr,$datestr,$regs)){
943		// not utc
944		if($regs[8] != 'Z'){
945			$op = substr($regs[8],0,1);
946			$h = substr($regs[8],1,2);
947			$m = substr($regs[8],strlen($regs[8])-2,2);
948			if($op == '-'){
949				$regs[4] = $regs[4] + $h;
950				$regs[5] = $regs[5] + $m;
951			} elseif($op == '+'){
952				$regs[4] = $regs[4] - $h;
953				$regs[5] = $regs[5] - $m;
954			}
955		}
956		return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
957//		return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
958	} else {
959		return false;
960	}
961}
962
963/**
964* sleeps some number of microseconds
965*
966* @param    string $usec the number of microseconds to sleep
967* @access   public
968* @deprecated
969*/
970function usleepWindows($usec)
971{
972	$start = gettimeofday();
973
974	do
975	{
976		$stop = gettimeofday();
977		$timePassed = 1000000 * ($stop['sec'] - $start['sec'])
978		+ $stop['usec'] - $start['usec'];
979	}
980	while ($timePassed < $usec);
981}
982
983?><?php
984
985
986
987/**
988* Contains information for a SOAP fault.
989* Mainly used for returning faults from deployed functions
990* in a server instance.
991* @author   Dietrich Ayala <dietrich@ganx4.com>
992* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
993* @access public
994*/
995class nusoap_fault extends nusoap_base {
996	/**
997	 * The fault code (client|server)
998	 * @var string
999	 * @access private
1000	 */
1001	var $faultcode;
1002	/**
1003	 * The fault actor
1004	 * @var string
1005	 * @access private
1006	 */
1007	var $faultactor;
1008	/**
1009	 * The fault string, a description of the fault
1010	 * @var string
1011	 * @access private
1012	 */
1013	var $faultstring;
1014	/**
1015	 * The fault detail, typically a string or array of string
1016	 * @var mixed
1017	 * @access private
1018	 */
1019	var $faultdetail;
1020
1021	/**
1022	* constructor
1023    *
1024    * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1025    * @param string $faultactor only used when msg routed between multiple actors
1026    * @param string $faultstring human readable error message
1027    * @param mixed $faultdetail detail, typically a string or array of string
1028	*/
1029	function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1030		parent::nusoap_base();
1031		$this->faultcode = $faultcode;
1032		$this->faultactor = $faultactor;
1033		$this->faultstring = $faultstring;
1034		$this->faultdetail = $faultdetail;
1035	}
1036
1037	/**
1038	* serialize a fault
1039	*
1040	* @return	string	The serialization of the fault instance.
1041	* @access   public
1042	*/
1043	function serialize(){
1044		$ns_string = '';
1045		foreach($this->namespaces as $k => $v){
1046			$ns_string .= "\n  xmlns:$k=\"$v\"";
1047		}
1048		$return_msg =
1049			'<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1050			'<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1051				'<SOAP-ENV:Body>'.
1052				'<SOAP-ENV:Fault>'.
1053					$this->serialize_val($this->faultcode, 'faultcode').
1054					$this->serialize_val($this->faultactor, 'faultactor').
1055					$this->serialize_val($this->faultstring, 'faultstring').
1056					$this->serialize_val($this->faultdetail, 'detail').
1057				'</SOAP-ENV:Fault>'.
1058				'</SOAP-ENV:Body>'.
1059			'</SOAP-ENV:Envelope>';
1060		return $return_msg;
1061	}
1062}
1063
1064/**
1065 * Backward compatibility
1066 */
1067class soap_fault extends nusoap_fault {
1068}
1069
1070?><?php
1071
1072
1073
1074/**
1075* parses an XML Schema, allows access to it's data, other utility methods.
1076* imperfect, no validation... yet, but quite functional.
1077*
1078* @author   Dietrich Ayala <dietrich@ganx4.com>
1079* @author   Scott Nichol <snichol@users.sourceforge.net>
1080* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
1081* @access   public
1082*/
1083class nusoap_xmlschema extends nusoap_base  {
1084
1085	// files
1086	var $schema = '';
1087	var $xml = '';
1088	// namespaces
1089	var $enclosingNamespaces;
1090	// schema info
1091	var $schemaInfo = array();
1092	var $schemaTargetNamespace = '';
1093	// types, elements, attributes defined by the schema
1094	var $attributes = array();
1095	var $complexTypes = array();
1096	var $complexTypeStack = array();
1097	var $currentComplexType = null;
1098	var $elements = array();
1099	var $elementStack = array();
1100	var $currentElement = null;
1101	var $simpleTypes = array();
1102	var $simpleTypeStack = array();
1103	var $currentSimpleType = null;
1104	// imports
1105	var $imports = array();
1106	// parser vars
1107	var $parser;
1108	var $position = 0;
1109	var $depth = 0;
1110	var $depth_array = array();
1111	var $message = array();
1112	var $defaultNamespace = array();
1113
1114	/**
1115	* constructor
1116	*
1117	* @param    string $schema schema document URI
1118	* @param    string $xml xml document URI
1119	* @param	string $namespaces namespaces defined in enclosing XML
1120	* @access   public
1121	*/
1122	function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1123		parent::nusoap_base();
1124		$this->debug('nusoap_xmlschema class instantiated, inside constructor');
1125		// files
1126		$this->schema = $schema;
1127		$this->xml = $xml;
1128
1129		// namespaces
1130		$this->enclosingNamespaces = $namespaces;
1131		$this->namespaces = array_merge($this->namespaces, $namespaces);
1132
1133		// parse schema file
1134		if($schema != ''){
1135			$this->debug('initial schema file: '.$schema);
1136			$this->parseFile($schema, 'schema');
1137		}
1138
1139		// parse xml file
1140		if($xml != ''){
1141			$this->debug('initial xml file: '.$xml);
1142			$this->parseFile($xml, 'xml');
1143		}
1144
1145	}
1146
1147    /**
1148    * parse an XML file
1149    *
1150    * @param string $xml path/URL to XML file
1151    * @param string $type (schema | xml)
1152	* @return boolean
1153    * @access public
1154    */
1155	function parseFile($xml,$type){
1156		// parse xml file
1157		if($xml != ""){
1158			$xmlStr = @join("",@file($xml));
1159			if($xmlStr == ""){
1160				$msg = 'Error reading XML from '.$xml;
1161				$this->setError($msg);
1162				$this->debug($msg);
1163			return false;
1164			} else {
1165				$this->debug("parsing $xml");
1166				$this->parseString($xmlStr,$type);
1167				$this->debug("done parsing $xml");
1168			return true;
1169			}
1170		}
1171		return false;
1172	}
1173
1174	/**
1175	* parse an XML string
1176	*
1177	* @param    string $xml path or URL
1178    * @param	string $type (schema|xml)
1179	* @access   private
1180	*/
1181	function parseString($xml,$type){
1182		// parse xml string
1183		if($xml != ""){
1184
1185	    	// Create an XML parser.
1186	    	$this->parser = xml_parser_create();
1187	    	// Set the options for parsing the XML data.
1188	    	xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1189
1190	    	// Set the object for the parser.
1191	    	xml_set_object($this->parser, $this);
1192
1193	    	// Set the element handlers for the parser.
1194			if($type == "schema"){
1195		    	xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1196		    	xml_set_character_data_handler($this->parser,'schemaCharacterData');
1197			} elseif($type == "xml"){
1198				xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1199		    	xml_set_character_data_handler($this->parser,'xmlCharacterData');
1200			}
1201
1202		    // Parse the XML file.
1203		    if(!xml_parse($this->parser,$xml,true)){
1204			// Display an error message.
1205				$errstr = sprintf('XML error parsing XML schema on line %d: %s',
1206				xml_get_current_line_number($this->parser),
1207				xml_error_string(xml_get_error_code($this->parser))
1208				);
1209				$this->debug($errstr);
1210				$this->debug("XML payload:\n" . $xml);
1211				$this->setError($errstr);
1212	    	}
1213
1214			xml_parser_free($this->parser);
1215		} else{
1216			$this->debug('no xml passed to parseString()!!');
1217			$this->setError('no xml passed to parseString()!!');
1218		}
1219	}
1220
1221	/**
1222	 * gets a type name for an unnamed type
1223	 *
1224	 * @param	string	Element name
1225	 * @return	string	A type name for an unnamed type
1226	 * @access	private
1227	 */
1228	function CreateTypeName($ename) {
1229		$scope = '';
1230		for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1231			$scope .= $this->complexTypeStack[$i] . '_';
1232		}
1233		return $scope . $ename . '_ContainedType';
1234	}
1235
1236	/**
1237	* start-element handler
1238	*
1239	* @param    string $parser XML parser object
1240	* @param    string $name element name
1241	* @param    string $attrs associative array of attributes
1242	* @access   private
1243	*/
1244	function schemaStartElement($parser, $name, $attrs) {
1245
1246		// position in the total number of elements, starting from 0
1247		$pos = $this->position++;
1248		$depth = $this->depth++;
1249		// set self as current value for this depth
1250		$this->depth_array[$depth] = $pos;
1251		$this->message[$pos] = array('cdata' => '');
1252		if ($depth > 0) {
1253			$this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1254		} else {
1255			$this->defaultNamespace[$pos] = false;
1256		}
1257
1258		// get element prefix
1259		if($prefix = $this->getPrefix($name)){
1260			// get unqualified name
1261			$name = $this->getLocalPart($name);
1262		} else {
1263        	$prefix = '';
1264        }
1265
1266        // loop thru attributes, expanding, and registering namespace declarations
1267        if(count($attrs) > 0){
1268        	foreach($attrs as $k => $v){
1269                // if ns declarations, add to class level array of valid namespaces
1270				if(ereg("^xmlns",$k)){
1271                	//$this->xdebug("$k: $v");
1272                	//$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1273                	if($ns_prefix = substr(strrchr($k,':'),1)){
1274                		//$this->xdebug("Add namespace[$ns_prefix] = $v");
1275						$this->namespaces[$ns_prefix] = $v;
1276					} else {
1277						$this->defaultNamespace[$pos] = $v;
1278						if (! $this->getPrefixFromNamespace($v)) {
1279							$this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1280						}
1281					}
1282					if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1283						$this->XMLSchemaVersion = $v;
1284						$this->namespaces['xsi'] = $v.'-instance';
1285					}
1286				}
1287        	}
1288        	foreach($attrs as $k => $v){
1289                // expand each attribute
1290                $k = strpos($k,':') ? $this->expandQname($k) : $k;
1291                $v = strpos($v,':') ? $this->expandQname($v) : $v;
1292        		$eAttrs[$k] = $v;
1293        	}
1294        	$attrs = $eAttrs;
1295        } else {
1296        	$attrs = array();
1297        }
1298		// find status, register data
1299		switch($name){
1300			case 'all':			// (optional) compositor content for a complexType
1301			case 'choice':
1302			case 'group':
1303			case 'sequence':
1304				//$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1305				$this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1306				//if($name == 'all' || $name == 'sequence'){
1307				//	$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1308				//}
1309			break;
1310			case 'attribute':	// complexType attribute
1311            	//$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1312            	$this->xdebug("parsing attribute:");
1313            	$this->appendDebug($this->varDump($attrs));
1314				if (!isset($attrs['form'])) {
1315					$attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1316				}
1317            	if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1318					$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1319					if (!strpos($v, ':')) {
1320						// no namespace in arrayType attribute value...
1321						if ($this->defaultNamespace[$pos]) {
1322							// ...so use the default
1323							$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1324						}
1325					}
1326            	}
1327                if(isset($attrs['name'])){
1328					$this->attributes[$attrs['name']] = $attrs;
1329					$aname = $attrs['name'];
1330				} elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1331					if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1332	                	$aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1333	                } else {
1334	                	$aname = '';
1335	                }
1336				} elseif(isset($attrs['ref'])){
1337					$aname = $attrs['ref'];
1338                    $this->attributes[$attrs['ref']] = $attrs;
1339				}
1340
1341				if($this->currentComplexType){	// This should *always* be
1342					$this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1343				}
1344				// arrayType attribute
1345				if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1346					$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1347                	$prefix = $this->getPrefix($aname);
1348					if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1349						$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1350					} else {
1351						$v = '';
1352					}
1353                    if(strpos($v,'[,]')){
1354                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1355                    }
1356                    $v = substr($v,0,strpos($v,'[')); // clip the []
1357                    if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1358                        $v = $this->XMLSchemaVersion.':'.$v;
1359                    }
1360                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1361				}
1362			break;
1363			case 'complexContent':	// (optional) content for a complexType
1364			break;
1365			case 'complexType':
1366				array_push($this->complexTypeStack, $this->currentComplexType);
1367				if(isset($attrs['name'])){
1368					// TODO: what is the scope of named complexTypes that appear
1369					//       nested within other c complexTypes?
1370					$this->xdebug('processing named complexType '.$attrs['name']);
1371					//$this->currentElement = false;
1372					$this->currentComplexType = $attrs['name'];
1373					$this->complexTypes[$this->currentComplexType] = $attrs;
1374					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1375					// This is for constructs like
1376					//           <complexType name="ListOfString" base="soap:Array">
1377					//                <sequence>
1378					//                    <element name="string" type="xsd:string"
1379					//                        minOccurs="0" maxOccurs="unbounded" />
1380					//                </sequence>
1381					//            </complexType>
1382					if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1383						$this->xdebug('complexType is unusual array');
1384						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1385					} else {
1386						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1387					}
1388				} else {
1389					$name = $this->CreateTypeName($this->currentElement);
1390					$this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1391					$this->currentComplexType = $name;
1392					//$this->currentElement = false;
1393					$this->complexTypes[$this->currentComplexType] = $attrs;
1394					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1395					// This is for constructs like
1396					//           <complexType name="ListOfString" base="soap:Array">
1397					//                <sequence>
1398					//                    <element name="string" type="xsd:string"
1399					//                        minOccurs="0" maxOccurs="unbounded" />
1400					//                </sequence>
1401					//            </complexType>
1402					if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1403						$this->xdebug('complexType is unusual array');
1404						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1405					} else {
1406						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1407					}
1408				}
1409			break;
1410			case 'element':
1411				array_push($this->elementStack, $this->currentElement);
1412				if (!isset($attrs['form'])) {
1413					$attrs['form'] = $this->schemaInfo['elementFormDefault'];
1414				}
1415				if(isset($attrs['type'])){
1416					$this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1417					if (! $this->getPrefix($attrs['type'])) {
1418						if ($this->defaultNamespace[$pos]) {
1419							$attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1420							$this->xdebug('used default namespace to make type ' . $attrs['type']);
1421						}
1422					}
1423					// This is for constructs like
1424					//           <complexType name="ListOfString" base="soap:Array">
1425					//                <sequence>
1426					//                    <element name="string" type="xsd:string"
1427					//                        minOccurs="0" maxOccurs="unbounded" />
1428					//                </sequence>
1429					//            </complexType>
1430					if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1431						$this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1432						$this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1433					}
1434					$this->currentElement = $attrs['name'];
1435					$ename = $attrs['name'];
1436				} elseif(isset($attrs['ref'])){
1437					$this->xdebug("processing element as ref to ".$attrs['ref']);
1438					$this->currentElement = "ref to ".$attrs['ref'];
1439					$ename = $this->getLocalPart($attrs['ref']);
1440				} else {
1441					$type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1442					$this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1443					$this->currentElement = $attrs['name'];
1444					$attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1445					$ename = $attrs['name'];
1446				}
1447				if (isset($ename) && $this->currentComplexType) {
1448					$this->xdebug("add element $ename to complexType $this->currentComplexType");
1449					$this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1450				} elseif (!isset($attrs['ref'])) {
1451					$this->xdebug("add element $ename to elements array");
1452					$this->elements[ $attrs['name'] ] = $attrs;
1453					$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1454				}
1455			break;
1456			case 'enumeration':	//	restriction value list member
1457				$this->xdebug('enumeration ' . $attrs['value']);
1458				if ($this->currentSimpleType) {
1459					$this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1460				} elseif ($this->currentComplexType) {
1461					$this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1462				}
1463			break;
1464			case 'extension':	// simpleContent or complexContent type extension
1465				$this->xdebug('extension ' . $attrs['base']);
1466				if ($this->currentComplexType) {
1467					$this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1468				}
1469			break;
1470			case 'import':
1471			    if (isset($attrs['schemaLocation'])) {
1472					//$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1473                    $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1474				} else {
1475					//$this->xdebug('import namespace ' . $attrs['namespace']);
1476                    $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1477					if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1478						$this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1479					}
1480				}
1481			break;
1482			case 'list':	// simpleType value list
1483			break;
1484			case 'restriction':	// simpleType, simpleContent or complexContent value restriction
1485				$this->xdebug('restriction ' . $attrs['base']);
1486				if($this->currentSimpleType){
1487					$this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1488				} elseif($this->currentComplexType){
1489					$this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1490					if(strstr($attrs['base'],':') == ':Array'){
1491						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1492					}
1493				}
1494			break;
1495			case 'schema':
1496				$this->schemaInfo = $attrs;
1497				$this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1498				if (isset($attrs['targetNamespace'])) {
1499					$this->schemaTargetNamespace = $attrs['targetNamespace'];
1500				}
1501				if (!isset($attrs['elementFormDefault'])) {
1502					$this->schemaInfo['elementFormDefault'] = 'unqualified';
1503				}
1504				if (!isset($attrs['attributeFormDefault'])) {
1505					$this->schemaInfo['attributeFormDefault'] = 'unqualified';
1506				}
1507			break;
1508			case 'simpleContent':	// (optional) content for a complexType
1509			break;
1510			case 'simpleType':
1511				array_push($this->simpleTypeStack, $this->currentSimpleType);
1512				if(isset($attrs['name'])){
1513					$this->xdebug("processing simpleType for name " . $attrs['name']);
1514					$this->currentSimpleType = $attrs['name'];
1515					$this->simpleTypes[ $attrs['name'] ] = $attrs;
1516					$this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1517					$this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1518				} else {
1519					$name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1520					$this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1521					$this->currentSimpleType = $name;
1522					//$this->currentElement = false;
1523					$this->simpleTypes[$this->currentSimpleType] = $attrs;
1524					$this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1525				}
1526			break;
1527			case 'union':	// simpleType type list
1528			break;
1529			default:
1530				//$this->xdebug("do not have anything to do for element $name");
1531		}
1532	}
1533
1534	/**
1535	* end-element handler
1536	*
1537	* @param    string $parser XML parser object
1538	* @param    string $name element name
1539	* @access   private
1540	*/
1541	function schemaEndElement($parser, $name) {
1542		// bring depth down a notch
1543		$this->depth--;
1544		// position of current element is equal to the last value left in depth_array for my depth
1545		if(isset($this->depth_array[$this->depth])){
1546        	$pos = $this->depth_array[$this->depth];
1547        }
1548		// get element prefix
1549		if ($prefix = $this->getPrefix($name)){
1550			// get unqualified name
1551			$name = $this->getLocalPart($name);
1552		} else {
1553        	$prefix = '';
1554        }
1555		// move on...
1556		if($name == 'complexType'){
1557			$this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1558			$this->currentComplexType = array_pop($this->complexTypeStack);
1559			//$this->currentElement = false;
1560		}
1561		if($name == 'element'){
1562			$this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1563			$this->currentElement = array_pop($this->elementStack);
1564		}
1565		if($name == 'simpleType'){
1566			$this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1567			$this->currentSimpleType = array_pop($this->simpleTypeStack);
1568		}
1569	}
1570
1571	/**
1572	* element content handler
1573	*
1574	* @param    string $parser XML parser object
1575	* @param    string $data element content
1576	* @access   private
1577	*/
1578	function schemaCharacterData($parser, $data){
1579		$pos = $this->depth_array[$this->depth - 1];
1580		$this->message[$pos]['cdata'] .= $data;
1581	}
1582
1583	/**
1584	* serialize the schema
1585	*
1586	* @access   public
1587	*/
1588	function serializeSchema(){
1589
1590		$schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1591		$xml = '';
1592		// imports
1593		if (sizeof($this->imports) > 0) {
1594			foreach($this->imports as $ns => $list) {
1595				foreach ($list as $ii) {
1596					if ($ii['location'] != '') {
1597						$xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1598					} else {
1599						$xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1600					}
1601				}
1602			}
1603		}
1604		// complex types
1605		foreach($this->complexTypes as $typeName => $attrs){
1606			$contentStr = '';
1607			// serialize child elements
1608			if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1609				foreach($attrs['elements'] as $element => $eParts){
1610					if(isset($eParts['ref'])){
1611						$contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1612					} else {
1613						$contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1614						foreach ($eParts as $aName => $aValue) {
1615							// handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1616							if ($aName != 'name' && $aName != 'type') {
1617								$contentStr .= " $aName=\"$aValue\"";
1618							}
1619						}
1620						$contentStr .= "/>\n";
1621					}
1622				}
1623				// compositor wraps elements
1624				if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1625					$contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1626				}
1627			}
1628			// attributes
1629			if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1630				foreach($attrs['attrs'] as $attr => $aParts){
1631					$contentStr .= "    <$schemaPrefix:attribute";
1632					foreach ($aParts as $a => $v) {
1633						if ($a == 'ref' || $a == 'type') {
1634							$contentStr .= " $a=\"".$this->contractQName($v).'"';
1635						} elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1636							$this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1637							$contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1638						} else {
1639							$contentStr .= " $a=\"$v\"";
1640						}
1641					}
1642					$contentStr .= "/>\n";
1643				}
1644			}
1645			// if restriction
1646			if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1647				$contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1648				// complex or simple content
1649				if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1650					$contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1651				}
1652			}
1653			// finalize complex type
1654			if($contentStr != ''){
1655				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1656			} else {
1657				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1658			}
1659			$xml .= $contentStr;
1660		}
1661		// simple types
1662		if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1663			foreach($this->simpleTypes as $typeName => $eParts){
1664				$xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1665				if (isset($eParts['enumeration'])) {
1666					foreach ($eParts['enumeration'] as $e) {
1667						$xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1668					}
1669				}
1670				$xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1671			}
1672		}
1673		// elements
1674		if(isset($this->elements) && count($this->elements) > 0){
1675			foreach($this->elements as $element => $eParts){
1676				$xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1677			}
1678		}
1679		// attributes
1680		if(isset($this->attributes) && count($this->attributes) > 0){
1681			foreach($this->attributes as $attr => $aParts){
1682				$xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1683			}
1684		}
1685		// finish 'er up
1686		$attr = '';
1687		foreach ($this->schemaInfo as $k => $v) {
1688			if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1689				$attr .= " $k=\"$v\"";
1690			}
1691		}
1692		$el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1693		foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1694			$el .= " xmlns:$nsp=\"$ns\"";
1695		}
1696		$xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1697		return $xml;
1698	}
1699
1700	/**
1701	* adds debug data to the clas level debug string
1702	*
1703	* @param    string $string debug data
1704	* @access   private
1705	*/
1706	function xdebug($string){
1707		$this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1708	}
1709
1710    /**
1711    * get the PHP type of a user defined type in the schema
1712    * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1713    * returns false if no type exists, or not w/ the given namespace
1714    * else returns a string that is either a native php type, or 'struct'
1715    *
1716    * @param string $type name of defined type
1717    * @param string $ns namespace of type
1718    * @return mixed
1719    * @access public
1720    * @deprecated
1721    */
1722	function getPHPType($type,$ns){
1723		if(isset($this->typemap[$ns][$type])){
1724			//print "found type '$type' and ns $ns in typemap<br>";
1725			return $this->typemap[$ns][$type];
1726		} elseif(isset($this->complexTypes[$type])){
1727			//print "getting type '$type' and ns $ns from complexTypes array<br>";
1728			return $this->complexTypes[$type]['phpType'];
1729		}
1730		return false;
1731	}
1732
1733	/**
1734    * returns an associative array of information about a given type
1735    * returns false if no type exists by the given name
1736    *
1737	*	For a complexType typeDef = array(
1738	*	'restrictionBase' => '',
1739	*	'phpType' => '',
1740	*	'compositor' => '(sequence|all)',
1741	*	'elements' => array(), // refs to elements array
1742	*	'attrs' => array() // refs to attributes array
1743	*	... and so on (see addComplexType)
1744	*	)
1745	*
1746	*   For simpleType or element, the array has different keys.
1747    *
1748    * @param string $type
1749    * @return mixed
1750    * @access public
1751    * @see addComplexType
1752    * @see addSimpleType
1753    * @see addElement
1754    */
1755	function getTypeDef($type){
1756		//$this->debug("in getTypeDef for type $type");
1757		if (substr($type, -1) == '^') {
1758			$is_element = 1;
1759			$type = substr($type, 0, -1);
1760		} else {
1761			$is_element = 0;
1762		}
1763
1764		if((! $is_element) && isset($this->complexTypes[$type])){
1765			$this->xdebug("in getTypeDef, found complexType $type");
1766			return $this->complexTypes[$type];
1767		} elseif((! $is_element) && isset($this->simpleTypes[$type])){
1768			$this->xdebug("in getTypeDef, found simpleType $type");
1769			if (!isset($this->simpleTypes[$type]['phpType'])) {
1770				// get info for type to tack onto the simple type
1771				// TODO: can this ever really apply (i.e. what is a simpleType really?)
1772				$uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1773				$ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1774				$etype = $this->getTypeDef($uqType);
1775				if ($etype) {
1776					$this->xdebug("in getTypeDef, found type for simpleType $type:");
1777					$this->xdebug($this->varDump($etype));
1778					if (isset($etype['phpType'])) {
1779						$this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1780					}
1781					if (isset($etype['elements'])) {
1782						$this->simpleTypes[$type]['elements'] = $etype['elements'];
1783					}
1784				}
1785			}
1786			return $this->simpleTypes[$type];
1787		} elseif(isset($this->elements[$type])){
1788			$this->xdebug("in getTypeDef, found element $type");
1789			if (!isset($this->elements[$type]['phpType'])) {
1790				// get info for type to tack onto the element
1791				$uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1792				$ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1793				$etype = $this->getTypeDef($uqType);
1794				if ($etype) {
1795					$this->xdebug("in getTypeDef, found type for element $type:");
1796					$this->xdebug($this->varDump($etype));
1797					if (isset($etype['phpType'])) {
1798						$this->elements[$type]['phpType'] = $etype['phpType'];
1799					}
1800					if (isset($etype['elements'])) {
1801						$this->elements[$type]['elements'] = $etype['elements'];
1802					}
1803				} elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1804					$this->xdebug("in getTypeDef, element $type is an XSD type");
1805					$this->elements[$type]['phpType'] = 'scalar';
1806				}
1807			}
1808			return $this->elements[$type];
1809		} elseif(isset($this->attributes[$type])){
1810			$this->xdebug("in getTypeDef, found attribute $type");
1811			return $this->attributes[$type];
1812		} elseif (ereg('_ContainedType$', $type)) {
1813			$this->xdebug("in getTypeDef, have an untyped element $type");
1814			$typeDef['typeClass'] = 'simpleType';
1815			$typeDef['phpType'] = 'scalar';
1816			$typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1817			return $typeDef;
1818		}
1819		$this->xdebug("in getTypeDef, did not find $type");
1820		return false;
1821	}
1822
1823	/**
1824    * returns a sample serialization of a given type, or false if no type by the given name
1825    *
1826    * @param string $type name of type
1827    * @return mixed
1828    * @access public
1829    * @deprecated
1830    */
1831    function serializeTypeDef($type){
1832    	//print "in sTD() for type $type<br>";
1833	if($typeDef = $this->getTypeDef($type)){
1834		$str .= '<'.$type;
1835	    if(is_array($typeDef['attrs'])){
1836		foreach($typeDef['attrs'] as $attName => $data){
1837		    $str .= " $attName=\"{type = ".$data['type']."}\"";
1838		}
1839	    }
1840	    $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1841	    if(count($typeDef['elements']) > 0){
1842		$str .= ">";
1843		foreach($typeDef['elements'] as $element => $eData){
1844		    $str .= $this->serializeTypeDef($element);
1845		}
1846		$str .= "</$type>";
1847	    } elseif($typeDef['typeClass'] == 'element') {
1848		$str .= "></$type>";
1849	    } else {
1850		$str .= "/>";
1851	    }
1852			return $str;
1853	}
1854    	return false;
1855    }
1856
1857    /**
1858    * returns HTML form elements that allow a user
1859    * to enter values for creating an instance of the given type.
1860    *
1861    * @param string $name name for type instance
1862    * @param string $type name of type
1863    * @return string
1864    * @access public
1865    * @deprecated
1866	*/
1867	function typeToForm($name,$type){
1868		// get typedef
1869		if($typeDef = $this->getTypeDef($type)){
1870			// if struct
1871			if($typeDef['phpType'] == 'struct'){
1872				$buffer .= '<table>';
1873				foreach($typeDef['elements'] as $child => $childDef){
1874					$buffer .= "
1875					<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1876					<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1877				}
1878				$buffer .= '</table>';
1879			// if array
1880			} elseif($typeDef['phpType'] == 'array'){
1881				$buffer .= '<table>';
1882				for($i=0;$i < 3; $i++){
1883					$buffer .= "
1884					<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1885					<td><input type='text' name='parameters[".$name."][]'></td></tr>";
1886				}
1887				$buffer .= '</table>';
1888			// if scalar
1889			} else {
1890				$buffer .= "<input type='text' name='parameters[$name]'>";
1891			}
1892		} else {
1893			$buffer .= "<input type='text' name='parameters[$name]'>";
1894		}
1895		return $buffer;
1896	}
1897
1898	/**
1899	* adds a complex type to the schema
1900	*
1901	* example: array
1902	*
1903	* addType(
1904	* 	'ArrayOfstring',
1905	* 	'complexType',
1906	* 	'array',
1907	* 	'',
1908	* 	'SOAP-ENC:Array',
1909	* 	array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1910	* 	'xsd:string'
1911	* );
1912	*
1913	* example: PHP associative array ( SOAP Struct )
1914	*
1915	* addType(
1916	* 	'SOAPStruct',
1917	* 	'complexType',
1918	* 	'struct',
1919	* 	'all',
1920	* 	array('myVar'=> array('name'=>'myVar','type'=>'string')
1921	* );
1922	*
1923	* @param name
1924	* @param typeClass (complexType|simpleType|attribute)
1925	* @param phpType: currently supported are array and struct (php assoc array)
1926	* @param compositor (all|sequence|choice)
1927	* @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1928	* @param elements = array ( name = array(name=>'',type=>'') )
1929	* @param attrs = array(
1930	* 	array(
1931	*		'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1932	*		"http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1933	* 	)
1934	* )
1935	* @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1936	* @access public
1937	* @see getTypeDef
1938	*/
1939	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1940		$this->complexTypes[$name] = array(
1941	    'name'		=> $name,
1942	    'typeClass'	=> $typeClass,
1943	    'phpType'	=> $phpType,
1944		'compositor'=> $compositor,
1945	    'restrictionBase' => $restrictionBase,
1946		'elements'	=> $elements,
1947	    'attrs'		=> $attrs,
1948	    'arrayType'	=> $arrayType
1949		);
1950
1951		$this->xdebug("addComplexType $name:");
1952		$this->appendDebug($this->varDump($this->complexTypes[$name]));
1953	}
1954
1955	/**
1956	* adds a simple type to the schema
1957	*
1958	* @param string $name
1959	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1960	* @param string $typeClass (should always be simpleType)
1961	* @param string $phpType (should always be scalar)
1962	* @param array $enumeration array of values
1963	* @access public
1964	* @see nusoap_xmlschema
1965	* @see getTypeDef
1966	*/
1967	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1968		$this->simpleTypes[$name] = array(
1969	    'name'			=> $name,
1970	    'typeClass'		=> $typeClass,
1971	    'phpType'		=> $phpType,
1972	    'type'			=> $restrictionBase,
1973	    'enumeration'	=> $enumeration
1974		);
1975
1976		$this->xdebug("addSimpleType $name:");
1977		$this->appendDebug($this->varDump($this->simpleTypes[$name]));
1978	}
1979
1980	/**
1981	* adds an element to the schema
1982	*
1983	* @param array $attrs attributes that must include name and type
1984	* @see nusoap_xmlschema
1985	* @access public
1986	*/
1987	function addElement($attrs) {
1988		if (! $this->getPrefix($attrs['type'])) {
1989			$attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1990		}
1991		$this->elements[ $attrs['name'] ] = $attrs;
1992		$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1993
1994		$this->xdebug("addElement " . $attrs['name']);
1995		$this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1996	}
1997}
1998
1999/**
2000 * Backward compatibility
2001 */
2002class XMLSchema extends nusoap_xmlschema {
2003}
2004
2005?><?php
2006
2007
2008
2009/**
2010* For creating serializable abstractions of native PHP types.  This class
2011* allows element name/namespace, XSD type, and XML attributes to be
2012* associated with a value.  This is extremely useful when WSDL is not
2013* used, but is also useful when WSDL is used with polymorphic types, including
2014* xsd:anyType and user-defined types.
2015*
2016* @author   Dietrich Ayala <dietrich@ganx4.com>
2017* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2018* @access   public
2019*/
2020class soapval extends nusoap_base {
2021	/**
2022	 * The XML element name
2023	 *
2024	 * @var string
2025	 * @access private
2026	 */
2027	var $name;
2028	/**
2029	 * The XML type name (string or false)
2030	 *
2031	 * @var mixed
2032	 * @access private
2033	 */
2034	var $type;
2035	/**
2036	 * The PHP value
2037	 *
2038	 * @var mixed
2039	 * @access private
2040	 */
2041	var $value;
2042	/**
2043	 * The XML element namespace (string or false)
2044	 *
2045	 * @var mixed
2046	 * @access private
2047	 */
2048	var $element_ns;
2049	/**
2050	 * The XML type namespace (string or false)
2051	 *
2052	 * @var mixed
2053	 * @access private
2054	 */
2055	var $type_ns;
2056	/**
2057	 * The XML element attributes (array or false)
2058	 *
2059	 * @var mixed
2060	 * @access private
2061	 */
2062	var $attributes;
2063
2064	/**
2065	* constructor
2066	*
2067	* @param    string $name optional name
2068	* @param    mixed $type optional type name
2069	* @param	mixed $value optional value
2070	* @param	mixed $element_ns optional namespace of value
2071	* @param	mixed $type_ns optional namespace of type
2072	* @param	mixed $attributes associative array of attributes to add to element serialization
2073	* @access   public
2074	*/
2075  	function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2076		parent::nusoap_base();
2077		$this->name = $name;
2078		$this->type = $type;
2079		$this->value = $value;
2080		$this->element_ns = $element_ns;
2081		$this->type_ns = $type_ns;
2082		$this->attributes = $attributes;
2083    }
2084
2085	/**
2086	* return serialized value
2087	*
2088	* @param	string $use The WSDL use value (encoded|literal)
2089	* @return	string XML data
2090	* @access   public
2091	*/
2092	function serialize($use='encoded') {
2093		return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2094    }
2095
2096	/**
2097	* decodes a soapval object into a PHP native type
2098	*
2099	* @return	mixed
2100	* @access   public
2101	*/
2102	function decode(){
2103		return $this->value;
2104	}
2105}
2106
2107
2108
2109?><?php
2110
2111
2112
2113/**
2114* transport class for sending/receiving data via HTTP and HTTPS
2115* NOTE: PHP must be compiled with the CURL extension for HTTPS support
2116*
2117* @author   Dietrich Ayala <dietrich@ganx4.com>
2118* @author   Scott Nichol <snichol@users.sourceforge.net>
2119* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2120* @access public
2121*/
2122class soap_transport_http extends nusoap_base {
2123
2124	var $url = '';
2125	var $uri = '';
2126	var $digest_uri = '';
2127	var $scheme = '';
2128	var $host = '';
2129	var $port = '';
2130	var $path = '';
2131	var $request_method = 'POST';
2132	var $protocol_version = '1.0';
2133	var $encoding = '';
2134	var $outgoing_headers = array();
2135	var $incoming_headers = array();
2136	var $incoming_cookies = array();
2137	var $outgoing_payload = '';
2138	var $incoming_payload = '';
2139	var $response_status_line;	// HTTP response status line
2140	var $useSOAPAction = true;
2141	var $persistentConnection = false;
2142	var $ch = false;	// cURL handle
2143	var $ch_options = array();	// cURL custom options
2144	var $use_curl = false;		// force cURL use
2145	var $proxy = null;			// proxy information (associative array)
2146	var $username = '';
2147	var $password = '';
2148	var $authtype = '';
2149	var $digestRequest = array();
2150	var $certRequest = array();	// keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2151								// cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2152								// sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2153								// sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2154								// passphrase: SSL key password/passphrase
2155								// certpassword: SSL certificate password
2156								// verifypeer: default is 1
2157								// verifyhost: default is 1
2158
2159	/**
2160	* constructor
2161	*
2162	* @param string $url The URL to which to connect
2163	* @param array $curl_options User-specified cURL options
2164	* @param boolean $use_curl Whether to try to force cURL use
2165	* @access public
2166	*/
2167	function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2168		parent::nusoap_base();
2169		$this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2170		$this->appendDebug($this->varDump($curl_options));
2171		$this->setURL($url);
2172		if (is_array($curl_options)) {
2173			$this->ch_options = $curl_options;
2174		}
2175		$this->use_curl = $use_curl;
2176		ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2177		$this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2178	}
2179
2180	/**
2181	* sets a cURL option
2182	*
2183	* @param	mixed $option The cURL option (always integer?)
2184	* @param	mixed $value The cURL option value
2185	* @access   private
2186	*/
2187	function setCurlOption($option, $value) {
2188		$this->debug("setCurlOption option=$option, value=");
2189		$this->appendDebug($this->varDump($value));
2190		curl_setopt($this->ch, $option, $value);
2191	}
2192
2193	/**
2194	* sets an HTTP header
2195	*
2196	* @param string $name The name of the header
2197	* @param string $value The value of the header
2198	* @access private
2199	*/
2200	function setHeader($name, $value) {
2201		$this->outgoing_headers[$name] = $value;
2202		$this->debug("set header $name: $value");
2203	}
2204
2205	/**
2206	* unsets an HTTP header
2207	*
2208	* @param string $name The name of the header
2209	* @access private
2210	*/
2211	function unsetHeader($name) {
2212		if (isset($this->outgoing_headers[$name])) {
2213			$this->debug("unset header $name");
2214			unset($this->outgoing_headers[$name]);
2215		}
2216	}
2217
2218	/**
2219	* sets the URL to which to connect
2220	*
2221	* @param string $url The URL to which to connect
2222	* @access private
2223	*/
2224	function setURL($url) {
2225		$this->url = $url;
2226
2227		$u = parse_url($url);
2228		foreach($u as $k => $v){
2229			$this->debug("parsed URL $k = $v");
2230			$this->$k = $v;
2231		}
2232
2233		// add any GET params to path
2234		if(isset($u['query']) && $u['query'] != ''){
2235            $this->path .= '?' . $u['query'];
2236		}
2237
2238		// set default port
2239		if(!isset($u['port'])){
2240			if($u['scheme'] == 'https'){
2241				$this->port = 443;
2242			} else {
2243				$this->port = 80;
2244			}
2245		}
2246
2247		$this->uri = $this->path;
2248		$this->digest_uri = $this->uri;
2249
2250		// build headers
2251		if (!isset($u['port'])) {
2252			$this->setHeader('Host', $this->host);
2253		} else {
2254			$this->setHeader('Host', $this->host.':'.$this->port);
2255		}
2256
2257		if (isset($u['user']) && $u['user'] != '') {
2258			$this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2259		}
2260	}
2261
2262	/**
2263	* gets the I/O method to use
2264	*
2265	* @return	string	I/O method to use (socket|curl|unknown)
2266	* @access	private
2267	*/
2268	function io_method() {
2269		if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2270			return 'curl';
2271		if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2272			return 'socket';
2273		return 'unknown';
2274	}
2275
2276	/**
2277	* establish an HTTP connection
2278	*
2279	* @param    integer $timeout set connection timeout in seconds
2280	* @param	integer $response_timeout set response timeout in seconds
2281	* @return	boolean true if connected, false if not
2282	* @access   private
2283	*/
2284	function connect($connection_timeout=0,$response_timeout=30){
2285	  	// For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2286	  	// "regular" socket.
2287	  	// TODO: disabled for now because OpenSSL must be *compiled* in (not just
2288	  	//       loaded), and until PHP5 stream_get_wrappers is not available.
2289//	  	if ($this->scheme == 'https') {
2290//		  	if (version_compare(phpversion(), '4.3.0') >= 0) {
2291//		  		if (extension_loaded('openssl')) {
2292//		  			$this->scheme = 'ssl';
2293//		  			$this->debug('Using SSL over OpenSSL');
2294//		  		}
2295//		  	}
2296//		}
2297		$this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2298	  if ($this->io_method() == 'socket') {
2299		if (!is_array($this->proxy)) {
2300			$host = $this->host;
2301			$port = $this->port;
2302		} else {
2303			$host = $this->proxy['host'];
2304			$port = $this->proxy['port'];
2305		}
2306
2307		// use persistent connection
2308		if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2309			if (!feof($this->fp)) {
2310				$this->debug('Re-use persistent connection');
2311				return true;
2312			}
2313			fclose($this->fp);
2314			$this->debug('Closed persistent connection at EOF');
2315		}
2316
2317		// munge host if using OpenSSL
2318		if ($this->scheme == 'ssl') {
2319			$host = 'ssl://' . $host;
2320		}
2321		$this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2322
2323		// open socket
2324		if($connection_timeout > 0){
2325			$this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2326		} else {
2327			$this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2328		}
2329
2330		// test pointer
2331		if(!$this->fp) {
2332			$msg = 'Couldn\'t open socket connection to server ' . $this->url;
2333			if ($this->errno) {
2334				$msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2335			} else {
2336				$msg .= ' prior to connect().  This is often a problem looking up the host name.';
2337			}
2338			$this->debug($msg);
2339			$this->setError($msg);
2340			return false;
2341		}
2342
2343		// set response timeout
2344		$this->debug('set response timeout to ' . $response_timeout);
2345		socket_set_timeout( $this->fp, $response_timeout);
2346
2347		$this->debug('socket connected');
2348		return true;
2349	  } else if ($this->io_method() == 'curl') {
2350		if (!extension_loaded('curl')) {
2351//			$this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2352			$this->setError('The PHP cURL Extension is required for HTTPS or NLTM.  You will need to re-build or update your PHP to included cURL.');
2353			return false;
2354		}
2355		// Avoid warnings when PHP does not have these options
2356		if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2357			$CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2358		else
2359			$CURLOPT_CONNECTIONTIMEOUT = 78;
2360		if (defined('CURLOPT_HTTPAUTH'))
2361			$CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2362		else
2363			$CURLOPT_HTTPAUTH = 107;
2364		if (defined('CURLOPT_PROXYAUTH'))
2365			$CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2366		else
2367			$CURLOPT_PROXYAUTH = 111;
2368		if (defined('CURLAUTH_BASIC'))
2369			$CURLAUTH_BASIC = CURLAUTH_BASIC;
2370		else
2371			$CURLAUTH_BASIC = 1;
2372		if (defined('CURLAUTH_DIGEST'))
2373			$CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2374		else
2375			$CURLAUTH_DIGEST = 2;
2376		if (defined('CURLAUTH_NTLM'))
2377			$CURLAUTH_NTLM = CURLAUTH_NTLM;
2378		else
2379			$CURLAUTH_NTLM = 8;
2380
2381		$this->debug('connect using cURL');
2382		// init CURL
2383		$this->ch = curl_init();
2384		// set url
2385		$hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2386		// add path
2387		$hostURL .= $this->path;
2388		$this->setCurlOption(CURLOPT_URL, $hostURL);
2389		// follow location headers (re-directs)
2390		if (ini_get('safe_mode') || ini_get('open_basedir')) {
2391			$this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2392			$this->debug('safe_mode = ');
2393			$this->appendDebug($this->varDump(ini_get('safe_mode')));
2394			$this->debug('open_basedir = ');
2395			$this->appendDebug($this->varDump(ini_get('open_basedir')));
2396		} else {
2397			$this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2398		}
2399		// ask for headers in the response output
2400		$this->setCurlOption(CURLOPT_HEADER, 1);
2401		// ask for the response output as the return value
2402		$this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2403		// encode
2404		// We manage this ourselves through headers and encoding
2405//		if(function_exists('gzuncompress')){
2406//			$this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2407//		}
2408		// persistent connection
2409		if ($this->persistentConnection) {
2410			// I believe the following comment is now bogus, having applied to
2411			// the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2412			// The way we send data, we cannot use persistent connections, since
2413			// there will be some "junk" at the end of our request.
2414			//$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2415			$this->persistentConnection = false;
2416			$this->setHeader('Connection', 'close');
2417		}
2418		// set timeouts
2419		if ($connection_timeout != 0) {
2420			$this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2421		}
2422		if ($response_timeout != 0) {
2423			$this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2424		}
2425
2426		if ($this->scheme == 'https') {
2427			$this->debug('set cURL SSL verify options');
2428			// recent versions of cURL turn on peer/host checking by default,
2429			// while PHP binaries are not compiled with a default location for the
2430			// CA cert bundle, so disable peer/host checking.
2431			//$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2432			$this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2433			$this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2434
2435			// support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2436			if ($this->authtype == 'certificate') {
2437				$this->debug('set cURL certificate options');
2438				if (isset($this->certRequest['cainfofile'])) {
2439					$this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2440				}
2441				if (isset($this->certRequest['verifypeer'])) {
2442					$this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2443				} else {
2444					$this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2445				}
2446				if (isset($this->certRequest['verifyhost'])) {
2447					$this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2448				} else {
2449					$this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2450				}
2451				if (isset($this->certRequest['sslcertfile'])) {
2452					$this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2453				}
2454				if (isset($this->certRequest['sslkeyfile'])) {
2455					$this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2456				}
2457				if (isset($this->certRequest['passphrase'])) {
2458					$this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2459				}
2460				if (isset($this->certRequest['certpassword'])) {
2461					$this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2462				}
2463			}
2464		}
2465		if ($this->authtype && ($this->authtype != 'certificate')) {
2466			if ($this->username) {
2467				$this->debug('set cURL username/password');
2468				$this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2469			}
2470			if ($this->authtype == 'basic') {
2471				$this->debug('set cURL for Basic authentication');
2472				$this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2473			}
2474			if ($this->authtype == 'digest') {
2475				$this->debug('set cURL for digest authentication');
2476				$this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2477			}
2478			if ($this->authtype == 'ntlm') {
2479				$this->debug('set cURL for NTLM authentication');
2480				$this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2481			}
2482		}
2483		if (is_array($this->proxy)) {
2484			$this->debug('set cURL proxy options');
2485			if ($this->proxy['port'] != '') {
2486				$this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2487			} else {
2488				$this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2489			}
2490			if ($this->proxy['username'] || $this->proxy['password']) {
2491				$this->debug('set cURL proxy authentication options');
2492				$this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2493				if ($this->proxy['authtype'] == 'basic') {
2494					$this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2495				}
2496				if ($this->proxy['authtype'] == 'ntlm') {
2497					$this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2498				}
2499			}
2500		}
2501		$this->debug('cURL connection set up');
2502		return true;
2503	  } else {
2504		$this->setError('Unknown scheme ' . $this->scheme);
2505		$this->debug('Unknown scheme ' . $this->scheme);
2506		return false;
2507	  }
2508	}
2509
2510	/**
2511	* sends the SOAP request and gets the SOAP response via HTTP[S]
2512	*
2513	* @param    string $data message data
2514	* @param    integer $timeout set connection timeout in seconds
2515	* @param	integer $response_timeout set response timeout in seconds
2516	* @param	array $cookies cookies to send
2517	* @return	string data
2518	* @access   public
2519	*/
2520	function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2521
2522		$this->debug('entered send() with data of length: '.strlen($data));
2523
2524		$this->tryagain = true;
2525		$tries = 0;
2526		while ($this->tryagain) {
2527			$this->tryagain = false;
2528			if ($tries++ < 2) {
2529				// make connnection
2530				if (!$this->connect($timeout, $response_timeout)){
2531					return false;
2532				}
2533
2534				// send request
2535				if (!$this->sendRequest($data, $cookies)){
2536					return false;
2537				}
2538
2539				// get response
2540				$respdata = $this->getResponse();
2541			} else {
2542				$this->setError("Too many tries to get an OK response ($this->response_status_line)");
2543			}
2544		}
2545		$this->debug('end of send()');
2546		return $respdata;
2547	}
2548
2549
2550	/**
2551	* sends the SOAP request and gets the SOAP response via HTTPS using CURL
2552	*
2553	* @param    string $data message data
2554	* @param    integer $timeout set connection timeout in seconds
2555	* @param	integer $response_timeout set response timeout in seconds
2556	* @param	array $cookies cookies to send
2557	* @return	string data
2558	* @access   public
2559	* @deprecated
2560	*/
2561	function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2562		return $this->send($data, $timeout, $response_timeout, $cookies);
2563	}
2564
2565	/**
2566	* if authenticating, set user credentials here
2567	*
2568	* @param    string $username
2569	* @param    string $password
2570	* @param	string $authtype (basic|digest|certificate|ntlm)
2571	* @param	array $digestRequest (keys must be nonce, nc, realm, qop)
2572	* @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2573	* @access   public
2574	*/
2575	function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2576		$this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2577		$this->appendDebug($this->varDump($digestRequest));
2578		$this->debug("certRequest=");
2579		$this->appendDebug($this->varDump($certRequest));
2580		// cf. RFC 2617
2581		if ($authtype == 'basic') {
2582			$this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2583		} elseif ($authtype == 'digest') {
2584			if (isset($digestRequest['nonce'])) {
2585				$digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2586
2587				// calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2588
2589				// A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2590				$A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2591
2592				// H(A1) = MD5(A1)
2593				$HA1 = md5($A1);
2594
2595				// A2 = Method ":" digest-uri-value
2596				$A2 = $this->request_method . ':' . $this->digest_uri;
2597
2598				// H(A2)
2599				$HA2 =  md5($A2);
2600
2601				// KD(secret, data) = H(concat(secret, ":", data))
2602				// if qop == auth:
2603				// request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2604				//                              ":" nc-value
2605				//                              ":" unq(cnonce-value)
2606				//                              ":" unq(qop-value)
2607				//                              ":" H(A2)
2608				//                            ) <">
2609				// if qop is missing,
2610				// request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2611
2612				$unhashedDigest = '';
2613				$nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2614				$cnonce = $nonce;
2615				if ($digestRequest['qop'] != '') {
2616					$unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2617				} else {
2618					$unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2619				}
2620
2621				$hashedDigest = md5($unhashedDigest);
2622
2623				$opaque = '';
2624				if (isset($digestRequest['opaque'])) {
2625					$opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2626				}
2627
2628				$this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2629			}
2630		} elseif ($authtype == 'certificate') {
2631			$this->certRequest = $certRequest;
2632			$this->debug('Authorization header not set for certificate');
2633		} elseif ($authtype == 'ntlm') {
2634			// do nothing
2635			$this->debug('Authorization header not set for ntlm');
2636		}
2637		$this->username = $username;
2638		$this->password = $password;
2639		$this->authtype = $authtype;
2640		$this->digestRequest = $digestRequest;
2641	}
2642
2643	/**
2644	* set the soapaction value
2645	*
2646	* @param    string $soapaction
2647	* @access   public
2648	*/
2649	function setSOAPAction($soapaction) {
2650		$this->setHeader('SOAPAction', '"' . $soapaction . '"');
2651	}
2652
2653	/**
2654	* use http encoding
2655	*
2656	* @param    string $enc encoding style. supported values: gzip, deflate, or both
2657	* @access   public
2658	*/
2659	function setEncoding($enc='gzip, deflate') {
2660		if (function_exists('gzdeflate')) {
2661			$this->protocol_version = '1.1';
2662			$this->setHeader('Accept-Encoding', $enc);
2663			if (!isset($this->outgoing_headers['Connection'])) {
2664				$this->setHeader('Connection', 'close');
2665				$this->persistentConnection = false;
2666			}
2667			set_magic_quotes_runtime(0);
2668			// deprecated
2669			$this->encoding = $enc;
2670		}
2671	}
2672
2673	/**
2674	* set proxy info here
2675	*
2676	* @param    string $proxyhost use an empty string to remove proxy
2677	* @param    string $proxyport
2678	* @param	string $proxyusername
2679	* @param	string $proxypassword
2680	* @param	string $proxyauthtype (basic|ntlm)
2681	* @access   public
2682	*/
2683	function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2684		if ($proxyhost) {
2685			$this->proxy = array(
2686				'host' => $proxyhost,
2687				'port' => $proxyport,
2688				'username' => $proxyusername,
2689				'password' => $proxypassword,
2690				'authtype' => $proxyauthtype
2691			);
2692			if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2693				$this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2694			}
2695		} else {
2696			$this->debug('remove proxy');
2697			$proxy = null;
2698			unsetHeader('Proxy-Authorization');
2699		}
2700	}
2701
2702
2703	/**
2704	 * Test if the given string starts with a header that is to be skipped.
2705	 * Skippable headers result from chunked transfer and proxy requests.
2706	 *
2707	 * @param	string $data The string to check.
2708	 * @returns	boolean	Whether a skippable header was found.
2709	 * @access	private
2710	 */
2711	function isSkippableCurlHeader(&$data) {
2712		$skipHeaders = array(	'HTTP/1.1 100',
2713								'HTTP/1.0 301',
2714								'HTTP/1.1 301',
2715								'HTTP/1.0 302',
2716								'HTTP/1.1 302',
2717								'HTTP/1.0 401',
2718								'HTTP/1.1 401',
2719								'HTTP/1.0 200 Connection established');
2720		foreach ($skipHeaders as $hd) {
2721			$prefix = substr($data, 0, strlen($hd));
2722			if ($prefix == $hd) return true;
2723		}
2724
2725		return false;
2726	}
2727
2728	/**
2729	* decode a string that is encoded w/ "chunked' transfer encoding
2730 	* as defined in RFC2068 19.4.6
2731	*
2732	* @param    string $buffer
2733	* @param    string $lb
2734	* @returns	string
2735	* @access   public
2736	* @deprecated
2737	*/
2738	function decodeChunked($buffer, $lb){
2739		// length := 0
2740		$length = 0;
2741		$new = '';
2742
2743		// read chunk-size, chunk-extension (if any) and CRLF
2744		// get the position of the linebreak
2745		$chunkend = strpos($buffer, $lb);
2746		if ($chunkend == FALSE) {
2747			$this->debug('no linebreak found in decodeChunked');
2748			return $new;
2749		}
2750		$temp = substr($buffer,0,$chunkend);
2751		$chunk_size = hexdec( trim($temp) );
2752		$chunkstart = $chunkend + strlen($lb);
2753		// while (chunk-size > 0) {
2754		while ($chunk_size > 0) {
2755			$this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2756			$chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2757
2758			// Just in case we got a broken connection
2759		  	if ($chunkend == FALSE) {
2760		  	    $chunk = substr($buffer,$chunkstart);
2761				// append chunk-data to entity-body
2762		    	$new .= $chunk;
2763		  	    $length += strlen($chunk);
2764		  	    break;
2765			}
2766
2767		  	// read chunk-data and CRLF
2768		  	$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2769		  	// append chunk-data to entity-body
2770		  	$new .= $chunk;
2771		  	// length := length + chunk-size
2772		  	$length += strlen($chunk);
2773		  	// read chunk-size and CRLF
2774		  	$chunkstart = $chunkend + strlen($lb);
2775
2776		  	$chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2777			if ($chunkend == FALSE) {
2778				break; //Just in case we got a broken connection
2779			}
2780			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2781			$chunk_size = hexdec( trim($temp) );
2782			$chunkstart = $chunkend;
2783		}
2784		return $new;
2785	}
2786
2787	/**
2788	 * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2789	 *
2790	 * @param	string $data HTTP body
2791	 * @param	string $cookie_str data for HTTP Cookie header
2792	 * @return	void
2793	 * @access	private
2794	 */
2795	function buildPayload($data, $cookie_str = '') {
2796		// Note: for cURL connections, $this->outgoing_payload is ignored,
2797		// as is the Content-Length header, but these are still created as
2798		// debugging guides.
2799
2800		// add content-length header
2801		$this->setHeader('Content-Length', strlen($data));
2802
2803		// start building outgoing payload:
2804		if ($this->proxy) {
2805			$uri = $this->url;
2806		} else {
2807			$uri = $this->uri;
2808		}
2809		$req = "$this->request_method $uri HTTP/$this->protocol_version";
2810		$this->debug("HTTP request: $req");
2811		$this->outgoing_payload = "$req\r\n";
2812
2813		// loop thru headers, serializing
2814		foreach($this->outgoing_headers as $k => $v){
2815			$hdr = $k.': '.$v;
2816			$this->debug("HTTP header: $hdr");
2817			$this->outgoing_payload .= "$hdr\r\n";
2818		}
2819
2820		// add any cookies
2821		if ($cookie_str != '') {
2822			$hdr = 'Cookie: '.$cookie_str;
2823			$this->debug("HTTP header: $hdr");
2824			$this->outgoing_payload .= "$hdr\r\n";
2825		}
2826
2827		// header/body separator
2828		$this->outgoing_payload .= "\r\n";
2829
2830		// add data
2831		$this->outgoing_payload .= $data;
2832	}
2833
2834	/**
2835	* sends the SOAP request via HTTP[S]
2836	*
2837	* @param    string $data message data
2838	* @param	array $cookies cookies to send
2839	* @return	boolean	true if OK, false if problem
2840	* @access   private
2841	*/
2842	function sendRequest($data, $cookies = NULL) {
2843		// build cookie string
2844		$cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2845
2846		// build payload
2847		$this->buildPayload($data, $cookie_str);
2848
2849	  if ($this->io_method() == 'socket') {
2850		// send payload
2851		if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2852			$this->setError('couldn\'t write message data to socket');
2853			$this->debug('couldn\'t write message data to socket');
2854			return false;
2855		}
2856		$this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2857		return true;
2858	  } else if ($this->io_method() == 'curl') {
2859		// set payload
2860		// cURL does say this should only be the verb, and in fact it
2861		// turns out that the URI and HTTP version are appended to this, which
2862		// some servers refuse to work with (so we no longer use this method!)
2863		//$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2864		$curl_headers = array();
2865		foreach($this->outgoing_headers as $k => $v){
2866			if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2867				$this->debug("Skip cURL header $k: $v");
2868			} else {
2869				$curl_headers[] = "$k: $v";
2870			}
2871		}
2872		if ($cookie_str != '') {
2873			$curl_headers[] = 'Cookie: ' . $cookie_str;
2874		}
2875		$this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2876		$this->debug('set cURL HTTP headers');
2877		if ($this->request_method == "POST") {
2878	  		$this->setCurlOption(CURLOPT_POST, 1);
2879	  		$this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2880			$this->debug('set cURL POST data');
2881	  	} else {
2882	  	}
2883		// insert custom user-set cURL options
2884		foreach ($this->ch_options as $key => $val) {
2885			$this->setCurlOption($key, $val);
2886		}
2887
2888		$this->debug('set cURL payload');
2889		return true;
2890	  }
2891	}
2892
2893	/**
2894	* gets the SOAP response via HTTP[S]
2895	*
2896	* @return	string the response (also sets member variables like incoming_payload)
2897	* @access   private
2898	*/
2899	function getResponse(){
2900		$this->incoming_payload = '';
2901
2902	  if ($this->io_method() == 'socket') {
2903	    // loop until headers have been retrieved
2904	    $data = '';
2905	    while (!isset($lb)){
2906
2907			// We might EOF during header read.
2908			if(feof($this->fp)) {
2909				$this->incoming_payload = $data;
2910				$this->debug('found no headers before EOF after length ' . strlen($data));
2911				$this->debug("received before EOF:\n" . $data);
2912				$this->setError('server failed to send headers');
2913				return false;
2914			}
2915
2916			$tmp = fgets($this->fp, 256);
2917			$tmplen = strlen($tmp);
2918			$this->debug("read line of $tmplen bytes: " . trim($tmp));
2919
2920			if ($tmplen == 0) {
2921				$this->incoming_payload = $data;
2922				$this->debug('socket read of headers timed out after length ' . strlen($data));
2923				$this->debug("read before timeout: " . $data);
2924				$this->setError('socket read of headers timed out');
2925				return false;
2926			}
2927
2928			$data .= $tmp;
2929			$pos = strpos($data,"\r\n\r\n");
2930			if($pos > 1){
2931				$lb = "\r\n";
2932			} else {
2933				$pos = strpos($data,"\n\n");
2934				if($pos > 1){
2935					$lb = "\n";
2936				}
2937			}
2938			// remove 100 headers
2939			if (isset($lb) && ereg('^HTTP/1.1 100',$data)) {
2940				unset($lb);
2941				$data = '';
2942			}//
2943		}
2944		// store header data
2945		$this->incoming_payload .= $data;
2946		$this->debug('found end of headers after length ' . strlen($data));
2947		// process headers
2948		$header_data = trim(substr($data,0,$pos));
2949		$header_array = explode($lb,$header_data);
2950		$this->incoming_headers = array();
2951		$this->incoming_cookies = array();
2952		foreach($header_array as $header_line){
2953			$arr = explode(':',$header_line, 2);
2954			if(count($arr) > 1){
2955				$header_name = strtolower(trim($arr[0]));
2956				$this->incoming_headers[$header_name] = trim($arr[1]);
2957				if ($header_name == 'set-cookie') {
2958					// TODO: allow multiple cookies from parseCookie
2959					$cookie = $this->parseCookie(trim($arr[1]));
2960					if ($cookie) {
2961						$this->incoming_cookies[] = $cookie;
2962						$this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2963					} else {
2964						$this->debug('did not find cookie in ' . trim($arr[1]));
2965					}
2966    			}
2967			} else if (isset($header_name)) {
2968				// append continuation line to previous header
2969				$this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2970			}
2971		}
2972
2973		// loop until msg has been received
2974		if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2975			$content_length =  2147483647;	// ignore any content-length header
2976			$chunked = true;
2977			$this->debug("want to read chunked content");
2978		} elseif (isset($this->incoming_headers['content-length'])) {
2979			$content_length = $this->incoming_headers['content-length'];
2980			$chunked = false;
2981			$this->debug("want to read content of length $content_length");
2982		} else {
2983			$content_length =  2147483647;
2984			$chunked = false;
2985			$this->debug("want to read content to EOF");
2986		}
2987		$data = '';
2988		do {
2989			if ($chunked) {
2990				$tmp = fgets($this->fp, 256);
2991				$tmplen = strlen($tmp);
2992				$this->debug("read chunk line of $tmplen bytes");
2993				if ($tmplen == 0) {
2994					$this->incoming_payload = $data;
2995					$this->debug('socket read of chunk length timed out after length ' . strlen($data));
2996					$this->debug("read before timeout:\n" . $data);
2997					$this->setError('socket read of chunk length timed out');
2998					return false;
2999				}
3000				$content_length = hexdec(trim($tmp));
3001				$this->debug("chunk length $content_length");
3002			}
3003			$strlen = 0;
3004		    while (($strlen < $content_length) && (!feof($this->fp))) {
3005		    	$readlen = min(8192, $content_length - $strlen);
3006				$tmp = fread($this->fp, $readlen);
3007				$tmplen = strlen($tmp);
3008				$this->debug("read buffer of $tmplen bytes");
3009				if (($tmplen == 0) && (!feof($this->fp))) {
3010					$this->incoming_payload = $data;
3011					$this->debug('socket read of body timed out after length ' . strlen($data));
3012					$this->debug("read before timeout:\n" . $data);
3013					$this->setError('socket read of body timed out');
3014					return false;
3015				}
3016				$strlen += $tmplen;
3017				$data .= $tmp;
3018			}
3019			if ($chunked && ($content_length > 0)) {
3020				$tmp = fgets($this->fp, 256);
3021				$tmplen = strlen($tmp);
3022				$this->debug("read chunk terminator of $tmplen bytes");
3023				if ($tmplen == 0) {
3024					$this->incoming_payload = $data;
3025					$this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3026					$this->debug("read before timeout:\n" . $data);
3027					$this->setError('socket read of chunk terminator timed out');
3028					return false;
3029				}
3030			}
3031		} while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3032		if (feof($this->fp)) {
3033			$this->debug('read to EOF');
3034		}
3035		$this->debug('read body of length ' . strlen($data));
3036		$this->incoming_payload .= $data;
3037		$this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3038
3039		// close filepointer
3040		if(
3041			(isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3042			(! $this->persistentConnection) || feof($this->fp)){
3043			fclose($this->fp);
3044			$this->fp = false;
3045			$this->debug('closed socket');
3046		}
3047
3048		// connection was closed unexpectedly
3049		if($this->incoming_payload == ''){
3050			$this->setError('no response from server');
3051			return false;
3052		}
3053
3054		// decode transfer-encoding
3055//		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3056//			if(!$data = $this->decodeChunked($data, $lb)){
3057//				$this->setError('Decoding of chunked data failed');
3058//				return false;
3059//			}
3060			//print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3061			// set decoded payload
3062//			$this->incoming_payload = $header_data.$lb.$lb.$data;
3063//		}
3064
3065	  } else if ($this->io_method() == 'curl') {
3066		// send and receive
3067		$this->debug('send and receive with cURL');
3068		$this->incoming_payload = curl_exec($this->ch);
3069		$data = $this->incoming_payload;
3070
3071        $cErr = curl_error($this->ch);
3072		if ($cErr != '') {
3073        	$err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3074        	// TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3075			foreach(curl_getinfo($this->ch) as $k => $v){
3076				$err .= "$k: $v<br>";
3077			}
3078			$this->debug($err);
3079			$this->setError($err);
3080			curl_close($this->ch);
3081	    	return false;
3082		} else {
3083			//echo '<pre>';
3084			//var_dump(curl_getinfo($this->ch));
3085			//echo '</pre>';
3086		}
3087		// close curl
3088		$this->debug('No cURL error, closing cURL');
3089		curl_close($this->ch);
3090
3091		// try removing skippable headers
3092		$savedata = $data;
3093		while ($this->isSkippableCurlHeader($data)) {
3094			$this->debug("Found HTTP header to skip");
3095			if ($pos = strpos($data,"\r\n\r\n")) {
3096				$data = ltrim(substr($data,$pos));
3097			} elseif($pos = strpos($data,"\n\n") ) {
3098				$data = ltrim(substr($data,$pos));
3099			}
3100		}
3101
3102		if ($data == '') {
3103			// have nothing left; just remove 100 header(s)
3104			$data = $savedata;
3105			while (ereg('^HTTP/1.1 100',$data)) {
3106				if ($pos = strpos($data,"\r\n\r\n")) {
3107					$data = ltrim(substr($data,$pos));
3108				} elseif($pos = strpos($data,"\n\n") ) {
3109					$data = ltrim(substr($data,$pos));
3110				}
3111			}
3112		}
3113
3114		// separate content from HTTP headers
3115		if ($pos = strpos($data,"\r\n\r\n")) {
3116			$lb = "\r\n";
3117		} elseif( $pos = strpos($data,"\n\n")) {
3118			$lb = "\n";
3119		} else {
3120			$this->debug('no proper separation of headers and document');
3121			$this->setError('no proper separation of headers and document');
3122			return false;
3123		}
3124		$header_data = trim(substr($data,0,$pos));
3125		$header_array = explode($lb,$header_data);
3126		$data = ltrim(substr($data,$pos));
3127		$this->debug('found proper separation of headers and document');
3128		$this->debug('cleaned data, stringlen: '.strlen($data));
3129		// clean headers
3130		foreach ($header_array as $header_line) {
3131			$arr = explode(':',$header_line,2);
3132			if(count($arr) > 1){
3133				$header_name = strtolower(trim($arr[0]));
3134				$this->incoming_headers[$header_name] = trim($arr[1]);
3135				if ($header_name == 'set-cookie') {
3136					// TODO: allow multiple cookies from parseCookie
3137					$cookie = $this->parseCookie(trim($arr[1]));
3138					if ($cookie) {
3139						$this->incoming_cookies[] = $cookie;
3140						$this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3141					} else {
3142						$this->debug('did not find cookie in ' . trim($arr[1]));
3143					}
3144    			}
3145			} else if (isset($header_name)) {
3146				// append continuation line to previous header
3147				$this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3148			}
3149		}
3150	  }
3151
3152		$this->response_status_line = $header_array[0];
3153		$arr = explode(' ', $this->response_status_line, 3);
3154		$http_version = $arr[0];
3155		$http_status = intval($arr[1]);
3156		$http_reason = count($arr) > 2 ? $arr[2] : '';
3157
3158 		// see if we need to resend the request with http digest authentication
3159 		if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3160 			$this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3161 			$this->setURL($this->incoming_headers['location']);
3162			$this->tryagain = true;
3163			return false;
3164		}
3165
3166 		// see if we need to resend the request with http digest authentication
3167 		if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3168 			$this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3169 			if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3170 				$this->debug('Server wants digest authentication');
3171 				// remove "Digest " from our elements
3172 				$digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3173
3174 				// parse elements into array
3175 				$digestElements = explode(',', $digestString);
3176 				foreach ($digestElements as $val) {
3177 					$tempElement = explode('=', trim($val), 2);
3178 					$digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3179 				}
3180
3181				// should have (at least) qop, realm, nonce
3182 				if (isset($digestRequest['nonce'])) {
3183 					$this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3184 					$this->tryagain = true;
3185 					return false;
3186 				}
3187 			}
3188			$this->debug('HTTP authentication failed');
3189			$this->setError('HTTP authentication failed');
3190			return false;
3191 		}
3192
3193		if (
3194			($http_status >= 300 && $http_status <= 307) ||
3195			($http_status >= 400 && $http_status <= 417) ||
3196			($http_status >= 501 && $http_status <= 505)
3197		   ) {
3198			$this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3199			return false;
3200		}
3201
3202		// decode content-encoding
3203		if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3204			if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3205    			// if decoding works, use it. else assume data wasn't gzencoded
3206    			if(function_exists('gzinflate')){
3207					//$timer->setMarker('starting decoding of gzip/deflated content');
3208					// IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3209					// this means there are no Zlib headers, although there should be
3210					$this->debug('The gzinflate function exists');
3211					$datalen = strlen($data);
3212					if ($this->incoming_headers['content-encoding'] == 'deflate') {
3213						if ($degzdata = @gzinflate($data)) {
3214	    					$data = $degzdata;
3215	    					$this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3216	    					if (strlen($data) < $datalen) {
3217	    						// test for the case that the payload has been compressed twice
3218		    					$this->debug('The inflated payload is smaller than the gzipped one; try again');
3219								if ($degzdata = @gzinflate($data)) {
3220			    					$data = $degzdata;
3221			    					$this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3222								}
3223	    					}
3224	    				} else {
3225	    					$this->debug('Error using gzinflate to inflate the payload');
3226	    					$this->setError('Error using gzinflate to inflate the payload');
3227	    				}
3228					} elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3229						if ($degzdata = @gzinflate(substr($data, 10))) {	// do our best
3230							$data = $degzdata;
3231	    					$this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3232	    					if (strlen($data) < $datalen) {
3233	    						// test for the case that the payload has been compressed twice
3234		    					$this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3235								if ($degzdata = @gzinflate(substr($data, 10))) {
3236			    					$data = $degzdata;
3237			    					$this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3238								}
3239	    					}
3240	    				} else {
3241	    					$this->debug('Error using gzinflate to un-gzip the payload');
3242							$this->setError('Error using gzinflate to un-gzip the payload');
3243	    				}
3244					}
3245					//$timer->setMarker('finished decoding of gzip/deflated content');
3246					//print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3247					// set decoded payload
3248					$this->incoming_payload = $header_data.$lb.$lb.$data;
3249    			} else {
3250					$this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3251					$this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3252				}
3253			} else {
3254				$this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3255				$this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3256			}
3257		} else {
3258			$this->debug('No Content-Encoding header');
3259		}
3260
3261		if(strlen($data) == 0){
3262			$this->debug('no data after headers!');
3263			$this->setError('no data present after HTTP headers');
3264			return false;
3265		}
3266
3267		return $data;
3268	}
3269
3270	/**
3271	 * sets the content-type for the SOAP message to be sent
3272	 *
3273	 * @param	string $type the content type, MIME style
3274	 * @param	mixed $charset character set used for encoding (or false)
3275	 * @access	public
3276	 */
3277	function setContentType($type, $charset = false) {
3278		$this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3279	}
3280
3281	/**
3282	 * specifies that an HTTP persistent connection should be used
3283	 *
3284	 * @return	boolean whether the request was honored by this method.
3285	 * @access	public
3286	 */
3287	function usePersistentConnection(){
3288		if (isset($this->outgoing_headers['Accept-Encoding'])) {
3289			return false;
3290		}
3291		$this->protocol_version = '1.1';
3292		$this->persistentConnection = true;
3293		$this->setHeader('Connection', 'Keep-Alive');
3294		return true;
3295	}
3296
3297	/**
3298	 * parse an incoming Cookie into it's parts
3299	 *
3300	 * @param	string $cookie_str content of cookie
3301	 * @return	array with data of that cookie
3302	 * @access	private
3303	 */
3304	/*
3305	 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3306	 */
3307	function parseCookie($cookie_str) {
3308		$cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3309		$data = split(';', $cookie_str);
3310		$value_str = $data[0];
3311
3312		$cookie_param = 'domain=';
3313		$start = strpos($cookie_str, $cookie_param);
3314		if ($start > 0) {
3315			$domain = substr($cookie_str, $start + strlen($cookie_param));
3316			$domain = substr($domain, 0, strpos($domain, ';'));
3317		} else {
3318			$domain = '';
3319		}
3320
3321		$cookie_param = 'expires=';
3322		$start = strpos($cookie_str, $cookie_param);
3323		if ($start > 0) {
3324			$expires = substr($cookie_str, $start + strlen($cookie_param));
3325			$expires = substr($expires, 0, strpos($expires, ';'));
3326		} else {
3327			$expires = '';
3328		}
3329
3330		$cookie_param = 'path=';
3331		$start = strpos($cookie_str, $cookie_param);
3332		if ( $start > 0 ) {
3333			$path = substr($cookie_str, $start + strlen($cookie_param));
3334			$path = substr($path, 0, strpos($path, ';'));
3335		} else {
3336			$path = '/';
3337		}
3338
3339		$cookie_param = ';secure;';
3340		if (strpos($cookie_str, $cookie_param) !== FALSE) {
3341			$secure = true;
3342		} else {
3343			$secure = false;
3344		}
3345
3346		$sep_pos = strpos($value_str, '=');
3347
3348		if ($sep_pos) {
3349			$name = substr($value_str, 0, $sep_pos);
3350			$value = substr($value_str, $sep_pos + 1);
3351			$cookie= array(	'name' => $name,
3352			                'value' => $value,
3353							'domain' => $domain,
3354							'path' => $path,
3355							'expires' => $expires,
3356							'secure' => $secure
3357							);
3358			return $cookie;
3359		}
3360		return false;
3361	}
3362
3363	/**
3364	 * sort out cookies for the current request
3365	 *
3366	 * @param	array $cookies array with all cookies
3367	 * @param	boolean $secure is the send-content secure or not?
3368	 * @return	string for Cookie-HTTP-Header
3369	 * @access	private
3370	 */
3371	function getCookiesForRequest($cookies, $secure=false) {
3372		$cookie_str = '';
3373		if ((! is_null($cookies)) && (is_array($cookies))) {
3374			foreach ($cookies as $cookie) {
3375				if (! is_array($cookie)) {
3376					continue;
3377				}
3378	    		$this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3379				if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3380					if (strtotime($cookie['expires']) <= time()) {
3381						$this->debug('cookie has expired');
3382						continue;
3383					}
3384				}
3385				if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3386					$domain = preg_quote($cookie['domain']);
3387					if (! preg_match("'.*$domain$'i", $this->host)) {
3388						$this->debug('cookie has different domain');
3389						continue;
3390					}
3391				}
3392				if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3393					$path = preg_quote($cookie['path']);
3394					if (! preg_match("'^$path.*'i", $this->path)) {
3395						$this->debug('cookie is for a different path');
3396						continue;
3397					}
3398				}
3399				if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3400					$this->debug('cookie is secure, transport is not');
3401					continue;
3402				}
3403				$cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3404	    		$this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3405			}
3406		}
3407		return $cookie_str;
3408  }
3409}
3410
3411?><?php
3412
3413
3414
3415/**
3416*
3417* nusoap_server allows the user to create a SOAP server
3418* that is capable of receiving messages and returning responses
3419*
3420* @author   Dietrich Ayala <dietrich@ganx4.com>
3421* @author   Scott Nichol <snichol@users.sourceforge.net>
3422* @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
3423* @access   public
3424*/
3425class nusoap_server extends nusoap_base {
3426	/**
3427	 * HTTP headers of request
3428	 * @var array
3429	 * @access private
3430	 */
3431	var $headers = array();
3432	/**
3433	 * HTTP request
3434	 * @var string
3435	 * @access private
3436	 */
3437	var $request = '';
3438	/**
3439	 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3440	 * @var string
3441	 * @access public
3442	 */
3443	var $requestHeaders = '';
3444	/**
3445	 * SOAP Headers from request (parsed)
3446	 * @var mixed
3447	 * @access public
3448	 */
3449	var $requestHeader = NULL;
3450	/**
3451	 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3452	 * @var string
3453	 * @access public
3454	 */
3455	var $document = '';
3456	/**
3457	 * SOAP payload for request (text)
3458	 * @var string
3459	 * @access public
3460	 */
3461	var $requestSOAP = '';
3462	/**
3463	 * requested method namespace URI
3464	 * @var string
3465	 * @access private
3466	 */
3467	var $methodURI = '';
3468	/**
3469	 * name of method requested
3470	 * @var string
3471	 * @access private
3472	 */
3473	var $methodname = '';
3474	/**
3475	 * method parameters from request
3476	 * @var array
3477	 * @access private
3478	 */
3479	var $methodparams = array();
3480	/**
3481	 * SOAP Action from request
3482	 * @var string
3483	 * @access private
3484	 */
3485	var $SOAPAction = '';
3486	/**
3487	 * character set encoding of incoming (request) messages
3488	 * @var string
3489	 * @access public
3490	 */
3491	var $xml_encoding = '';
3492	/**
3493	 * toggles whether the parser decodes element content w/ utf8_decode()
3494	 * @var boolean
3495	 * @access public
3496	 */
3497    var $decode_utf8 = true;
3498
3499	/**
3500	 * HTTP headers of response
3501	 * @var array
3502	 * @access public
3503	 */
3504	var $outgoing_headers = array();
3505	/**
3506	 * HTTP response
3507	 * @var string
3508	 * @access private
3509	 */
3510	var $response = '';
3511	/**
3512	 * SOAP headers for response (text or array of soapval or associative array)
3513	 * @var mixed
3514	 * @access public
3515	 */
3516	var $responseHeaders = '';
3517	/**
3518	 * SOAP payload for response (text)
3519	 * @var string
3520	 * @access private
3521	 */
3522	var $responseSOAP = '';
3523	/**
3524	 * method return value to place in response
3525	 * @var mixed
3526	 * @access private
3527	 */
3528	var $methodreturn = false;
3529	/**
3530	 * whether $methodreturn is a string of literal XML
3531	 * @var boolean
3532	 * @access public
3533	 */
3534	var $methodreturnisliteralxml = false;
3535	/**
3536	 * SOAP fault for response (or false)
3537	 * @var mixed
3538	 * @access private
3539	 */
3540	var $fault = false;
3541	/**
3542	 * text indication of result (for debugging)
3543	 * @var string
3544	 * @access private
3545	 */
3546	var $result = 'successful';
3547
3548	/**
3549	 * assoc array of operations => opData; operations are added by the register()
3550	 * method or by parsing an external WSDL definition
3551	 * @var array
3552	 * @access private
3553	 */
3554	var $operations = array();
3555	/**
3556	 * wsdl instance (if one)
3557	 * @var mixed
3558	 * @access private
3559	 */
3560	var $wsdl = false;
3561	/**
3562	 * URL for WSDL (if one)
3563	 * @var mixed
3564	 * @access private
3565	 */
3566	var $externalWSDLURL = false;
3567	/**
3568	 * whether to append debug to response as XML comment
3569	 * @var boolean
3570	 * @access public
3571	 */
3572	var $debug_flag = false;
3573
3574
3575	/**
3576	* constructor
3577    * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3578	*
3579    * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3580	* @access   public
3581	*/
3582	function nusoap_server($wsdl=false){
3583		parent::nusoap_base();
3584		// turn on debugging?
3585		global $debug;
3586		global $HTTP_SERVER_VARS;
3587
3588		if (isset($_SERVER)) {
3589			$this->debug("_SERVER is defined:");
3590			$this->appendDebug($this->varDump($_SERVER));
3591		} elseif (isset($HTTP_SERVER_VARS)) {
3592			$this->debug("HTTP_SERVER_VARS is defined:");
3593			$this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3594		} else {
3595			$this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3596		}
3597
3598		if (isset($debug)) {
3599			$this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3600			$this->debug_flag = $debug;
3601		} elseif (isset($_SERVER['QUERY_STRING'])) {
3602			$qs = explode('&', $_SERVER['QUERY_STRING']);
3603			foreach ($qs as $v) {
3604				if (substr($v, 0, 6) == 'debug=') {
3605					$this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3606					$this->debug_flag = substr($v, 6);
3607				}
3608			}
3609		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3610			$qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3611			foreach ($qs as $v) {
3612				if (substr($v, 0, 6) == 'debug=') {
3613					$this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3614					$this->debug_flag = substr($v, 6);
3615				}
3616			}
3617		}
3618
3619		// wsdl
3620		if($wsdl){
3621			$this->debug("In nusoap_server, WSDL is specified");
3622			if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3623				$this->wsdl = $wsdl;
3624				$this->externalWSDLURL = $this->wsdl->wsdl;
3625				$this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3626			} else {
3627				$this->debug('Create wsdl from ' . $wsdl);
3628				$this->wsdl = new wsdl($wsdl);
3629				$this->externalWSDLURL = $wsdl;
3630			}
3631			$this->appendDebug($this->wsdl->getDebug());
3632			$this->wsdl->clearDebug();
3633			if($err = $this->wsdl->getError()){
3634				die('WSDL ERROR: '.$err);
3635			}
3636		}
3637	}
3638
3639	/**
3640	* processes request and returns response
3641	*
3642	* @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3643	* @access   public
3644	*/
3645	function service($data){
3646		global $HTTP_SERVER_VARS;
3647
3648		if (isset($_SERVER['QUERY_STRING'])) {
3649			$qs = $_SERVER['QUERY_STRING'];
3650		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3651			$qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3652		} else {
3653			$qs = '';
3654		}
3655		$this->debug("In service, query string=$qs");
3656
3657		if (ereg('wsdl', $qs) ){
3658			$this->debug("In service, this is a request for WSDL");
3659			if($this->externalWSDLURL){
3660              if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3661				header('Location: '.$this->externalWSDLURL);
3662              } else { // assume file
3663                header("Content-Type: text/xml\r\n");
3664                $fp = fopen($this->externalWSDLURL, 'r');
3665                fpassthru($fp);
3666              }
3667			} elseif ($this->wsdl) {
3668				header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3669				print $this->wsdl->serialize($this->debug_flag);
3670				if ($this->debug_flag) {
3671					$this->debug('wsdl:');
3672					$this->appendDebug($this->varDump($this->wsdl));
3673					print $this->getDebugAsXMLComment();
3674				}
3675			} else {
3676				header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3677				print "This service does not provide WSDL";
3678			}
3679		} elseif ($data == '' && $this->wsdl) {
3680			$this->debug("In service, there is no data, so return Web description");
3681			print $this->wsdl->webDescription();
3682		} else {
3683			$this->debug("In service, invoke the request");
3684			$this->parse_request($data);
3685			if (! $this->fault) {
3686				$this->invoke_method();
3687			}
3688			if (! $this->fault) {
3689				$this->serialize_return();
3690			}
3691			$this->send_response();
3692		}
3693	}
3694
3695	/**
3696	* parses HTTP request headers.
3697	*
3698	* The following fields are set by this function (when successful)
3699	*
3700	* headers
3701	* request
3702	* xml_encoding
3703	* SOAPAction
3704	*
3705	* @access   private
3706	*/
3707	function parse_http_headers() {
3708		global $HTTP_SERVER_VARS;
3709
3710		$this->request = '';
3711		$this->SOAPAction = '';
3712		if(function_exists('getallheaders')){
3713			$this->debug("In parse_http_headers, use getallheaders");
3714			$headers = getallheaders();
3715			foreach($headers as $k=>$v){
3716				$k = strtolower($k);
3717				$this->headers[$k] = $v;
3718				$this->request .= "$k: $v\r\n";
3719				$this->debug("$k: $v");
3720			}
3721			// get SOAPAction header
3722			if(isset($this->headers['soapaction'])){
3723				$this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3724			}
3725			// get the character encoding of the incoming request
3726			if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3727				$enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3728				if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3729					$this->xml_encoding = strtoupper($enc);
3730				} else {
3731					$this->xml_encoding = 'US-ASCII';
3732				}
3733			} else {
3734				// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3735				$this->xml_encoding = 'ISO-8859-1';
3736			}
3737		} elseif(isset($_SERVER) && is_array($_SERVER)){
3738			$this->debug("In parse_http_headers, use _SERVER");
3739			foreach ($_SERVER as $k => $v) {
3740				if (substr($k, 0, 5) == 'HTTP_') {
3741					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3742				} else {
3743					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3744				}
3745				if ($k == 'soapaction') {
3746					// get SOAPAction header
3747					$k = 'SOAPAction';
3748					$v = str_replace('"', '', $v);
3749					$v = str_replace('\\', '', $v);
3750					$this->SOAPAction = $v;
3751				} else if ($k == 'content-type') {
3752					// get the character encoding of the incoming request
3753					if (strpos($v, '=')) {
3754						$enc = substr(strstr($v, '='), 1);
3755						$enc = str_replace('"', '', $enc);
3756						$enc = str_replace('\\', '', $enc);
3757						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3758							$this->xml_encoding = strtoupper($enc);
3759						} else {
3760							$this->xml_encoding = 'US-ASCII';
3761						}
3762					} else {
3763						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3764						$this->xml_encoding = 'ISO-8859-1';
3765					}
3766				}
3767				$this->headers[$k] = $v;
3768				$this->request .= "$k: $v\r\n";
3769				$this->debug("$k: $v");
3770			}
3771		} elseif (is_array($HTTP_SERVER_VARS)) {
3772			$this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3773			foreach ($HTTP_SERVER_VARS as $k => $v) {
3774				if (substr($k, 0, 5) == 'HTTP_') {
3775					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 	                                         $k = strtolower(substr($k, 5));
3776				} else {
3777					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 	                                         $k = strtolower($k);
3778				}
3779				if ($k == 'soapaction') {
3780					// get SOAPAction header
3781					$k = 'SOAPAction';
3782					$v = str_replace('"', '', $v);
3783					$v = str_replace('\\', '', $v);
3784					$this->SOAPAction = $v;
3785				} else if ($k == 'content-type') {
3786					// get the character encoding of the incoming request
3787					if (strpos($v, '=')) {
3788						$enc = substr(strstr($v, '='), 1);
3789						$enc = str_replace('"', '', $enc);
3790						$enc = str_replace('\\', '', $enc);
3791						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3792							$this->xml_encoding = strtoupper($enc);
3793						} else {
3794							$this->xml_encoding = 'US-ASCII';
3795						}
3796					} else {
3797						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3798						$this->xml_encoding = 'ISO-8859-1';
3799					}
3800				}
3801				$this->headers[$k] = $v;
3802				$this->request .= "$k: $v\r\n";
3803				$this->debug("$k: $v");
3804			}
3805		} else {
3806			$this->debug("In parse_http_headers, HTTP headers not accessible");
3807			$this->setError("HTTP headers not accessible");
3808		}
3809	}
3810
3811	/**
3812	* parses a request
3813	*
3814	* The following fields are set by this function (when successful)
3815	*
3816	* headers
3817	* request
3818	* xml_encoding
3819	* SOAPAction
3820	* request
3821	* requestSOAP
3822	* methodURI
3823	* methodname
3824	* methodparams
3825	* requestHeaders
3826	* document
3827	*
3828	* This sets the fault field on error
3829	*
3830	* @param    string $data XML string
3831	* @access   private
3832	*/
3833	function parse_request($data='') {
3834		$this->debug('entering parse_request()');
3835		$this->parse_http_headers();
3836		$this->debug('got character encoding: '.$this->xml_encoding);
3837		// uncompress if necessary
3838		if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3839			$this->debug('got content encoding: ' . $this->headers['content-encoding']);
3840			if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3841		    	// if decoding works, use it. else assume data wasn't gzencoded
3842				if (function_exists('gzuncompress')) {
3843					if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3844						$data = $degzdata;
3845					} elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3846						$data = $degzdata;
3847					} else {
3848						$this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3849						return;
3850					}
3851				} else {
3852					$this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3853					return;
3854				}
3855			}
3856		}
3857		$this->request .= "\r\n".$data;
3858		$data = $this->parseRequest($this->headers, $data);
3859		$this->requestSOAP = $data;
3860		$this->debug('leaving parse_request');
3861	}
3862
3863	/**
3864	* invokes a PHP function for the requested SOAP method
3865	*
3866	* The following fields are set by this function (when successful)
3867	*
3868	* methodreturn
3869	*
3870	* Note that the PHP function that is called may also set the following
3871	* fields to affect the response sent to the client
3872	*
3873	* responseHeaders
3874	* outgoing_headers
3875	*
3876	* This sets the fault field on error
3877	*
3878	* @access   private
3879	*/
3880	function invoke_method() {
3881		$this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3882
3883
3884    // NEW - BSP
3885    $data = split("_", $this->methodname);
3886    if(sizeof($data) == 2)
3887    {
3888      $class = $data[0];
3889      $method = $data[1];
3890    }
3891    else
3892    {
3893      $class = '';
3894      $method = '';
3895    }
3896    // END OF NEW
3897
3898
3899		if ($this->wsdl) {
3900			if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3901				$this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3902				$this->appendDebug('opData=' . $this->varDump($this->opData));
3903			} elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3904				// Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3905				$this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3906				$this->appendDebug('opData=' . $this->varDump($this->opData));
3907				$this->methodname = $this->opData['name'];
3908			} else {
3909				$this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3910				$this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3911				return;
3912			}
3913		} else {
3914			$this->debug('in invoke_method, no WSDL to validate method');
3915		}
3916
3917		// if a . is present in $this->methodname, we see if there is a class in scope,
3918		// which could be referred to. We will also distinguish between two deliminators,
3919		// to allow methods to be called a the class or an instance
3920		//$class = '';
3921		//$method = '';
3922		if (strpos($this->methodname, '..') > 0) {
3923			$delim = '..';
3924		} else if (strpos($this->methodname, '.') > 0) {
3925			$delim = '.';
3926		} else if (strpos($this->methodname, '_') > 0) {
3927			$delim = '_';
3928		} else {
3929			$delim = '';
3930		}
3931
3932		if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
3933			class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
3934			// get the class and method name
3935			$class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3936			$method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
3937			$this->debug("in invoke_method, class=$class method=$method delim=$delim");
3938		}
3939
3940		// does method exist?
3941		if ($class == '') {
3942			if (!function_exists($this->methodname)) {
3943				$this->debug("in invoke_method, function '$this->methodname' not found!");
3944				$this->result = 'fault: method not found';
3945//				$this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3946				$this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction");
3947				return;
3948			}
3949		} else {
3950			$method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
3951			if (!in_array($method_to_compare, get_class_methods($class))) {
3952				$this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3953				$this->result = 'fault: method not found';
3954				$this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
3955				return;
3956			}
3957		}
3958
3959		// evaluate message, getting back parameters
3960		// verify that request parameters match the method's signature
3961		if(! $this->verify_method($this->methodname,$this->methodparams)){
3962			// debug
3963			$this->debug('ERROR: request not verified against method signature');
3964			$this->result = 'fault: request failed validation against method signature';
3965			// return fault
3966			$this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
3967			return;
3968		}
3969
3970		// if there are parameters to pass
3971		$this->debug('in invoke_method, params:');
3972		$this->appendDebug($this->varDump($this->methodparams));
3973		$this->debug("in invoke_method, calling '$this->methodname'");
3974		if (!function_exists('call_user_func_array')) {
3975			if ($class == '') {
3976				$this->debug('in invoke_method, calling function using eval()');
3977				$funcCall = "\$this->methodreturn = $this->methodname(";
3978			} else {
3979				if ($delim == '..') {
3980					$this->debug('in invoke_method, calling class method using eval()');
3981					$funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3982				} else {
3983					$this->debug('in invoke_method, calling instance method using eval()');
3984					// generate unique instance name
3985					$instname = "\$inst_".time();
3986					$funcCall = $instname." = new ".$class."(); ";
3987					$funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3988				}
3989			}
3990			if ($this->methodparams) {
3991				foreach ($this->methodparams as $param) {
3992					if (is_array($param) || is_object($param)) {
3993						$this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3994						return;
3995					}
3996					$funcCall .= "\"$param\",";
3997				}
3998				$funcCall = substr($funcCall, 0, -1);
3999			}
4000			$funcCall .= ');';
4001			$this->debug('in invoke_method, function call: '.$funcCall);
4002			@eval($funcCall);
4003		} else {
4004			if ($class == '') {
4005				$this->debug('in invoke_method, calling function using call_user_func_array()');
4006				$call_arg = "$this->methodname";	// straight assignment changes $this->methodname to lower case after call_user_func_array()
4007			} elseif ($delim == '..') {
4008				$this->debug('in invoke_method, calling class method using call_user_func_array()');
4009				$call_arg = array ($class, $method);
4010			} else {
4011				$this->debug('in invoke_method, calling instance method using call_user_func_array()');
4012				$instance = new $class ();
4013				$call_arg = array(&$instance, $method);
4014			}
4015
4016			/* NEU BSP */
4017			$bsp_call_arg = array(&$instance, "addServer");
4018			call_user_func_array($bsp_call_arg, array($this));
4019			/* END NEU */
4020
4021			if (is_array($this->methodparams)) {
4022				$this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4023			} else {
4024				$this->methodreturn = call_user_func_array($call_arg, array());
4025			}
4026		}
4027        $this->debug('in invoke_method, methodreturn:');
4028        $this->appendDebug($this->varDump($this->methodreturn));
4029		$this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
4030	}
4031
4032	/**
4033	* serializes the return value from a PHP function into a full SOAP Envelope
4034	*
4035	* The following fields are set by this function (when successful)
4036	*
4037	* responseSOAP
4038	*
4039	* This sets the fault field on error
4040	*
4041	* @access   private
4042	*/
4043	function serialize_return() {
4044		$this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4045		// if fault
4046		if (isset($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
4047			$this->debug('got a fault object from method');
4048			$this->fault = $this->methodreturn;
4049			return;
4050		} elseif ($this->methodreturnisliteralxml) {
4051			$return_val = $this->methodreturn;
4052		// returned value(s)
4053		} else {
4054			$this->debug('got a(n) '.gettype($this->methodreturn).' from method');
4055			$this->debug('serializing return value');
4056			if($this->wsdl){
4057				if (sizeof($this->opData['output']['parts']) > 1) {
4058					$this->debug('more than one output part, so use the method return unchanged');
4059			    	$opParams = $this->methodreturn;
4060			    } elseif (sizeof($this->opData['output']['parts']) == 1) {
4061					$this->debug('exactly one output part, so wrap the method return in a simple array');
4062					// TODO: verify that it is not already wrapped!
4063			    	//foreach ($this->opData['output']['parts'] as $name => $type) {
4064					//	$this->debug('wrap in element named ' . $name);
4065			    	//}
4066			    	$opParams = array($this->methodreturn);
4067			    }
4068			    $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
4069			    $this->appendDebug($this->wsdl->getDebug());
4070			    $this->wsdl->clearDebug();
4071				if($errstr = $this->wsdl->getError()){
4072					$this->debug('got wsdl error: '.$errstr);
4073					$this->fault('SOAP-ENV:Server', 'unable to serialize result');
4074					return;
4075				}
4076			} else {
4077				if (isset($this->methodreturn)) {
4078					$return_val = $this->serialize_val($this->methodreturn, 'return');
4079				} else {
4080					$return_val = '';
4081					$this->debug('in absence of WSDL, assume void return for backward compatibility');
4082				}
4083			}
4084		}
4085		$this->debug('return value:');
4086		$this->appendDebug($this->varDump($return_val));
4087
4088		$this->debug('serializing response');
4089		if ($this->wsdl) {
4090			$this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4091			if ($this->opData['style'] == 'rpc') {
4092				$this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4093				if ($this->opData['output']['use'] == 'literal') {
4094					// http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
4095					$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4096				} else {
4097					$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4098				}
4099			} else {
4100				$this->debug('style is not rpc for serialization: assume document');
4101				$payload = $return_val;
4102			}
4103		} else {
4104			$this->debug('do not have WSDL for serialization: assume rpc/encoded');
4105			$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4106		}
4107		$this->result = 'successful';
4108		if($this->wsdl){
4109			//if($this->debug_flag){
4110            	$this->appendDebug($this->wsdl->getDebug());
4111            //	}
4112			if (isset($opData['output']['encodingStyle'])) {
4113				$encodingStyle = $opData['output']['encodingStyle'];
4114			} else {
4115				$encodingStyle = '';
4116			}
4117
4118			/* CHANGED 2009-04-08 BY Björn Spichal ---------------------------------*/
4119      $payload = str_replace("&lt;![CDATA[", "<![CDATA[", $payload);
4120      $payload = str_replace("]]&gt;", "]]>", $payload);
4121			/*----------------------------------------------------------------------*/
4122
4123			// Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4124			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
4125		} else {
4126			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
4127		}
4128
4129		$this->debug("Leaving serialize_return");
4130	}
4131
4132	/**
4133	* sends an HTTP response
4134	*
4135	* The following fields are set by this function (when successful)
4136	*
4137	* outgoing_headers
4138	* response
4139	*
4140	* @access   private
4141	*/
4142	function send_response() {
4143		$this->debug('Enter send_response');
4144		if ($this->fault) {
4145			$payload = $this->fault->serialize();
4146			$this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4147			$this->outgoing_headers[] = "Status: 500 Internal Server Error";
4148		} else {
4149			$payload = $this->responseSOAP;
4150			// Some combinations of PHP+Web server allow the Status
4151			// to come through as a header.  Since OK is the default
4152			// just do nothing.
4153			// $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4154			// $this->outgoing_headers[] = "Status: 200 OK";
4155		}
4156        // add debug data if in debug mode
4157		if(isset($this->debug_flag) && $this->debug_flag){
4158        	$payload .= $this->getDebugAsXMLComment();
4159        }
4160		$this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4161		ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
4162		$this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
4163		// Let the Web server decide about this
4164		//$this->outgoing_headers[] = "Connection: Close\r\n";
4165		$payload = $this->getHTTPBody($payload);
4166		$type = $this->getHTTPContentType();
4167		$charset = $this->getHTTPContentTypeCharset();
4168		$this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4169		//begin code to compress payload - by John
4170		// NOTE: there is no way to know whether the Web server will also compress
4171		// this data.
4172		if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4173			if (strstr($this->headers['accept-encoding'], 'gzip')) {
4174				if (function_exists('gzencode')) {
4175					if (isset($this->debug_flag) && $this->debug_flag) {
4176						$payload .= "<!-- Content being gzipped -->"<