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