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