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