1<?php
2
3
4
5
6/**
7*
8* nusoap_server allows the user to create a SOAP server
9* that is capable of receiving messages and returning responses
10*
11* @author   Dietrich Ayala <dietrich@ganx4.com>
12* @author   Scott Nichol <snichol@users.sourceforge.net>
13* @version  $Id: class.soap_server.php,v 1.58 2007/10/30 18:50:30 snichol Exp $
14* @access   public
15*/
16class nusoap_server extends nusoap_base {
17	/**
18	 * HTTP headers of request
19	 * @var array
20	 * @access private
21	 */
22	var $headers = array();
23	/**
24	 * HTTP request
25	 * @var string
26	 * @access private
27	 */
28	var $request = '';
29	/**
30	 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
31	 * @var string
32	 * @access public
33	 */
34	var $requestHeaders = '';
35	/**
36	 * SOAP Headers from request (parsed)
37	 * @var mixed
38	 * @access public
39	 */
40	var $requestHeader = NULL;
41	/**
42	 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
43	 * @var string
44	 * @access public
45	 */
46	var $document = '';
47	/**
48	 * SOAP payload for request (text)
49	 * @var string
50	 * @access public
51	 */
52	var $requestSOAP = '';
53	/**
54	 * requested method namespace URI
55	 * @var string
56	 * @access private
57	 */
58	var $methodURI = '';
59	/**
60	 * name of method requested
61	 * @var string
62	 * @access private
63	 */
64	var $methodname = '';
65	/**
66	 * method parameters from request
67	 * @var array
68	 * @access private
69	 */
70	var $methodparams = array();
71	/**
72	 * SOAP Action from request
73	 * @var string
74	 * @access private
75	 */
76	var $SOAPAction = '';
77	/**
78	 * character set encoding of incoming (request) messages
79	 * @var string
80	 * @access public
81	 */
82	var $xml_encoding = '';
83	/**
84	 * toggles whether the parser decodes element content w/ utf8_decode()
85	 * @var boolean
86	 * @access public
87	 */
88    var $decode_utf8 = true;
89
90	/**
91	 * HTTP headers of response
92	 * @var array
93	 * @access public
94	 */
95	var $outgoing_headers = array();
96	/**
97	 * HTTP response
98	 * @var string
99	 * @access private
100	 */
101	var $response = '';
102	/**
103	 * SOAP headers for response (text or array of soapval or associative array)
104	 * @var mixed
105	 * @access public
106	 */
107	var $responseHeaders = '';
108	/**
109	 * SOAP payload for response (text)
110	 * @var string
111	 * @access private
112	 */
113	var $responseSOAP = '';
114	/**
115	 * method return value to place in response
116	 * @var mixed
117	 * @access private
118	 */
119	var $methodreturn = false;
120	/**
121	 * whether $methodreturn is a string of literal XML
122	 * @var boolean
123	 * @access public
124	 */
125	var $methodreturnisliteralxml = false;
126	/**
127	 * SOAP fault for response (or false)
128	 * @var mixed
129	 * @access private
130	 */
131	var $fault = false;
132	/**
133	 * text indication of result (for debugging)
134	 * @var string
135	 * @access private
136	 */
137	var $result = 'successful';
138
139	/**
140	 * assoc array of operations => opData; operations are added by the register()
141	 * method or by parsing an external WSDL definition
142	 * @var array
143	 * @access private
144	 */
145	var $operations = array();
146	/**
147	 * wsdl instance (if one)
148	 * @var mixed
149	 * @access private
150	 */
151	var $wsdl = false;
152	/**
153	 * URL for WSDL (if one)
154	 * @var mixed
155	 * @access private
156	 */
157	var $externalWSDLURL = false;
158	/**
159	 * whether to append debug to response as XML comment
160	 * @var boolean
161	 * @access public
162	 */
163	var $debug_flag = false;
164
165
166	/**
167	* constructor
168    * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
169	*
170    * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
171	* @access   public
172	*/
173	function nusoap_server($wsdl=false){
174		parent::nusoap_base();
175		// turn on debugging?
176		global $debug;
177		global $HTTP_SERVER_VARS;
178
179		if (isset($_SERVER)) {
180			$this->debug("_SERVER is defined:");
181			$this->appendDebug($this->varDump($_SERVER));
182		} elseif (isset($HTTP_SERVER_VARS)) {
183			$this->debug("HTTP_SERVER_VARS is defined:");
184			$this->appendDebug($this->varDump($HTTP_SERVER_VARS));
185		} else {
186			$this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
187		}
188
189		if (isset($debug)) {
190			$this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
191			$this->debug_flag = $debug;
192		} elseif (isset($_SERVER['QUERY_STRING'])) {
193			$qs = explode('&', $_SERVER['QUERY_STRING']);
194			foreach ($qs as $v) {
195				if (substr($v, 0, 6) == 'debug=') {
196					$this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
197					$this->debug_flag = substr($v, 6);
198				}
199			}
200		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
201			$qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
202			foreach ($qs as $v) {
203				if (substr($v, 0, 6) == 'debug=') {
204					$this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
205					$this->debug_flag = substr($v, 6);
206				}
207			}
208		}
209
210		// wsdl
211		if($wsdl){
212			$this->debug("In nusoap_server, WSDL is specified");
213			if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
214				$this->wsdl = $wsdl;
215				$this->externalWSDLURL = $this->wsdl->wsdl;
216				$this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
217			} else {
218				$this->debug('Create wsdl from ' . $wsdl);
219				$this->wsdl = new wsdl($wsdl);
220				$this->externalWSDLURL = $wsdl;
221			}
222			$this->appendDebug($this->wsdl->getDebug());
223			$this->wsdl->clearDebug();
224			if($err = $this->wsdl->getError()){
225				die('WSDL ERROR: '.$err);
226			}
227		}
228	}
229
230	/**
231	* processes request and returns response
232	*
233	* @param    string $data usually is the value of $HTTP_RAW_POST_DATA
234	* @access   public
235	*/
236	function service($data){
237		global $HTTP_SERVER_VARS;
238
239		if (isset($_SERVER['QUERY_STRING'])) {
240			$qs = $_SERVER['QUERY_STRING'];
241		} elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
242			$qs = $HTTP_SERVER_VARS['QUERY_STRING'];
243		} else {
244			$qs = '';
245		}
246		$this->debug("In service, query string=$qs");
247
248		if (ereg('wsdl', $qs) ){
249			$this->debug("In service, this is a request for WSDL");
250			if($this->externalWSDLURL){
251              if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
252				header('Location: '.$this->externalWSDLURL);
253              } else { // assume file
254                header("Content-Type: text/xml\r\n");
255                $fp = fopen($this->externalWSDLURL, 'r');
256                fpassthru($fp);
257              }
258			} elseif ($this->wsdl) {
259				header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
260				print $this->wsdl->serialize($this->debug_flag);
261				if ($this->debug_flag) {
262					$this->debug('wsdl:');
263					$this->appendDebug($this->varDump($this->wsdl));
264					print $this->getDebugAsXMLComment();
265				}
266			} else {
267				header("Content-Type: text/html; charset=ISO-8859-1\r\n");
268				print "This service does not provide WSDL";
269			}
270		} elseif ($data == '' && $this->wsdl) {
271			$this->debug("In service, there is no data, so return Web description");
272			print $this->wsdl->webDescription();
273		} else {
274			$this->debug("In service, invoke the request");
275			$this->parse_request($data);
276			if (! $this->fault) {
277				$this->invoke_method();
278			}
279			if (! $this->fault) {
280				$this->serialize_return();
281			}
282			$this->send_response();
283		}
284	}
285
286	/**
287	* parses HTTP request headers.
288	*
289	* The following fields are set by this function (when successful)
290	*
291	* headers
292	* request
293	* xml_encoding
294	* SOAPAction
295	*
296	* @access   private
297	*/
298	function parse_http_headers() {
299		global $HTTP_SERVER_VARS;
300
301		$this->request = '';
302		$this->SOAPAction = '';
303		if(function_exists('getallheaders')){
304			$this->debug("In parse_http_headers, use getallheaders");
305			$headers = getallheaders();
306			foreach($headers as $k=>$v){
307				$k = strtolower($k);
308				$this->headers[$k] = $v;
309				$this->request .= "$k: $v\r\n";
310				$this->debug("$k: $v");
311			}
312			// get SOAPAction header
313			if(isset($this->headers['soapaction'])){
314				$this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
315			}
316			// get the character encoding of the incoming request
317			if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
318				$enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
319				if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
320					$this->xml_encoding = strtoupper($enc);
321				} else {
322					$this->xml_encoding = 'US-ASCII';
323				}
324			} else {
325				// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
326				$this->xml_encoding = 'ISO-8859-1';
327			}
328		} elseif(isset($_SERVER) && is_array($_SERVER)){
329			$this->debug("In parse_http_headers, use _SERVER");
330			foreach ($_SERVER as $k => $v) {
331				if (substr($k, 0, 5) == 'HTTP_') {
332					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
333				} else {
334					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
335				}
336				if ($k == 'soapaction') {
337					// get SOAPAction header
338					$k = 'SOAPAction';
339					$v = str_replace('"', '', $v);
340					$v = str_replace('\\', '', $v);
341					$this->SOAPAction = $v;
342				} else if ($k == 'content-type') {
343					// get the character encoding of the incoming request
344					if (strpos($v, '=')) {
345						$enc = substr(strstr($v, '='), 1);
346						$enc = str_replace('"', '', $enc);
347						$enc = str_replace('\\', '', $enc);
348						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
349							$this->xml_encoding = strtoupper($enc);
350						} else {
351							$this->xml_encoding = 'US-ASCII';
352						}
353					} else {
354						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
355						$this->xml_encoding = 'ISO-8859-1';
356					}
357				}
358				$this->headers[$k] = $v;
359				$this->request .= "$k: $v\r\n";
360				$this->debug("$k: $v");
361			}
362		} elseif (is_array($HTTP_SERVER_VARS)) {
363			$this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
364			foreach ($HTTP_SERVER_VARS as $k => $v) {
365				if (substr($k, 0, 5) == 'HTTP_') {
366					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 	                                         $k = strtolower(substr($k, 5));
367				} else {
368					$k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 	                                         $k = strtolower($k);
369				}
370				if ($k == 'soapaction') {
371					// get SOAPAction header
372					$k = 'SOAPAction';
373					$v = str_replace('"', '', $v);
374					$v = str_replace('\\', '', $v);
375					$this->SOAPAction = $v;
376				} else if ($k == 'content-type') {
377					// get the character encoding of the incoming request
378					if (strpos($v, '=')) {
379						$enc = substr(strstr($v, '='), 1);
380						$enc = str_replace('"', '', $enc);
381						$enc = str_replace('\\', '', $enc);
382						if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
383							$this->xml_encoding = strtoupper($enc);
384						} else {
385							$this->xml_encoding = 'US-ASCII';
386						}
387					} else {
388						// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
389						$this->xml_encoding = 'ISO-8859-1';
390					}
391				}
392				$this->headers[$k] = $v;
393				$this->request .= "$k: $v\r\n";
394				$this->debug("$k: $v");
395			}
396		} else {
397			$this->debug("In parse_http_headers, HTTP headers not accessible");
398			$this->setError("HTTP headers not accessible");
399		}
400	}
401
402	/**
403	* parses a request
404	*
405	* The following fields are set by this function (when successful)
406	*
407	* headers
408	* request
409	* xml_encoding
410	* SOAPAction
411	* request
412	* requestSOAP
413	* methodURI
414	* methodname
415	* methodparams
416	* requestHeaders
417	* document
418	*
419	* This sets the fault field on error
420	*
421	* @param    string $data XML string
422	* @access   private
423	*/
424	function parse_request($data='') {
425		$this->debug('entering parse_request()');
426		$this->parse_http_headers();
427		$this->debug('got character encoding: '.$this->xml_encoding);
428		// uncompress if necessary
429		if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
430			$this->debug('got content encoding: ' . $this->headers['content-encoding']);
431			if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
432		    	// if decoding works, use it. else assume data wasn't gzencoded
433				if (function_exists('gzuncompress')) {
434					if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
435						$data = $degzdata;
436					} elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
437						$data = $degzdata;
438					} else {
439						$this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
440						return;
441					}
442				} else {
443					$this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
444					return;
445				}
446			}
447		}
448		$this->request .= "\r\n".$data;
449		$data = $this->parseRequest($this->headers, $data);
450		$this->requestSOAP = $data;
451		$this->debug('leaving parse_request');
452	}
453
454	/**
455	* invokes a PHP function for the requested SOAP method
456	*
457	* The following fields are set by this function (when successful)
458	*
459	* methodreturn
460	*
461	* Note that the PHP function that is called may also set the following
462	* fields to affect the response sent to the client
463	*
464	* responseHeaders
465	* outgoing_headers
466	*
467	* This sets the fault field on error
468	*
469	* @access   private
470	*/
471	function invoke_method() {
472		$this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
473
474		if ($this->wsdl) {
475			if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
476				$this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
477				$this->appendDebug('opData=' . $this->varDump($this->opData));
478			} elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
479				// Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
480				$this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
481				$this->appendDebug('opData=' . $this->varDump($this->opData));
482				$this->methodname = $this->opData['name'];
483			} else {
484				$this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
485				$this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
486				return;
487			}
488		} else {
489			$this->debug('in invoke_method, no WSDL to validate method');
490		}
491
492		// if a . is present in $this->methodname, we see if there is a class in scope,
493		// which could be referred to. We will also distinguish between two deliminators,
494		// to allow methods to be called a the class or an instance
495        $class = "MamWebservice"; //TODO FIXME HELP this is absolutely hardcoded!!!
496        $method = $this->methodname; //TODO FIXME HELP this is absolutely hardcoded!!!
497//		$method = '';
498		if (strpos($this->methodname, '..') > 0) {
499			$delim = '..';
500		} else if (strpos($this->methodname, '.') > 0) {
501			$delim = '.';
502		} else if (strpos($this->methodname, '_') > 0) {
503			$delim = '_';
504		} else {
505			$delim = '';
506		}
507
508		if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
509			class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
510			// get the class and method name
511			$class = substr($this->methodname, 0, strpos($this->methodname, $delim));
512			$method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
513			$this->debug("in invoke_method, class=$class method=$method delim=$delim");
514		}
515
516		// does method exist?
517		if ($class == '') {
518			if (!function_exists($this->methodname)) {
519				$this->debug("in invoke_method, function '$this->methodname' not found!");
520				$this->result = 'fault: method not found';
521				$this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
522				return;
523			}
524		} else {
525			$method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
526			if (!in_array($method_to_compare, get_class_methods($class))) {
527				$this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
528				$this->result = 'fault: method not found';
529				$this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service");
530				return;
531			}
532		}
533
534		// evaluate message, getting back parameters
535		// verify that request parameters match the method's signature
536		if(! $this->verify_method($this->methodname,$this->methodparams)){
537			// debug
538			$this->debug('ERROR: request not verified against method signature');
539			$this->result = 'fault: request failed validation against method signature';
540			// return fault
541			$this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
542			return;
543		}
544
545		// if there are parameters to pass
546		$this->debug('in invoke_method, params:');
547		$this->appendDebug($this->varDump($this->methodparams));
548		$this->debug("in invoke_method, calling '$this->methodname'");
549		if (!function_exists('call_user_func_array')) {
550			if ($class == '') {
551				$this->debug('in invoke_method, calling function using eval()');
552				$funcCall = "\$this->methodreturn = $this->methodname(";
553			} else {
554				if ($delim == '..') {
555					$this->debug('in invoke_method, calling class method using eval()');
556					$funcCall = "\$this->methodreturn = ".$class."::".$method."(";
557				} else {
558					$this->debug('in invoke_method, calling instance method using eval()');
559					// generate unique instance name
560					$instname = "\$inst_".time();
561					$funcCall = $instname." = new ".$class."(); ";
562					$funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
563				}
564			}
565			if ($this->methodparams) {
566				foreach ($this->methodparams as $param) {
567					if (is_array($param) || is_object($param)) {
568						$this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
569						return;
570					}
571					$funcCall .= "\"$param\",";
572				}
573				$funcCall = substr($funcCall, 0, -1);
574			}
575			$funcCall .= ');';
576			$this->debug('in invoke_method, function call: '.$funcCall);
577			@eval($funcCall);
578		} else {
579			if ($class == '') {
580				$this->debug('in invoke_method, calling function using call_user_func_array()');
581				$call_arg = "$this->methodname";	// straight assignment changes $this->methodname to lower case after call_user_func_array()
582			} elseif ($delim == '..') {
583				$this->debug('in invoke_method, calling class method using call_user_func_array()');
584				$call_arg = array ($class, $method);
585			} else {
586				$this->debug('in invoke_method, calling instance method using call_user_func_array()');
587				$instance = new $class ();
588				$call_arg = array(&$instance, $method);
589			}
590			if (is_array($this->methodparams)) {
591				$this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
592			} else {
593				$this->methodreturn = call_user_func_array($call_arg, array());
594			}
595		}
596        $this->debug('in invoke_method, methodreturn:');
597        $this->appendDebug($this->varDump($this->methodreturn));
598		$this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
599	}
600
601	/**
602	* serializes the return value from a PHP function into a full SOAP Envelope
603	*
604	* The following fields are set by this function (when successful)
605	*
606	* responseSOAP
607	*
608	* This sets the fault field on error
609	*
610	* @access   private
611	*/
612	function serialize_return() {
613		$this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
614		// if fault
615		if (isset($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
616			$this->debug('got a fault object from method');
617			$this->fault = $this->methodreturn;
618			return;
619		} elseif ($this->methodreturnisliteralxml) {
620			$return_val = $this->methodreturn;
621		// returned value(s)
622		} else {
623			$this->debug('got a(n) '.gettype($this->methodreturn).' from method');
624			$this->debug('serializing return value');
625			if($this->wsdl){
626				if (sizeof($this->opData['output']['parts']) > 1) {
627					$this->debug('more than one output part, so use the method return unchanged');
628			    	$opParams = $this->methodreturn;
629			    } elseif (sizeof($this->opData['output']['parts']) == 1) {
630					$this->debug('exactly one output part, so wrap the method return in a simple array');
631					// TODO: verify that it is not already wrapped!
632			    	//foreach ($this->opData['output']['parts'] as $name => $type) {
633					//	$this->debug('wrap in element named ' . $name);
634			    	//}
635			    	$opParams = array($this->methodreturn);
636			    }
637			    $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
638			    $this->appendDebug($this->wsdl->getDebug());
639			    $this->wsdl->clearDebug();
640				if($errstr = $this->wsdl->getError()){
641					$this->debug('got wsdl error: '.$errstr);
642					$this->fault('SOAP-ENV:Server', 'unable to serialize result');
643					return;
644				}
645			} else {
646				if (isset($this->methodreturn)) {
647					$return_val = $this->serialize_val($this->methodreturn, 'return');
648				} else {
649					$return_val = '';
650					$this->debug('in absence of WSDL, assume void return for backward compatibility');
651				}
652			}
653		}
654		$this->debug('return value:');
655		$this->appendDebug($this->varDump($return_val));
656
657		$this->debug('serializing response');
658		if ($this->wsdl) {
659			$this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
660			if ($this->opData['style'] == 'rpc') {
661				$this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
662				if ($this->opData['output']['use'] == 'literal') {
663					// http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
664					$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
665				} else {
666					$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
667				}
668			} else {
669				$this->debug('style is not rpc for serialization: assume document');
670				$payload = $return_val;
671			}
672		} else {
673			$this->debug('do not have WSDL for serialization: assume rpc/encoded');
674			$payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
675		}
676		$this->result = 'successful';
677		if($this->wsdl){
678			//if($this->debug_flag){
679            	$this->appendDebug($this->wsdl->getDebug());
680            //	}
681			if (isset($opData['output']['encodingStyle'])) {
682				$encodingStyle = $opData['output']['encodingStyle'];
683			} else {
684				$encodingStyle = '';
685			}
686			// Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
687			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
688		} else {
689			$this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
690		}
691		$this->debug("Leaving serialize_return");
692	}
693
694	/**
695	* sends an HTTP response
696	*
697	* The following fields are set by this function (when successful)
698	*
699	* outgoing_headers
700	* response
701	*
702	* @access   private
703	*/
704	function send_response() {
705		$this->debug('Enter send_response');
706		if ($this->fault) {
707			$payload = $this->fault->serialize();
708			$this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
709			$this->outgoing_headers[] = "Status: 500 Internal Server Error";
710		} else {
711			$payload = $this->responseSOAP;
712			// Some combinations of PHP+Web server allow the Status
713			// to come through as a header.  Since OK is the default
714			// just do nothing.
715			// $this->outgoing_headers[] = "HTTP/1.0 200 OK";
716			// $this->outgoing_headers[] = "Status: 200 OK";
717		}
718        // add debug data if in debug mode
719		if(isset($this->debug_flag) && $this->debug_flag){
720        	$payload .= $this->getDebugAsXMLComment();
721        }
722		$this->outgoing_headers[] = "Server: $this->title Server v$this->version";
723		ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
724		$this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
725		// Let the Web server decide about this
726		//$this->outgoing_headers[] = "Connection: Close\r\n";
727		$payload = $this->getHTTPBody($payload);
728		$type = $this->getHTTPContentType();
729		$charset = $this->getHTTPContentTypeCharset();
730		$this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
731		//begin code to compress payload - by John
732		// NOTE: there is no way to know whether the Web server will also compress
733		// this data.
734		if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
735			if (strstr($this->headers['accept-encoding'], 'gzip')) {
736				if (function_exists('gzencode')) {
737					if (isset($this->debug_flag) && $this->debug_flag) {
738						$payload .= "<!-- Content being gzipped -->";
739					}
740					$this->outgoing_headers[] = "Content-Encoding: gzip";
741					$payload = gzencode($payload);
742				} else {
743					if (isset($this->debug_flag) && $this->debug_flag) {
744						$payload .= "<!-- Content will not be gzipped: no gzencode -->";
745					}
746				}
747			} elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
748				// Note: MSIE requires gzdeflate output (no Zlib header and checksum),
749				// instead of gzcompress output,
750				// which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
751				if (function_exists('gzdeflate')) {
752					if (isset($this->debug_flag) && $this->debug_flag) {
753						$payload .= "<!-- Content being deflated -->";
754					}
755					$this->outgoing_headers[] = "Content-Encoding: deflate";
756					$payload = gzdeflate($payload);
757				} else {
758					if (isset($this->debug_flag) && $this->debug_flag) {
759						$payload .= "<!-- Content will not be deflated: no gzcompress -->";
760					}
761				}
762			}
763		}
764		//end code
765		$this->outgoing_headers[] = "Content-Length: ".strlen($payload);
766		reset($this->outgoing_headers);
767		foreach($this->outgoing_headers as $hdr){
768			header($hdr, false);
769		}
770
771		print $payload;
772		$this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
773	}
774
775	/**
776	* takes the value that was created by parsing the request
777	* and compares to the method's signature, if available.
778	*
779	* @param	string	$operation	The operation to be invoked
780	* @param	array	$request	The array of parameter values
781	* @return	boolean	Whether the operation was found
782	* @access   private
783	*/
784	function verify_method($operation,$request){
785		if(isset($this->wsdl) && is_object($this->wsdl)){
786			if($this->wsdl->getOperationData($operation)){
787				return true;
788			}
789	    } elseif(isset($this->operations[$operation])){
790			return true;
791		}
792		return false;
793	}
794
795	/**
796	* processes SOAP message received from client
797	*
798	* @param	array	$headers	The HTTP headers
799	* @param	string	$data		unprocessed request data from client
800	* @return	mixed	value of the message, decoded into a PHP type
801	* @access   private
802	*/
803    function parseRequest($headers, $data) {
804		$this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
805		if (!strstr($headers['content-type'], 'text/xml')) {
806			$this->setError('Request not of type text/xml');
807			return false;
808		}
809		if (strpos($headers['content-type'], '=')) {
810			$enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
811			$this->debug('Got response encoding: ' . $enc);
812			if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
813				$this->xml_encoding = strtoupper($enc);
814			} else {
815				$this->xml_encoding = 'US-ASCII';
816			}
817		} else {
818			// should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
819			$this->xml_encoding = 'ISO-8859-1';
820		}
821		$this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
822		// parse response, get soap parser obj
823		$parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
824		// parser debug
825		$this->debug("parser debug: \n".$parser->getDebug());
826		// if fault occurred during message parsing
827		if($err = $parser->getError()){
828			$this->result = 'fault: error in msg parsing: '.$err;
829			$this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
830		// else successfully parsed request into soapval object
831		} else {
832			// get/set methodname
833			$this->methodURI = $parser->root_struct_namespace;
834			$this->methodname = $parser->root_struct_name;
835			$this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
836			$this->debug('calling parser->get_soapbody()');
837			$this->methodparams = $parser->get_soapbody();
838			// get SOAP headers
839			$this->requestHeaders = $parser->getHeaders();
840			// get SOAP Header
841			$this->requestHeader = $parser->get_soapheader();
842            // add document for doclit support
843            $this->document = $parser->document;
844		}
845	 }
846
847	/**
848	* gets the HTTP body for the current response.
849	*
850	* @param string $soapmsg The SOAP payload
851	* @return string The HTTP body, which includes the SOAP payload
852	* @access private
853	*/
854	function getHTTPBody($soapmsg) {
855		return $soapmsg;
856	}
857
858	/**
859	* gets the HTTP content type for the current response.
860	*
861	* Note: getHTTPBody must be called before this.
862	*
863	* @return string the HTTP content type for the current response.
864	* @access private
865	*/
866	function getHTTPContentType() {
867		return 'text/xml';
868	}
869
870	/**
871	* gets the HTTP content type charset for the current response.
872	* returns false for non-text content types.
873	*
874	* Note: getHTTPBody must be called before this.
875	*
876	* @return string the HTTP content type charset for the current response.
877	* @access private
878	*/
879	function getHTTPContentTypeCharset() {
880		return $this->soap_defencoding;
881	}
882
883	/**
884	* add a method to the dispatch map (this has been replaced by the register method)
885	*
886	* @param    string $methodname
887	* @param    string $in array of input values
888	* @param    string $out array of output values
889	* @access   public
890	* @deprecated
891	*/
892	function add_to_map($methodname,$in,$out){
893			$this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
894	}
895
896	/**
897	* register a service function with the server
898	*
899	* @param    string $name the name of the PHP function, class.method or class..method
900	* @param    array $in assoc array of input values: key = param name, value = param type
901	* @param    array $out assoc array of output values: key = param name, value = param type
902	* @param	mixed $namespace the element namespace for the method or false
903	* @param	mixed $soapaction the soapaction for the method or false
904	* @param	mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
905	* @param	mixed $use optional (encoded|literal) or false
906	* @param	string $documentation optional Description to include in WSDL
907	* @param	string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
908	* @access   public
909	*/
910	function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
911		global $HTTP_SERVER_VARS;
912
913		if($this->externalWSDLURL){
914			die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
915		}
916		if (! $name) {
917			die('You must specify a name when you register an operation');
918		}
919		if (!is_array($in)) {
920			die('You must provide an array for operation inputs');
921		}
922		if (!is_array($out)) {
923			die('You must provide an array for operation outputs');
924		}
925		if(false == $namespace) {
926		}
927		if(false == $soapaction) {
928			if (isset($_SERVER)) {
929				$SERVER_NAME = $_SERVER['SERVER_NAME'];
930				$SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
931				$HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
932			} elseif (isset($HTTP_SERVER_VARS)) {
933				$SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
934				$SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
935				$HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
936			} else {
937				$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
938			}
939        	if ($HTTPS == '1' || $HTTPS == 'on') {
940        		$SCHEME = 'https';
941        	} else {
942        		$SCHEME = 'http';
943        	}
944			$soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
945		}
946		if(false == $style) {
947			$style = "rpc";
948		}
949		if(false == $use) {
950			$use = "encoded";
951		}
952		if ($use == 'encoded' && $encodingStyle = '') {
953			$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
954		}
955
956		$this->operations[$name] = array(
957	    'name' => $name,
958	    'in' => $in,
959	    'out' => $out,
960	    'namespace' => $namespace,
961	    'soapaction' => $soapaction,
962	    'style' => $style);
963        if($this->wsdl){
964        	$this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
965	    }
966		return true;
967	}
968
969	/**
970	* Specify a fault to be returned to the client.
971	* This also acts as a flag to the server that a fault has occured.
972	*
973	* @param	string $faultcode
974	* @param	string $faultstring
975	* @param	string $faultactor
976	* @param	string $faultdetail
977	* @access   public
978	*/
979	function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
980		if ($faultdetail == '' && $this->debug_flag) {
981			$faultdetail = $this->getDebug();
982		}
983		$this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
984		$this->fault->soap_defencoding = $this->soap_defencoding;
985	}
986
987    /**
988    * Sets up wsdl object.
989    * Acts as a flag to enable internal WSDL generation
990    *
991    * @param string $serviceName, name of the service
992    * @param mixed $namespace optional 'tns' service namespace or false
993    * @param mixed $endpoint optional URL of service endpoint or false
994    * @param string $style optional (rpc|document) WSDL style (also specified by operation)
995    * @param string $transport optional SOAP transport
996    * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
997    */
998    function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
999    {
1000    	global $HTTP_SERVER_VARS;
1001
1002		if (isset($_SERVER)) {
1003			$SERVER_NAME = $_SERVER['SERVER_NAME'];
1004			$SERVER_PORT = $_SERVER['SERVER_PORT'];
1005			$SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
1006			$HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
1007		} elseif (isset($HTTP_SERVER_VARS)) {
1008			$SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
1009			$SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
1010			$SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
1011			$HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
1012		} else {
1013			$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
1014		}
1015		// If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
1016		$colon = strpos($SERVER_NAME,":");
1017		if ($colon) {
1018		    $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
1019		}
1020		if ($SERVER_PORT == 80) {
1021			$SERVER_PORT = '';
1022		} else {
1023			$SERVER_PORT = ':' . $SERVER_PORT;
1024		}
1025        if(false == $namespace) {
1026            $namespace = "http://$SERVER_NAME/soap/$serviceName";
1027        }
1028
1029        if(false == $endpoint) {
1030        	if ($HTTPS == '1' || $HTTPS == 'on') {
1031        		$SCHEME = 'https';
1032        	} else {
1033        		$SCHEME = 'http';
1034        	}
1035            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
1036        }
1037
1038        if(false == $schemaTargetNamespace) {
1039            $schemaTargetNamespace = $namespace;
1040        }
1041
1042		$this->wsdl = new wsdl;
1043		$this->wsdl->serviceName = $serviceName;
1044        $this->wsdl->endpoint = $endpoint;
1045		$this->wsdl->namespaces['tns'] = $namespace;
1046		$this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
1047		$this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
1048		if ($schemaTargetNamespace != $namespace) {
1049			$this->wsdl->namespaces['types'] = $schemaTargetNamespace;
1050		}
1051        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
1052        if ($style == 'document') {
1053	        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
1054        }
1055        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
1056        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
1057        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
1058        $this->wsdl->bindings[$serviceName.'Binding'] = array(
1059        	'name'=>$serviceName.'Binding',
1060            'style'=>$style,
1061            'transport'=>$transport,
1062            'portType'=>$serviceName.'PortType');
1063        $this->wsdl->ports[$serviceName.'Port'] = array(
1064        	'binding'=>$serviceName.'Binding',
1065            'location'=>$endpoint,
1066            'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
1067    }
1068}
1069
1070/**
1071 * Backward compatibility
1072 */
1073class soap_server extends nusoap_server {
1074}
1075
1076
1077?>