5 Modification information for LGPL compliance
7 r57813 - 2010-08-19 10:34:44 -0700 (Thu, 19 Aug 2010) - kjing - Author: John Mertic <jmertic@sugarcrm.com>
8 Bug 39085 - When loading the opposite search panel via ajax on the ListViews, call the index action instead of the ListView action to avoid touching pre-MVC code by accident.
10 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
12 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
14 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
16 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3 tags and updated the build system
18 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
20 r51443 - 2009-10-12 13:34:36 -0700 (Mon, 12 Oct 2009) - jmertic - Bug 33332 - Made application PHP 5.3 compliant with E_DEPRECATED warnings on by:
21 - Changing all ereg function to either preg or simple string based ones
22 - No more references to magic quotes.
23 - Change all the session_unregister() functions to just unset() the correct session variable instead.
25 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
27 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
29 r13782 - 2006-06-06 10:58:55 -0700 (Tue, 06 Jun 2006) - majed - changes entry point code
31 r11115 - 2006-01-17 14:54:45 -0800 (Tue, 17 Jan 2006) - majed - add entry point validation
33 r8846 - 2005-10-31 11:01:12 -0800 (Mon, 31 Oct 2005) - majed - new version of nusoap
35 r5462 - 2005-05-25 13:50:11 -0700 (Wed, 25 May 2005) - majed - upgraded nusoap to .6.9
37 r5104 - 2005-05-04 15:33:41 -0700 (Wed, 04 May 2005) - majed - gets rid of HTTP_GET_VARS and what not which has been deprecated
39 r573 - 2004-09-04 13:03:32 -0700 (Sat, 04 Sep 2004) - sugarclint - undoing copyrights added in inadvertantly. --clint
41 r546 - 2004-09-03 11:49:38 -0700 (Fri, 03 Sep 2004) - sugarmsi - removed echo count
43 r354 - 2004-08-02 23:00:37 -0700 (Mon, 02 Aug 2004) - sugarjacob - Adding Soap
49 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
56 * nusoap_server allows the user to create a SOAP server
57 * that is capable of receiving messages and returning responses
59 * NOTE: WSDL functionality is experimental
61 * @author Dietrich Ayala <dietrich@ganx4.com>
62 * @author Scott Nichol <snichol@users.sourceforge.net>
66 class nusoap_server extends nusoap_base {
68 * HTTP headers of request
72 var $headers = array();
80 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
84 var $requestHeaders = '';
86 * SOAP Headers from request (parsed)
90 var $requestHeader = NULL;
92 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
98 * SOAP payload for request (text)
102 var $requestSOAP = '';
104 * requested method namespace URI
110 * name of method requested
114 var $methodname = '';
116 * method parameters from request
120 var $methodparams = array();
122 * SOAP Action from request
126 var $SOAPAction = '';
128 * character set encoding of incoming (request) messages
132 var $xml_encoding = '';
134 * toggles whether the parser decodes element content w/ utf8_decode()
138 var $decode_utf8 = true;
141 * HTTP headers of response
145 var $outgoing_headers = array();
153 * SOAP headers for response (text or array of soapval or associative array)
157 var $responseHeaders = '';
159 * SOAP payload for response (text)
163 var $responseSOAP = '';
165 * method return value to place in response
169 var $methodreturn = false;
171 * whether $methodreturn is a string of literal XML
175 var $methodreturnisliteralxml = false;
177 * SOAP fault for response (or false)
183 * text indication of result (for debugging)
187 var $result = 'successful';
190 * assoc array of operations => opData; operations are added by the register()
191 * method or by parsing an external WSDL definition
195 var $operations = array();
197 * wsdl instance (if one)
203 * URL for WSDL (if one)
207 var $externalWSDLURL = false;
209 * whether to append debug to response as XML comment
213 var $debug_flag = false;
218 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
220 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
223 function nusoap_server($wsdl=false){
224 parent::nusoap_base();
225 // turn on debugging?
227 global $HTTP_SERVER_VARS;
229 if (isset($_SERVER)) {
230 $this->debug("_SERVER is defined:");
231 $this->appendDebug($this->varDump($_SERVER));
232 } elseif (isset($HTTP_SERVER_VARS)) {
233 $this->debug("HTTP_SERVER_VARS is defined:");
234 $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
236 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
240 $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
241 $this->debug_flag = $debug;
242 } elseif (isset($_SERVER['QUERY_STRING'])) {
243 $qs = explode('&', $_SERVER['QUERY_STRING']);
244 foreach ($qs as $v) {
245 if (substr($v, 0, 6) == 'debug=') {
246 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
247 $this->debug_flag = substr($v, 6);
250 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
251 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
252 foreach ($qs as $v) {
253 if (substr($v, 0, 6) == 'debug=') {
254 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
255 $this->debug_flag = substr($v, 6);
262 $this->debug("In nusoap_server, WSDL is specified");
263 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
265 $this->externalWSDLURL = $this->wsdl->wsdl;
266 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
268 $this->debug('Create wsdl from ' . $wsdl);
269 $this->wsdl = new wsdl($wsdl);
270 $this->externalWSDLURL = $wsdl;
272 $this->appendDebug($this->wsdl->getDebug());
273 $this->wsdl->clearDebug();
274 if($err = $this->wsdl->getError()){
275 die('WSDL ERROR: '.$err);
281 * processes request and returns response
283 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
286 function service($data){
287 global $HTTP_SERVER_VARS;
289 if (isset($_SERVER['REQUEST_METHOD'])) {
290 $rm = $_SERVER['REQUEST_METHOD'];
291 } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
292 $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
297 if (isset($_SERVER['QUERY_STRING'])) {
298 $qs = $_SERVER['QUERY_STRING'];
299 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
300 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
304 $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
307 $this->debug("In service, invoke the request");
308 $this->parse_request($data);
309 if (! $this->fault) {
310 $this->invoke_method();
312 if (! $this->fault) {
313 $this->serialize_return();
315 $this->send_response();
316 } elseif (preg_match('/wsdl/', $qs) ){
317 $this->debug("In service, this is a request for WSDL");
318 if ($this->externalWSDLURL){
319 if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
320 $this->debug("In service, re-direct for WSDL");
321 header('Location: '.$this->externalWSDLURL);
322 } else { // assume file
323 $this->debug("In service, use file passthru for WSDL");
324 header("Content-Type: text/xml\r\n");
325 $pos = strpos($this->externalWSDLURL, "file://");
326 if ($pos === false) {
327 $filename = $this->externalWSDLURL;
329 $filename = substr($this->externalWSDLURL, $pos + 7);
331 $fp = fopen($this->externalWSDLURL, 'r');
334 } elseif ($this->wsdl) {
335 $this->debug("In service, serialize WSDL");
336 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
337 print $this->wsdl->serialize($this->debug_flag);
338 if ($this->debug_flag) {
339 $this->debug('wsdl:');
340 $this->appendDebug($this->varDump($this->wsdl));
341 print $this->getDebugAsXMLComment();
344 $this->debug("In service, there is no WSDL");
345 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
346 print "This service does not provide WSDL";
348 } elseif ($this->wsdl) {
349 $this->debug("In service, return Web description");
350 print $this->wsdl->webDescription();
352 $this->debug("In service, no Web description");
353 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
354 print "This service does not provide a Web description";
359 * parses HTTP request headers.
361 * The following fields are set by this function (when successful)
370 function parse_http_headers() {
371 global $HTTP_SERVER_VARS;
374 $this->SOAPAction = '';
375 if(function_exists('getallheaders')){
376 $this->debug("In parse_http_headers, use getallheaders");
377 $headers = getallheaders();
378 foreach($headers as $k=>$v){
380 $this->headers[$k] = $v;
381 $this->request .= "$k: $v\r\n";
382 $this->debug("$k: $v");
384 // get SOAPAction header
385 if(isset($this->headers['soapaction'])){
386 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
388 // get the character encoding of the incoming request
389 if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
390 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
391 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
392 $this->xml_encoding = strtoupper($enc);
394 $this->xml_encoding = 'US-ASCII';
397 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
398 $this->xml_encoding = 'ISO-8859-1';
400 } elseif(isset($_SERVER) && is_array($_SERVER)){
401 $this->debug("In parse_http_headers, use _SERVER");
402 foreach ($_SERVER as $k => $v) {
403 if (substr($k, 0, 5) == 'HTTP_') {
404 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
406 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
408 if ($k == 'soapaction') {
409 // get SOAPAction header
411 $v = str_replace('"', '', $v);
412 $v = str_replace('\\', '', $v);
413 $this->SOAPAction = $v;
414 } else if ($k == 'content-type') {
415 // get the character encoding of the incoming request
416 if (strpos($v, '=')) {
417 $enc = substr(strstr($v, '='), 1);
418 $enc = str_replace('"', '', $enc);
419 $enc = str_replace('\\', '', $enc);
420 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
421 $this->xml_encoding = strtoupper($enc);
423 $this->xml_encoding = 'US-ASCII';
426 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
427 $this->xml_encoding = 'ISO-8859-1';
430 $this->headers[$k] = $v;
431 $this->request .= "$k: $v\r\n";
432 $this->debug("$k: $v");
434 } elseif (is_array($HTTP_SERVER_VARS)) {
435 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
436 foreach ($HTTP_SERVER_VARS as $k => $v) {
437 if (substr($k, 0, 5) == 'HTTP_') {
438 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
440 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
442 if ($k == 'soapaction') {
443 // get SOAPAction header
445 $v = str_replace('"', '', $v);
446 $v = str_replace('\\', '', $v);
447 $this->SOAPAction = $v;
448 } else if ($k == 'content-type') {
449 // get the character encoding of the incoming request
450 if (strpos($v, '=')) {
451 $enc = substr(strstr($v, '='), 1);
452 $enc = str_replace('"', '', $enc);
453 $enc = str_replace('\\', '', $enc);
454 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
455 $this->xml_encoding = strtoupper($enc);
457 $this->xml_encoding = 'US-ASCII';
460 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
461 $this->xml_encoding = 'ISO-8859-1';
464 $this->headers[$k] = $v;
465 $this->request .= "$k: $v\r\n";
466 $this->debug("$k: $v");
469 $this->debug("In parse_http_headers, HTTP headers not accessible");
470 $this->setError("HTTP headers not accessible");
477 * The following fields are set by this function (when successful)
491 * This sets the fault field on error
493 * @param string $data XML string
496 function parse_request($data='') {
497 $this->debug('entering parse_request()');
498 $this->parse_http_headers();
499 $this->debug('got character encoding: '.$this->xml_encoding);
500 // uncompress if necessary
501 if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
502 $this->debug('got content encoding: ' . $this->headers['content-encoding']);
503 if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
504 // if decoding works, use it. else assume data wasn't gzencoded
505 if (function_exists('gzuncompress')) {
506 if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
508 } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
511 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
515 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
520 $this->request .= "\r\n".$data;
521 $data = $this->parseRequest($this->headers, $data);
522 $this->requestSOAP = $data;
523 $this->debug('leaving parse_request');
527 * invokes a PHP function for the requested SOAP method
529 * The following fields are set by this function (when successful)
533 * Note that the PHP function that is called may also set the following
534 * fields to affect the response sent to the client
539 * This sets the fault field on error
543 function invoke_method() {
544 $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
547 // if you are debugging in this area of the code, your service uses a class to implement methods,
548 // you use SOAP RPC, and the client is .NET, please be aware of the following...
549 // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
550 // method name. that is fine for naming the .NET methods. it is not fine for properly constructing
551 // the XML request and reading the XML response. you need to add the RequestElementName and
552 // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
553 // generates for the method. these parameters are used to specify the correct XML element names
554 // for .NET to use, i.e. the names with the '.' in them.
556 $orig_methodname = $this->methodname;
558 if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
559 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
560 $this->appendDebug('opData=' . $this->varDump($this->opData));
561 } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
562 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
563 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
564 $this->appendDebug('opData=' . $this->varDump($this->opData));
565 $this->methodname = $this->opData['name'];
567 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
568 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
572 $this->debug('in invoke_method, no WSDL to validate method');
575 // if a . is present in $this->methodname, we see if there is a class in scope,
576 // which could be referred to. We will also distinguish between two deliminators,
577 // to allow methods to be called a the class or an instance
578 if (strpos($this->methodname, '..') > 0) {
580 } else if (strpos($this->methodname, '.') > 0) {
585 $this->debug("in invoke_method, delim=$delim");
589 if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
590 $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
591 if (class_exists($try_class)) {
592 // get the class and method name
594 $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
595 $this->debug("in invoke_method, class=$class method=$method delim=$delim");
597 $this->debug("in invoke_method, class=$try_class not found");
601 $this->debug("in invoke_method, no class to try");
604 // does method exist?
606 if (!function_exists($this->methodname)) {
607 $this->debug("in invoke_method, function '$this->methodname' not found!");
608 $this->result = 'fault: method not found';
609 $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
613 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
614 if (!in_array($method_to_compare, get_class_methods($class))) {
615 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
616 $this->result = 'fault: method not found';
617 $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
622 // evaluate message, getting back parameters
623 // verify that request parameters match the method's signature
624 if(! $this->verify_method($this->methodname,$this->methodparams)){
626 $this->debug('ERROR: request not verified against method signature');
627 $this->result = 'fault: request failed validation against method signature';
629 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
633 // if there are parameters to pass
634 $this->debug('in invoke_method, params:');
635 $this->appendDebug($this->varDump($this->methodparams));
636 $this->debug("in invoke_method, calling '$this->methodname'");
637 if (!function_exists('call_user_func_array')) {
639 $this->debug('in invoke_method, calling function using eval()');
640 $funcCall = "\$this->methodreturn = $this->methodname(";
642 if ($delim == '..') {
643 $this->debug('in invoke_method, calling class method using eval()');
644 $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
646 $this->debug('in invoke_method, calling instance method using eval()');
647 // generate unique instance name
648 $instname = "\$inst_".time();
649 $funcCall = $instname." = new ".$class."(); ";
650 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
653 if ($this->methodparams) {
654 foreach ($this->methodparams as $param) {
655 if (is_array($param) || is_object($param)) {
656 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
659 $funcCall .= "\"$param\",";
661 $funcCall = substr($funcCall, 0, -1);
664 $this->debug('in invoke_method, function call: '.$funcCall);
668 $this->debug('in invoke_method, calling function using call_user_func_array()');
669 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
670 } elseif ($delim == '..') {
671 $this->debug('in invoke_method, calling class method using call_user_func_array()');
672 $call_arg = array ($class, $method);
674 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
675 $instance = new $class ();
676 $call_arg = array(&$instance, $method);
678 if (is_array($this->methodparams)) {
679 $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
681 $this->methodreturn = call_user_func_array($call_arg, array());
684 $this->debug('in invoke_method, methodreturn:');
685 $this->appendDebug($this->varDump($this->methodreturn));
686 $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
690 * serializes the return value from a PHP function into a full SOAP Envelope
692 * The following fields are set by this function (when successful)
696 * This sets the fault field on error
700 function serialize_return() {
701 $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
703 if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
704 $this->debug('got a fault object from method');
705 $this->fault = $this->methodreturn;
707 } elseif ($this->methodreturnisliteralxml) {
708 $return_val = $this->methodreturn;
711 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
712 $this->debug('serializing return value');
714 if (sizeof($this->opData['output']['parts']) > 1) {
715 $this->debug('more than one output part, so use the method return unchanged');
716 $opParams = $this->methodreturn;
717 } elseif (sizeof($this->opData['output']['parts']) == 1) {
718 $this->debug('exactly one output part, so wrap the method return in a simple array');
719 // TODO: verify that it is not already wrapped!
720 //foreach ($this->opData['output']['parts'] as $name => $type) {
721 // $this->debug('wrap in element named ' . $name);
723 $opParams = array($this->methodreturn);
725 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
726 $this->appendDebug($this->wsdl->getDebug());
727 $this->wsdl->clearDebug();
728 if($errstr = $this->wsdl->getError()){
729 $this->debug('got wsdl error: '.$errstr);
730 $this->fault('SOAP-ENV:Server', 'unable to serialize result');
734 if (isset($this->methodreturn)) {
735 $return_val = $this->serialize_val($this->methodreturn, 'return');
738 $this->debug('in absence of WSDL, assume void return for backward compatibility');
742 $this->debug('return value:');
743 $this->appendDebug($this->varDump($return_val));
745 $this->debug('serializing response');
747 $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
748 if ($this->opData['style'] == 'rpc') {
749 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
750 if ($this->opData['output']['use'] == 'literal') {
751 // 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
752 if ($this->methodURI) {
753 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
755 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
758 if ($this->methodURI) {
759 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
761 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
765 $this->debug('style is not rpc for serialization: assume document');
766 $payload = $return_val;
769 $this->debug('do not have WSDL for serialization: assume rpc/encoded');
770 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
772 $this->result = 'successful';
774 //if($this->debug_flag){
775 $this->appendDebug($this->wsdl->getDebug());
777 if (isset($this->opData['output']['encodingStyle'])) {
778 $encodingStyle = $this->opData['output']['encodingStyle'];
782 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
783 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
785 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
787 $this->debug("Leaving serialize_return");
791 * sends an HTTP response
793 * The following fields are set by this function (when successful)
800 function send_response() {
801 $this->debug('Enter send_response');
803 $payload = $this->fault->serialize();
804 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
805 $this->outgoing_headers[] = "Status: 500 Internal Server Error";
807 $payload = $this->responseSOAP;
808 // Some combinations of PHP+Web server allow the Status
809 // to come through as a header. Since OK is the default
811 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
812 // $this->outgoing_headers[] = "Status: 200 OK";
814 // add debug data if in debug mode
815 if(isset($this->debug_flag) && $this->debug_flag){
816 $payload .= $this->getDebugAsXMLComment();
818 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
819 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
820 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
821 // Let the Web server decide about this
822 //$this->outgoing_headers[] = "Connection: Close\r\n";
823 $payload = $this->getHTTPBody($payload);
824 $type = $this->getHTTPContentType();
825 $charset = $this->getHTTPContentTypeCharset();
826 $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
827 //begin code to compress payload - by John
828 // NOTE: there is no way to know whether the Web server will also compress
830 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
831 if (strstr($this->headers['accept-encoding'], 'gzip')) {
832 if (function_exists('gzencode')) {
833 if (isset($this->debug_flag) && $this->debug_flag) {
834 $payload .= "<!-- Content being gzipped -->";
836 $this->outgoing_headers[] = "Content-Encoding: gzip";
837 $payload = gzencode($payload);
839 if (isset($this->debug_flag) && $this->debug_flag) {
840 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
843 } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
844 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
845 // instead of gzcompress output,
846 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
847 if (function_exists('gzdeflate')) {
848 if (isset($this->debug_flag) && $this->debug_flag) {
849 $payload .= "<!-- Content being deflated -->";
851 $this->outgoing_headers[] = "Content-Encoding: deflate";
852 $payload = gzdeflate($payload);
854 if (isset($this->debug_flag) && $this->debug_flag) {
855 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
861 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
862 reset($this->outgoing_headers);
863 foreach($this->outgoing_headers as $hdr){
867 $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
871 * takes the value that was created by parsing the request
872 * and compares to the method's signature, if available.
874 * @param string $operation The operation to be invoked
875 * @param array $request The array of parameter values
876 * @return boolean Whether the operation was found
879 function verify_method($operation,$request){
880 if(isset($this->wsdl) && is_object($this->wsdl)){
881 if($this->wsdl->getOperationData($operation)){
884 } elseif(isset($this->operations[$operation])){
891 * processes SOAP message received from client
893 * @param array $headers The HTTP headers
894 * @param string $data unprocessed request data from client
895 * @return mixed value of the message, decoded into a PHP type
898 function parseRequest($headers, $data) {
899 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
900 $this->appendDebug($this->varDump($headers));
901 if (!isset($headers['content-type'])) {
902 $this->setError('Request not of type text/xml (no content-type header)');
905 if (!strstr($headers['content-type'], 'text/xml')) {
906 $this->setError('Request not of type text/xml');
909 if (strpos($headers['content-type'], '=')) {
910 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
911 $this->debug('Got response encoding: ' . $enc);
912 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
913 $this->xml_encoding = strtoupper($enc);
915 $this->xml_encoding = 'US-ASCII';
918 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
919 $this->xml_encoding = 'ISO-8859-1';
921 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
922 // parse response, get soap parser obj
923 $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
925 $this->debug("parser debug: \n".$parser->getDebug());
926 // if fault occurred during message parsing
927 if($err = $parser->getError()){
928 $this->result = 'fault: error in msg parsing: '.$err;
929 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
930 // else successfully parsed request into soapval object
932 // get/set methodname
933 $this->methodURI = $parser->root_struct_namespace;
934 $this->methodname = $parser->root_struct_name;
935 $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
936 $this->debug('calling parser->get_soapbody()');
937 $this->methodparams = $parser->get_soapbody();
939 $this->requestHeaders = $parser->getHeaders();
941 $this->requestHeader = $parser->get_soapheader();
942 // add document for doclit support
943 $this->document = $parser->document;
948 * gets the HTTP body for the current response.
950 * @param string $soapmsg The SOAP payload
951 * @return string The HTTP body, which includes the SOAP payload
954 function getHTTPBody($soapmsg) {
959 * gets the HTTP content type for the current response.
961 * Note: getHTTPBody must be called before this.
963 * @return string the HTTP content type for the current response.
966 function getHTTPContentType() {
971 * gets the HTTP content type charset for the current response.
972 * returns false for non-text content types.
974 * Note: getHTTPBody must be called before this.
976 * @return string the HTTP content type charset for the current response.
979 function getHTTPContentTypeCharset() {
980 return $this->soap_defencoding;
984 * add a method to the dispatch map (this has been replaced by the register method)
986 * @param string $methodname
987 * @param string $in array of input values
988 * @param string $out array of output values
992 function add_to_map($methodname,$in,$out){
993 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
997 * register a service function with the server
999 * @param string $name the name of the PHP function, class.method or class..method
1000 * @param array $in assoc array of input values: key = param name, value = param type
1001 * @param array $out assoc array of output values: key = param name, value = param type
1002 * @param mixed $namespace the element namespace for the method or false
1003 * @param mixed $soapaction the soapaction for the method or false
1004 * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
1005 * @param mixed $use optional (encoded|literal) or false
1006 * @param string $documentation optional Description to include in WSDL
1007 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
1010 function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
1011 global $HTTP_SERVER_VARS;
1013 if($this->externalWSDLURL){
1014 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
1017 die('You must specify a name when you register an operation');
1019 if (!is_array($in)) {
1020 die('You must provide an array for operation inputs');
1022 if (!is_array($out)) {
1023 die('You must provide an array for operation outputs');
1025 if(false == $namespace) {
1027 if(false == $soapaction) {
1028 if (isset($_SERVER)) {
1029 $SERVER_NAME = $_SERVER['SERVER_NAME'];
1030 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
1031 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
1032 } elseif (isset($HTTP_SERVER_VARS)) {
1033 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
1034 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
1035 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
1037 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
1039 if ($HTTPS == '1' || $HTTPS == 'on') {
1044 $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
1046 if(false == $style) {
1052 if ($use == 'encoded' && $encodingStyle == '') {
1053 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
1056 $this->operations[$name] = array(
1060 'namespace' => $namespace,
1061 'soapaction' => $soapaction,
1064 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
1070 * Specify a fault to be returned to the client.
1071 * This also acts as a flag to the server that a fault has occured.
1073 * @param string $faultcode
1074 * @param string $faultstring
1075 * @param string $faultactor
1076 * @param string $faultdetail
1079 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
1080 if ($faultdetail == '' && $this->debug_flag) {
1081 $faultdetail = $this->getDebug();
1083 $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
1084 $this->fault->soap_defencoding = $this->soap_defencoding;
1088 * Sets up wsdl object.
1089 * Acts as a flag to enable internal WSDL generation
1091 * @param string $serviceName, name of the service
1092 * @param mixed $namespace optional 'tns' service namespace or false
1093 * @param mixed $endpoint optional URL of service endpoint or false
1094 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
1095 * @param string $transport optional SOAP transport
1096 * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
1098 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
1100 global $HTTP_SERVER_VARS;
1102 if (isset($_SERVER)) {
1103 $SERVER_NAME = $_SERVER['SERVER_NAME'];
1104 $SERVER_PORT = $_SERVER['SERVER_PORT'];
1105 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
1106 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
1107 } elseif (isset($HTTP_SERVER_VARS)) {
1108 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
1109 $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
1110 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
1111 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
1113 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
1115 // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
1116 $colon = strpos($SERVER_NAME,":");
1118 $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
1120 if ($SERVER_PORT == 80) {
1123 $SERVER_PORT = ':' . $SERVER_PORT;
1125 if(false == $namespace) {
1126 $namespace = "http://$SERVER_NAME/soap/$serviceName";
1129 if(false == $endpoint) {
1130 if ($HTTPS == '1' || $HTTPS == 'on') {
1135 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
1138 if(false == $schemaTargetNamespace) {
1139 $schemaTargetNamespace = $namespace;
1142 $this->wsdl = new wsdl;
1143 $this->wsdl->serviceName = $serviceName;
1144 $this->wsdl->endpoint = $endpoint;
1145 $this->wsdl->namespaces['tns'] = $namespace;
1146 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
1147 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
1148 if ($schemaTargetNamespace != $namespace) {
1149 $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
1151 $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
1152 if ($style == 'document') {
1153 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
1155 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
1156 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
1157 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
1158 $this->wsdl->bindings[$serviceName.'Binding'] = array(
1159 'name'=>$serviceName.'Binding',
1161 'transport'=>$transport,
1162 'portType'=>$serviceName.'PortType');
1163 $this->wsdl->ports[$serviceName.'Port'] = array(
1164 'binding'=>$serviceName.'Binding',
1165 'location'=>$endpoint,
1166 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
1171 * Backward compatibility
1173 class soap_server extends nusoap_server {