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