[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/nusoap/ -> nusoap.php (source)

   1  <?php
   2  
   3  /*
   4  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
   5  
   6  NuSOAP - Web Services Toolkit for PHP
   7  
   8  Copyright (c) 2002 NuSphere Corporation
   9  
  10  This library is free software; you can redistribute it and/or
  11  modify it under the terms of the GNU Lesser General Public
  12  License as published by the Free Software Foundation; either
  13  version 2.1 of the License, or (at your option) any later version.
  14  
  15  This library is distributed in the hope that it will be useful,
  16  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18  Lesser General Public License for more details.
  19  
  20  You should have received a copy of the GNU Lesser General Public
  21  License along with this library; if not, write to the Free Software
  22  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  23  
  24  The NuSOAP project home is:
  25  http://sourceforge.net/projects/nusoap/
  26  
  27  The primary support for NuSOAP is the Help forum on the project home page.
  28  
  29  If you have any questions or comments, please email:
  30  
  31  Dietrich Ayala
  32  dietrich@ganx4.com
  33  http://dietrich.ganx4.com/nusoap
  34  
  35  NuSphere Corporation
  36  http://www.nusphere.com
  37  
  38  */
  39  
  40  /*
  41   *    Some of the standards implmented in whole or part by NuSOAP:
  42   *
  43   *    SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
  44   *    WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
  45   *    SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
  46   *    XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
  47   *    Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
  48   *    XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
  49   *    RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
  50   *    RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
  51   *    RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
  52   */
  53  
  54  /* load classes
  55  
  56  // necessary classes
  57  require_once('class.soapclient.php');
  58  require_once('class.soap_val.php');
  59  require_once('class.soap_parser.php');
  60  require_once('class.soap_fault.php');
  61  
  62  // transport classes
  63  require_once('class.soap_transport_http.php');
  64  
  65  // optional add-on classes
  66  require_once('class.xmlschema.php');
  67  require_once('class.wsdl.php');
  68  
  69  // server class
  70  require_once('class.soap_server.php');*/
  71  
  72  // class variable emulation
  73  // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
  74  $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
  75  
  76  /**
  77  *
  78  * nusoap_base
  79  *
  80  * @author   Dietrich Ayala <dietrich@ganx4.com>
  81  * @author   Scott Nichol <snichol@users.sourceforge.net>
  82  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
  83  * @access   public
  84  */
  85  class nusoap_base {
  86      /**
  87       * Identification for HTTP headers.
  88       *
  89       * @var string
  90       * @access private
  91       */
  92      var $title = 'NuSOAP';
  93      /**
  94       * Version for HTTP headers.
  95       *
  96       * @var string
  97       * @access private
  98       */
  99      var $version = '0.9.5';
 100      /**
 101       * CVS revision for HTTP headers.
 102       *
 103       * @var string
 104       * @access private
 105       */
 106      var $revision = '$Revision: 1.123 $';
 107      /**
 108       * Current error string (manipulated by getError/setError)
 109       *
 110       * @var string
 111       * @access private
 112       */
 113      var $error_str = '';
 114      /**
 115       * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
 116       *
 117       * @var string
 118       * @access private
 119       */
 120      var $debug_str = '';
 121      /**
 122       * toggles automatic encoding of special characters as entities
 123       * (should always be true, I think)
 124       *
 125       * @var boolean
 126       * @access private
 127       */
 128      var $charencoding = true;
 129      /**
 130       * the debug level for this instance
 131       *
 132       * @var    integer
 133       * @access private
 134       */
 135      var $debugLevel;
 136  
 137      /**
 138      * set schema version
 139      *
 140      * @var      string
 141      * @access   public
 142      */
 143      var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
 144      
 145      /**
 146      * charset encoding for outgoing messages
 147      *
 148      * @var      string
 149      * @access   public
 150      */
 151      var $soap_defencoding = 'ISO-8859-1';
 152      //var $soap_defencoding = 'UTF-8';
 153  
 154      /**
 155      * namespaces in an array of prefix => uri
 156      *
 157      * this is "seeded" by a set of constants, but it may be altered by code
 158      *
 159      * @var      array
 160      * @access   public
 161      */
 162      var $namespaces = array(
 163          'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
 164          'xsd' => 'http://www.w3.org/2001/XMLSchema',
 165          'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
 166          'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
 167          );
 168  
 169      /**
 170      * namespaces used in the current context, e.g. during serialization
 171      *
 172      * @var      array
 173      * @access   private
 174      */
 175      var $usedNamespaces = array();
 176  
 177      /**
 178      * XML Schema types in an array of uri => (array of xml type => php type)
 179      * is this legacy yet?
 180      * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
 181      * @var      array
 182      * @access   public
 183      */
 184      var $typemap = array(
 185      'http://www.w3.org/2001/XMLSchema' => array(
 186          'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
 187          'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
 188          'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
 189          // abstract "any" types
 190          'anyType'=>'string','anySimpleType'=>'string',
 191          // derived datatypes
 192          'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
 193          'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
 194          'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
 195          'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
 196      'http://www.w3.org/2000/10/XMLSchema' => array(
 197          'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
 198          'float'=>'double','dateTime'=>'string',
 199          'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
 200      'http://www.w3.org/1999/XMLSchema' => array(
 201          'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
 202          'float'=>'double','dateTime'=>'string',
 203          'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
 204      'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
 205      'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
 206      'http://xml.apache.org/xml-soap' => array('Map')
 207      );
 208  
 209      /**
 210      * XML entities to convert
 211      *
 212      * @var      array
 213      * @access   public
 214      * @deprecated
 215      * @see    expandEntities
 216      */
 217      var $xmlEntities = array('quot' => '"','amp' => '&',
 218          'lt' => '<','gt' => '>','apos' => "'");
 219  
 220      /**
 221      * constructor
 222      *
 223      * @access    public
 224      */
 225  	function nusoap_base() {
 226          $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
 227      }
 228  
 229      /**
 230      * gets the global debug level, which applies to future instances
 231      *
 232      * @return    integer    Debug level 0-9, where 0 turns off
 233      * @access    public
 234      */
 235  	function getGlobalDebugLevel() {
 236          return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
 237      }
 238  
 239      /**
 240      * sets the global debug level, which applies to future instances
 241      *
 242      * @param    int    $level    Debug level 0-9, where 0 turns off
 243      * @access    public
 244      */
 245  	function setGlobalDebugLevel($level) {
 246          $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
 247      }
 248  
 249      /**
 250      * gets the debug level for this instance
 251      *
 252      * @return    int    Debug level 0-9, where 0 turns off
 253      * @access    public
 254      */
 255  	function getDebugLevel() {
 256          return $this->debugLevel;
 257      }
 258  
 259      /**
 260      * sets the debug level for this instance
 261      *
 262      * @param    int    $level    Debug level 0-9, where 0 turns off
 263      * @access    public
 264      */
 265  	function setDebugLevel($level) {
 266          $this->debugLevel = $level;
 267      }
 268  
 269      /**
 270      * adds debug data to the instance debug string with formatting
 271      *
 272      * @param    string $string debug data
 273      * @access   private
 274      */
 275  	function debug($string){
 276          if ($this->debugLevel > 0) {
 277              $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
 278          }
 279      }
 280  
 281      /**
 282      * adds debug data to the instance debug string without formatting
 283      *
 284      * @param    string $string debug data
 285      * @access   public
 286      */
 287  	function appendDebug($string){
 288          if ($this->debugLevel > 0) {
 289              // it would be nice to use a memory stream here to use
 290              // memory more efficiently
 291              $this->debug_str .= $string;
 292          }
 293      }
 294  
 295      /**
 296      * clears the current debug data for this instance
 297      *
 298      * @access   public
 299      */
 300  	function clearDebug() {
 301          // it would be nice to use a memory stream here to use
 302          // memory more efficiently
 303          $this->debug_str = '';
 304      }
 305  
 306      /**
 307      * gets the current debug data for this instance
 308      *
 309      * @return   debug data
 310      * @access   public
 311      */
 312      function &getDebug() {
 313          // it would be nice to use a memory stream here to use
 314          // memory more efficiently
 315          return $this->debug_str;
 316      }
 317  
 318      /**
 319      * gets the current debug data for this instance as an XML comment
 320      * this may change the contents of the debug data
 321      *
 322      * @return   debug data as an XML comment
 323      * @access   public
 324      */
 325      function &getDebugAsXMLComment() {
 326          // it would be nice to use a memory stream here to use
 327          // memory more efficiently
 328          while (strpos($this->debug_str, '--')) {
 329              $this->debug_str = str_replace('--', '- -', $this->debug_str);
 330          }
 331          $ret = "<!--\n" . $this->debug_str . "\n-->";
 332          return $ret;
 333      }
 334  
 335      /**
 336      * expands entities, e.g. changes '<' to '&lt;'.
 337      *
 338      * @param    string    $val    The string in which to expand entities.
 339      * @access    private
 340      */
 341  	function expandEntities($val) {
 342          if ($this->charencoding) {
 343              $val = str_replace('&', '&amp;', $val);
 344              $val = str_replace("'", '&apos;', $val);
 345              $val = str_replace('"', '&quot;', $val);
 346              $val = str_replace('<', '&lt;', $val);
 347              $val = str_replace('>', '&gt;', $val);
 348          }
 349          return $val;
 350      }
 351  
 352      /**
 353      * returns error string if present
 354      *
 355      * @return   mixed error string or false
 356      * @access   public
 357      */
 358  	function getError(){
 359          if($this->error_str != ''){
 360              return $this->error_str;
 361          }
 362          return false;
 363      }
 364  
 365      /**
 366      * sets error string
 367      *
 368      * @return   boolean $string error string
 369      * @access   private
 370      */
 371  	function setError($str){
 372          $this->error_str = $str;
 373      }
 374  
 375      /**
 376      * detect if array is a simple array or a struct (associative array)
 377      *
 378      * @param    mixed    $val    The PHP array
 379      * @return    string    (arraySimple|arrayStruct)
 380      * @access    private
 381      */
 382  	function isArraySimpleOrStruct($val) {
 383          $keyList = array_keys($val);
 384          foreach ($keyList as $keyListValue) {
 385              if (!is_int($keyListValue)) {
 386                  return 'arrayStruct';
 387              }
 388          }
 389          return 'arraySimple';
 390      }
 391  
 392      /**
 393      * serializes PHP values in accordance w/ section 5. Type information is
 394      * not serialized if $use == 'literal'.
 395      *
 396      * @param    mixed    $val    The value to serialize
 397      * @param    string    $name    The name (local part) of the XML element
 398      * @param    string    $type    The XML schema type (local part) for the element
 399      * @param    string    $name_ns    The namespace for the name of the XML element
 400      * @param    string    $type_ns    The namespace for the type of the element
 401      * @param    array    $attributes    The attributes to serialize as name=>value pairs
 402      * @param    string    $use    The WSDL "use" (encoded|literal)
 403      * @param    boolean    $soapval    Whether this is called from soapval.
 404      * @return    string    The serialized element, possibly with child elements
 405      * @access    public
 406      */
 407  	function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
 408          $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
 409          $this->appendDebug('value=' . $this->varDump($val));
 410          $this->appendDebug('attributes=' . $this->varDump($attributes));
 411          
 412          if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
 413              $this->debug("serialize_val: serialize soapval");
 414              $xml = $val->serialize($use);
 415              $this->appendDebug($val->getDebug());
 416              $val->clearDebug();
 417              $this->debug("serialize_val of soapval returning $xml");
 418              return $xml;
 419          }
 420          // force valid name if necessary
 421          if (is_numeric($name)) {
 422              $name = '__numeric_' . $name;
 423          } elseif (! $name) {
 424              $name = 'noname';
 425          }
 426          // if name has ns, add ns prefix to name
 427          $xmlns = '';
 428          if($name_ns){
 429              $prefix = 'nu'.rand(1000,9999);
 430              $name = $prefix.':'.$name;
 431              $xmlns .= " xmlns:$prefix=\"$name_ns\"";
 432          }
 433          // if type is prefixed, create type prefix
 434          if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
 435              // need to fix this. shouldn't default to xsd if no ns specified
 436              // w/o checking against typemap
 437              $type_prefix = 'xsd';
 438          } elseif($type_ns){
 439              $type_prefix = 'ns'.rand(1000,9999);
 440              $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
 441          }
 442          // serialize attributes if present
 443          $atts = '';
 444          if($attributes){
 445              foreach($attributes as $k => $v){
 446                  $atts .= " $k=\"".$this->expandEntities($v).'"';
 447              }
 448          }
 449          // serialize null value
 450          if (is_null($val)) {
 451              $this->debug("serialize_val: serialize null");
 452              if ($use == 'literal') {
 453                  // TODO: depends on minOccurs
 454                  $xml = "<$name$xmlns$atts/>";
 455                  $this->debug("serialize_val returning $xml");
 456                  return $xml;
 457              } else {
 458                  if (isset($type) && isset($type_prefix)) {
 459                      $type_str = " xsi:type=\"$type_prefix:$type\"";
 460                  } else {
 461                      $type_str = '';
 462                  }
 463                  $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
 464                  $this->debug("serialize_val returning $xml");
 465                  return $xml;
 466              }
 467          }
 468          // serialize if an xsd built-in primitive type
 469          if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
 470              $this->debug("serialize_val: serialize xsd built-in primitive type");
 471              if (is_bool($val)) {
 472                  if ($type == 'boolean') {
 473                      $val = $val ? 'true' : 'false';
 474                  } elseif (! $val) {
 475                      $val = 0;
 476                  }
 477              } else if (is_string($val)) {
 478                  $val = $this->expandEntities($val);
 479              }
 480              if ($use == 'literal') {
 481                  $xml = "<$name$xmlns$atts>$val</$name>";
 482                  $this->debug("serialize_val returning $xml");
 483                  return $xml;
 484              } else {
 485                  $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
 486                  $this->debug("serialize_val returning $xml");
 487                  return $xml;
 488              }
 489          }
 490          // detect type and serialize
 491          $xml = '';
 492          switch(true) {
 493              case (is_bool($val) || $type == 'boolean'):
 494                     $this->debug("serialize_val: serialize boolean");
 495                  if ($type == 'boolean') {
 496                      $val = $val ? 'true' : 'false';
 497                  } elseif (! $val) {
 498                      $val = 0;
 499                  }
 500                  if ($use == 'literal') {
 501                      $xml .= "<$name$xmlns$atts>$val</$name>";
 502                  } else {
 503                      $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
 504                  }
 505                  break;
 506              case (is_int($val) || is_long($val) || $type == 'int'):
 507                     $this->debug("serialize_val: serialize int");
 508                  if ($use == 'literal') {
 509                      $xml .= "<$name$xmlns$atts>$val</$name>";
 510                  } else {
 511                      $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
 512                  }
 513                  break;
 514              case (is_float($val)|| is_double($val) || $type == 'float'):
 515                     $this->debug("serialize_val: serialize float");
 516                  if ($use == 'literal') {
 517                      $xml .= "<$name$xmlns$atts>$val</$name>";
 518                  } else {
 519                      $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
 520                  }
 521                  break;
 522              case (is_string($val) || $type == 'string'):
 523                     $this->debug("serialize_val: serialize string");
 524                  $val = $this->expandEntities($val);
 525                  if ($use == 'literal') {
 526                      $xml .= "<$name$xmlns$atts>$val</$name>";
 527                  } else {
 528                      $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
 529                  }
 530                  break;
 531              case is_object($val):
 532                     $this->debug("serialize_val: serialize object");
 533                  if (get_class($val) == 'soapval') {
 534                      $this->debug("serialize_val: serialize soapval object");
 535                      $pXml = $val->serialize($use);
 536                      $this->appendDebug($val->getDebug());
 537                      $val->clearDebug();
 538                  } else {
 539                      if (! $name) {
 540                          $name = get_class($val);
 541                          $this->debug("In serialize_val, used class name $name as element name");
 542                      } else {
 543                          $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
 544                      }
 545                      foreach(get_object_vars($val) as $k => $v){
 546                          $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
 547                      }
 548                  }
 549                  if(isset($type) && isset($type_prefix)){
 550                      $type_str = " xsi:type=\"$type_prefix:$type\"";
 551                  } else {
 552                      $type_str = '';
 553                  }
 554                  if ($use == 'literal') {
 555                      $xml .= "<$name$xmlns$atts>$pXml</$name>";
 556                  } else {
 557                      $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
 558                  }
 559                  break;
 560              break;
 561              case (is_array($val) || $type):
 562                  // detect if struct or array
 563                  $valueType = $this->isArraySimpleOrStruct($val);
 564                  if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
 565                         $this->debug("serialize_val: serialize array");
 566                      $i = 0;
 567                      if(is_array($val) && count($val)> 0){
 568                          foreach($val as $v){
 569                              if(is_object($v) && get_class($v) ==  'soapval'){
 570                                  $tt_ns = $v->type_ns;
 571                                  $tt = $v->type;
 572                              } elseif (is_array($v)) {
 573                                  $tt = $this->isArraySimpleOrStruct($v);
 574                              } else {
 575                                  $tt = gettype($v);
 576                              }
 577                              $array_types[$tt] = 1;
 578                              // TODO: for literal, the name should be $name
 579                              $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
 580                              ++$i;
 581                          }
 582                          if(count($array_types) > 1){
 583                              $array_typename = 'xsd:anyType';
 584                          } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
 585                              if ($tt == 'integer') {
 586                                  $tt = 'int';
 587                              }
 588                              $array_typename = 'xsd:'.$tt;
 589                          } elseif(isset($tt) && $tt == 'arraySimple'){
 590                              $array_typename = 'SOAP-ENC:Array';
 591                          } elseif(isset($tt) && $tt == 'arrayStruct'){
 592                              $array_typename = 'unnamed_struct_use_soapval';
 593                          } else {
 594                              // if type is prefixed, create type prefix
 595                              if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
 596                                   $array_typename = 'xsd:' . $tt;
 597                              } elseif ($tt_ns) {
 598                                  $tt_prefix = 'ns' . rand(1000, 9999);
 599                                  $array_typename = "$tt_prefix:$tt";
 600                                  $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
 601                              } else {
 602                                  $array_typename = $tt;
 603                              }
 604                          }
 605                          $array_type = $i;
 606                          if ($use == 'literal') {
 607                              $type_str = '';
 608                          } else if (isset($type) && isset($type_prefix)) {
 609                              $type_str = " xsi:type=\"$type_prefix:$type\"";
 610                          } else {
 611                              $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
 612                          }
 613                      // empty array
 614                      } else {
 615                          if ($use == 'literal') {
 616                              $type_str = '';
 617                          } else if (isset($type) && isset($type_prefix)) {
 618                              $type_str = " xsi:type=\"$type_prefix:$type\"";
 619                          } else {
 620                              $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
 621                          }
 622                      }
 623                      // TODO: for array in literal, there is no wrapper here
 624                      $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
 625                  } else {
 626                      // got a struct
 627                         $this->debug("serialize_val: serialize struct");
 628                      if(isset($type) && isset($type_prefix)){
 629                          $type_str = " xsi:type=\"$type_prefix:$type\"";
 630                      } else {
 631                          $type_str = '';
 632                      }
 633                      if ($use == 'literal') {
 634                          $xml .= "<$name$xmlns$atts>";
 635                      } else {
 636                          $xml .= "<$name$xmlns$type_str$atts>";
 637                      }
 638                      foreach($val as $k => $v){
 639                          // Apache Map
 640                          if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
 641                              $xml .= '<item>';
 642                              $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
 643                              $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
 644                              $xml .= '</item>';
 645                          } else {
 646                              $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
 647                          }
 648                      }
 649                      $xml .= "</$name>";
 650                  }
 651                  break;
 652              default:
 653                     $this->debug("serialize_val: serialize unknown");
 654                  $xml .= 'not detected, got '.gettype($val).' for '.$val;
 655                  break;
 656          }
 657          $this->debug("serialize_val returning $xml");
 658          return $xml;
 659      }
 660  
 661      /**
 662      * serializes a message
 663      *
 664      * @param string $body the XML of the SOAP body
 665      * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
 666      * @param array $namespaces optional the namespaces used in generating the body and headers
 667      * @param string $style optional (rpc|document)
 668      * @param string $use optional (encoded|literal)
 669      * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
 670      * @return string the message
 671      * @access public
 672      */
 673      function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
 674      // TODO: add an option to automatically run utf8_encode on $body and $headers
 675      // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
 676      // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
 677  
 678      $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
 679      $this->debug("headers:");
 680      $this->appendDebug($this->varDump($headers));
 681      $this->debug("namespaces:");
 682      $this->appendDebug($this->varDump($namespaces));
 683  
 684      // serialize namespaces
 685      $ns_string = '';
 686      foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
 687          $ns_string .= " xmlns:$k=\"$v\"";
 688      }
 689      if($encodingStyle) {
 690          $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
 691      }
 692  
 693      // serialize headers
 694      if($headers){
 695          if (is_array($headers)) {
 696              $xml = '';
 697              foreach ($headers as $k => $v) {
 698                  if (is_object($v) && get_class($v) == 'soapval') {
 699                      $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
 700                  } else {
 701                      $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
 702                  }
 703              }
 704              $headers = $xml;
 705              $this->debug("In serializeEnvelope, serialized array of headers to $headers");
 706          }
 707          $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
 708      }
 709      // serialize envelope
 710      return
 711      '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
 712      '<SOAP-ENV:Envelope'.$ns_string.">".
 713      $headers.
 714      "<SOAP-ENV:Body>".
 715          $body.
 716      "</SOAP-ENV:Body>".
 717      "</SOAP-ENV:Envelope>";
 718      }
 719  
 720      /**
 721       * formats a string to be inserted into an HTML stream
 722       *
 723       * @param string $str The string to format
 724       * @return string The formatted string
 725       * @access public
 726       * @deprecated
 727       */
 728      function formatDump($str){
 729          $str = htmlspecialchars($str);
 730          return nl2br($str);
 731      }
 732  
 733      /**
 734      * contracts (changes namespace to prefix) a qualified name
 735      *
 736      * @param    string $qname qname
 737      * @return    string contracted qname
 738      * @access   private
 739      */
 740  	function contractQname($qname){
 741          // get element namespace
 742          //$this->xdebug("Contract $qname");
 743          if (strrpos($qname, ':')) {
 744              // get unqualified name
 745              $name = substr($qname, strrpos($qname, ':') + 1);
 746              // get ns
 747              $ns = substr($qname, 0, strrpos($qname, ':'));
 748              $p = $this->getPrefixFromNamespace($ns);
 749              if ($p) {
 750                  return $p . ':' . $name;
 751              }
 752              return $qname;
 753          } else {
 754              return $qname;
 755          }
 756      }
 757  
 758      /**
 759      * expands (changes prefix to namespace) a qualified name
 760      *
 761      * @param    string $qname qname
 762      * @return    string expanded qname
 763      * @access   private
 764      */
 765  	function expandQname($qname){
 766          // get element prefix
 767          if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
 768              // get unqualified name
 769              $name = substr(strstr($qname,':'),1);
 770              // get ns prefix
 771              $prefix = substr($qname,0,strpos($qname,':'));
 772              if(isset($this->namespaces[$prefix])){
 773                  return $this->namespaces[$prefix].':'.$name;
 774              } else {
 775                  return $qname;
 776              }
 777          } else {
 778              return $qname;
 779          }
 780      }
 781  
 782      /**
 783      * returns the local part of a prefixed string
 784      * returns the original string, if not prefixed
 785      *
 786      * @param string $str The prefixed string
 787      * @return string The local part
 788      * @access public
 789      */
 790  	function getLocalPart($str){
 791          if($sstr = strrchr($str,':')){
 792              // get unqualified name
 793              return substr( $sstr, 1 );
 794          } else {
 795              return $str;
 796          }
 797      }
 798  
 799      /**
 800      * returns the prefix part of a prefixed string
 801      * returns false, if not prefixed
 802      *
 803      * @param string $str The prefixed string
 804      * @return mixed The prefix or false if there is no prefix
 805      * @access public
 806      */
 807  	function getPrefix($str){
 808          if($pos = strrpos($str,':')){
 809              // get prefix
 810              return substr($str,0,$pos);
 811          }
 812          return false;
 813      }
 814  
 815      /**
 816      * pass it a prefix, it returns a namespace
 817      *
 818      * @param string $prefix The prefix
 819      * @return mixed The namespace, false if no namespace has the specified prefix
 820      * @access public
 821      */
 822  	function getNamespaceFromPrefix($prefix){
 823          if (isset($this->namespaces[$prefix])) {
 824              return $this->namespaces[$prefix];
 825          }
 826          //$this->setError("No namespace registered for prefix '$prefix'");
 827          return false;
 828      }
 829  
 830      /**
 831      * returns the prefix for a given namespace (or prefix)
 832      * or false if no prefixes registered for the given namespace
 833      *
 834      * @param string $ns The namespace
 835      * @return mixed The prefix, false if the namespace has no prefixes
 836      * @access public
 837      */
 838  	function getPrefixFromNamespace($ns) {
 839          foreach ($this->namespaces as $p => $n) {
 840              if ($ns == $n || $ns == $p) {
 841                  $this->usedNamespaces[$p] = $n;
 842                  return $p;
 843              }
 844          }
 845          return false;
 846      }
 847  
 848      /**
 849      * returns the time in ODBC canonical form with microseconds
 850      *
 851      * @return string The time in ODBC canonical form with microseconds
 852      * @access public
 853      */
 854  	function getmicrotime() {
 855          if (function_exists('gettimeofday')) {
 856              $tod = gettimeofday();
 857              $sec = $tod['sec'];
 858              $usec = $tod['usec'];
 859          } else {
 860              $sec = time();
 861              $usec = 0;
 862          }
 863          return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
 864      }
 865  
 866      /**
 867       * Returns a string with the output of var_dump
 868       *
 869       * @param mixed $data The variable to var_dump
 870       * @return string The output of var_dump
 871       * @access public
 872       */
 873      function varDump($data) {
 874          ob_start();
 875          var_dump($data);
 876          $ret_val = ob_get_contents();
 877          ob_end_clean();
 878          return $ret_val;
 879      }
 880  
 881      /**
 882      * represents the object as a string
 883      *
 884      * @return    string
 885      * @access   public
 886      */
 887  	function __toString() {
 888          return $this->varDump($this);
 889      }
 890  }
 891  
 892  // XML Schema Datatype Helper Functions
 893  
 894  //xsd:dateTime helpers
 895  
 896  /**
 897  * convert unix timestamp to ISO 8601 compliant date string
 898  *
 899  * @param    int $timestamp Unix time stamp
 900  * @param    boolean $utc Whether the time stamp is UTC or local
 901  * @return    mixed ISO 8601 date string or false
 902  * @access   public
 903  */
 904  function timestamp_to_iso8601($timestamp,$utc=true){
 905      $datestr = date('Y-m-d\TH:i:sO',$timestamp);
 906      $pos = strrpos($datestr, "+");
 907      if ($pos === FALSE) {
 908          $pos = strrpos($datestr, "-");
 909      }
 910      if ($pos !== FALSE) {
 911          if (strlen($datestr) == $pos + 5) {
 912              $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
 913          }
 914      }
 915      if($utc){
 916          $pattern = '/'.
 917          '([0-9]{4})-'.    // centuries & years CCYY-
 918          '([0-9]{2})-'.    // months MM-
 919          '([0-9]{2})'.    // days DD
 920          'T'.            // separator T
 921          '([0-9]{2}):'.    // hours hh:
 922          '([0-9]{2}):'.    // minutes mm:
 923          '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
 924          '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
 925          '/';
 926  
 927          if(preg_match($pattern,$datestr,$regs)){
 928              return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
 929          }
 930          return false;
 931      } else {
 932          return $datestr;
 933      }
 934  }
 935  
 936  /**
 937  * convert ISO 8601 compliant date string to unix timestamp
 938  *
 939  * @param    string $datestr ISO 8601 compliant date string
 940  * @return    mixed Unix timestamp (int) or false
 941  * @access   public
 942  */
 943  function iso8601_to_timestamp($datestr){
 944      $pattern = '/'.
 945      '([0-9]{4})-'.    // centuries & years CCYY-
 946      '([0-9]{2})-'.    // months MM-
 947      '([0-9]{2})'.    // days DD
 948      'T'.            // separator T
 949      '([0-9]{2}):'.    // hours hh:
 950      '([0-9]{2}):'.    // minutes mm:
 951      '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
 952      '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
 953      '/';
 954      if(preg_match($pattern,$datestr,$regs)){
 955          // not utc
 956          if($regs[8] != 'Z'){
 957              $op = substr($regs[8],0,1);
 958              $h = substr($regs[8],1,2);
 959              $m = substr($regs[8],strlen($regs[8])-2,2);
 960              if($op == '-'){
 961                  $regs[4] = $regs[4] + $h;
 962                  $regs[5] = $regs[5] + $m;
 963              } elseif($op == '+'){
 964                  $regs[4] = $regs[4] - $h;
 965                  $regs[5] = $regs[5] - $m;
 966              }
 967          }
 968          return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 969  //        return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
 970      } else {
 971          return false;
 972      }
 973  }
 974  
 975  /**
 976  * sleeps some number of microseconds
 977  *
 978  * @param    string $usec the number of microseconds to sleep
 979  * @access   public
 980  * @deprecated
 981  */
 982  function usleepWindows($usec)
 983  {
 984      $start = gettimeofday();
 985      
 986      do
 987      {
 988          $stop = gettimeofday();
 989          $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
 990          + $stop['usec'] - $start['usec'];
 991      }
 992      while ($timePassed < $usec);
 993  }
 994  
 995  ?><?php
 996  
 997  
 998  
 999  /**
1000  * Contains information for a SOAP fault.
1001  * Mainly used for returning faults from deployed functions
1002  * in a server instance.
1003  * @author   Dietrich Ayala <dietrich@ganx4.com>
1004  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1005  * @access public
1006  */
1007  class nusoap_fault extends nusoap_base {
1008      /**
1009       * The fault code (client|server)
1010       * @var string
1011       * @access private
1012       */
1013      var $faultcode;
1014      /**
1015       * The fault actor
1016       * @var string
1017       * @access private
1018       */
1019      var $faultactor;
1020      /**
1021       * The fault string, a description of the fault
1022       * @var string
1023       * @access private
1024       */
1025      var $faultstring;
1026      /**
1027       * The fault detail, typically a string or array of string
1028       * @var mixed
1029       * @access private
1030       */
1031      var $faultdetail;
1032  
1033      /**
1034      * constructor
1035      *
1036      * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1037      * @param string $faultactor only used when msg routed between multiple actors
1038      * @param string $faultstring human readable error message
1039      * @param mixed $faultdetail detail, typically a string or array of string
1040      */
1041  	function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1042          parent::nusoap_base();
1043          $this->faultcode = $faultcode;
1044          $this->faultactor = $faultactor;
1045          $this->faultstring = $faultstring;
1046          $this->faultdetail = $faultdetail;
1047      }
1048  
1049      /**
1050      * serialize a fault
1051      *
1052      * @return    string    The serialization of the fault instance.
1053      * @access   public
1054      */
1055  	function serialize(){
1056          $ns_string = '';
1057          foreach($this->namespaces as $k => $v){
1058              $ns_string .= "\n  xmlns:$k=\"$v\"";
1059          }
1060          $return_msg =
1061              '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1062              '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1063                  '<SOAP-ENV:Body>'.
1064                  '<SOAP-ENV:Fault>'.
1065                      $this->serialize_val($this->faultcode, 'faultcode').
1066                      $this->serialize_val($this->faultactor, 'faultactor').
1067                      $this->serialize_val($this->faultstring, 'faultstring').
1068                      $this->serialize_val($this->faultdetail, 'detail').
1069                  '</SOAP-ENV:Fault>'.
1070                  '</SOAP-ENV:Body>'.
1071              '</SOAP-ENV:Envelope>';
1072          return $return_msg;
1073      }
1074  }
1075  
1076  /**
1077   * Backward compatibility
1078   */
1079  class soap_fault extends nusoap_fault {
1080  }
1081  
1082  ?><?php
1083  
1084  
1085  
1086  /**
1087  * parses an XML Schema, allows access to it's data, other utility methods.
1088  * imperfect, no validation... yet, but quite functional.
1089  *
1090  * @author   Dietrich Ayala <dietrich@ganx4.com>
1091  * @author   Scott Nichol <snichol@users.sourceforge.net>
1092  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1093  * @access   public
1094  */
1095  class nusoap_xmlschema extends nusoap_base  {
1096      
1097      // files
1098      var $schema = '';
1099      var $xml = '';
1100      // namespaces
1101      var $enclosingNamespaces;
1102      // schema info
1103      var $schemaInfo = array();
1104      var $schemaTargetNamespace = '';
1105      // types, elements, attributes defined by the schema
1106      var $attributes = array();
1107      var $complexTypes = array();
1108      var $complexTypeStack = array();
1109      var $currentComplexType = null;
1110      var $elements = array();
1111      var $elementStack = array();
1112      var $currentElement = null;
1113      var $simpleTypes = array();
1114      var $simpleTypeStack = array();
1115      var $currentSimpleType = null;
1116      // imports
1117      var $imports = array();
1118      // parser vars
1119      var $parser;
1120      var $position = 0;
1121      var $depth = 0;
1122      var $depth_array = array();
1123      var $message = array();
1124      var $defaultNamespace = array();
1125      
1126      /**
1127      * constructor
1128      *
1129      * @param    string $schema schema document URI
1130      * @param    string $xml xml document URI
1131      * @param    string $namespaces namespaces defined in enclosing XML
1132      * @access   public
1133      */
1134  	function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1135          parent::nusoap_base();
1136          $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1137          // files
1138          $this->schema = $schema;
1139          $this->xml = $xml;
1140  
1141          // namespaces
1142          $this->enclosingNamespaces = $namespaces;
1143          $this->namespaces = array_merge($this->namespaces, $namespaces);
1144  
1145          // parse schema file
1146          if($schema != ''){
1147              $this->debug('initial schema file: '.$schema);
1148              $this->parseFile($schema, 'schema');
1149          }
1150  
1151          // parse xml file
1152          if($xml != ''){
1153              $this->debug('initial xml file: '.$xml);
1154              $this->parseFile($xml, 'xml');
1155          }
1156  
1157      }
1158  
1159      /**
1160      * parse an XML file
1161      *
1162      * @param string $xml path/URL to XML file
1163      * @param string $type (schema | xml)
1164      * @return boolean
1165      * @access public
1166      */
1167  	function parseFile($xml,$type){
1168          // parse xml file
1169          if($xml != ""){
1170              $xmlStr = @join("",@file($xml));
1171              if($xmlStr == ""){
1172                  $msg = 'Error reading XML from '.$xml;
1173                  $this->setError($msg);
1174                  $this->debug($msg);
1175              return false;
1176              } else {
1177                  $this->debug("parsing $xml");
1178                  $this->parseString($xmlStr,$type);
1179                  $this->debug("done parsing $xml");
1180              return true;
1181              }
1182          }
1183          return false;
1184      }
1185  
1186      /**
1187      * parse an XML string
1188      *
1189      * @param    string $xml path or URL
1190      * @param    string $type (schema|xml)
1191      * @access   private
1192      */
1193  	function parseString($xml,$type){
1194          // parse xml string
1195          if($xml != ""){
1196  
1197              // Create an XML parser.
1198              $this->parser = xml_parser_create();
1199              // Set the options for parsing the XML data.
1200              xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1201  
1202              // Set the object for the parser.
1203              xml_set_object($this->parser, $this);
1204  
1205              // Set the element handlers for the parser.
1206              if($type == "schema"){
1207                  xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1208                  xml_set_character_data_handler($this->parser,'schemaCharacterData');
1209              } elseif($type == "xml"){
1210                  xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1211                  xml_set_character_data_handler($this->parser,'xmlCharacterData');
1212              }
1213  
1214              // Parse the XML file.
1215              if(!xml_parse($this->parser,$xml,true)){
1216              // Display an error message.
1217                  $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1218                  xml_get_current_line_number($this->parser),
1219                  xml_error_string(xml_get_error_code($this->parser))
1220                  );
1221                  $this->debug($errstr);
1222                  $this->debug("XML payload:\n" . $xml);
1223                  $this->setError($errstr);
1224              }
1225              
1226              xml_parser_free($this->parser);
1227          } else{
1228              $this->debug('no xml passed to parseString()!!');
1229              $this->setError('no xml passed to parseString()!!');
1230          }
1231      }
1232  
1233      /**
1234       * gets a type name for an unnamed type
1235       *
1236       * @param    string    Element name
1237       * @return    string    A type name for an unnamed type
1238       * @access    private
1239       */
1240  	function CreateTypeName($ename) {
1241          $scope = '';
1242          for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1243              $scope .= $this->complexTypeStack[$i] . '_';
1244          }
1245          return $scope . $ename . '_ContainedType';
1246      }
1247      
1248      /**
1249      * start-element handler
1250      *
1251      * @param    string $parser XML parser object
1252      * @param    string $name element name
1253      * @param    string $attrs associative array of attributes
1254      * @access   private
1255      */
1256  	function schemaStartElement($parser, $name, $attrs) {
1257          
1258          // position in the total number of elements, starting from 0
1259          $pos = $this->position++;
1260          $depth = $this->depth++;
1261          // set self as current value for this depth
1262          $this->depth_array[$depth] = $pos;
1263          $this->message[$pos] = array('cdata' => ''); 
1264          if ($depth > 0) {
1265              $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1266          } else {
1267              $this->defaultNamespace[$pos] = false;
1268          }
1269  
1270          // get element prefix
1271          if($prefix = $this->getPrefix($name)){
1272              // get unqualified name
1273              $name = $this->getLocalPart($name);
1274          } else {
1275              $prefix = '';
1276          }
1277          
1278          // loop thru attributes, expanding, and registering namespace declarations
1279          if(count($attrs) > 0){
1280              foreach($attrs as $k => $v){
1281                  // if ns declarations, add to class level array of valid namespaces
1282                  if(preg_match('/^xmlns/',$k)){
1283                      //$this->xdebug("$k: $v");
1284                      //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1285                      if($ns_prefix = substr(strrchr($k,':'),1)){
1286                          //$this->xdebug("Add namespace[$ns_prefix] = $v");
1287                          $this->namespaces[$ns_prefix] = $v;
1288                      } else {
1289                          $this->defaultNamespace[$pos] = $v;
1290                          if (! $this->getPrefixFromNamespace($v)) {
1291                              $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1292                          }
1293                      }
1294                      if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1295                          $this->XMLSchemaVersion = $v;
1296                          $this->namespaces['xsi'] = $v.'-instance';
1297                      }
1298                  }
1299              }
1300              foreach($attrs as $k => $v){
1301                  // expand each attribute
1302                  $k = strpos($k,':') ? $this->expandQname($k) : $k;
1303                  $v = strpos($v,':') ? $this->expandQname($v) : $v;
1304                  $eAttrs[$k] = $v;
1305              }
1306              $attrs = $eAttrs;
1307          } else {
1308              $attrs = array();
1309          }
1310          // find status, register data
1311          switch($name){
1312              case 'all':            // (optional) compositor content for a complexType
1313              case 'choice':
1314              case 'group':
1315              case 'sequence':
1316                  //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1317                  $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1318                  //if($name == 'all' || $name == 'sequence'){
1319                  //    $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1320                  //}
1321              break;
1322              case 'attribute':    // complexType attribute
1323                  //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1324                  $this->xdebug("parsing attribute:");
1325                  $this->appendDebug($this->varDump($attrs));
1326                  if (!isset($attrs['form'])) {
1327                      // TODO: handle globals
1328                      $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1329                  }
1330                  if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1331                      $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1332                      if (!strpos($v, ':')) {
1333                          // no namespace in arrayType attribute value...
1334                          if ($this->defaultNamespace[$pos]) {
1335                              // ...so use the default
1336                              $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1337                          }
1338                      }
1339                  }
1340                  if(isset($attrs['name'])){
1341                      $this->attributes[$attrs['name']] = $attrs;
1342                      $aname = $attrs['name'];
1343                  } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1344                      if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1345                          $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1346                      } else {
1347                          $aname = '';
1348                      }
1349                  } elseif(isset($attrs['ref'])){
1350                      $aname = $attrs['ref'];
1351                      $this->attributes[$attrs['ref']] = $attrs;
1352                  }
1353                  
1354                  if($this->currentComplexType){    // This should *always* be
1355                      $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1356                  }
1357                  // arrayType attribute
1358                  if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1359                      $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1360                      $prefix = $this->getPrefix($aname);
1361                      if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1362                          $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1363                      } else {
1364                          $v = '';
1365                      }
1366                      if(strpos($v,'[,]')){
1367                          $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1368                      }
1369                      $v = substr($v,0,strpos($v,'[')); // clip the []
1370                      if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1371                          $v = $this->XMLSchemaVersion.':'.$v;
1372                      }
1373                      $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1374                  }
1375              break;
1376              case 'complexContent':    // (optional) content for a complexType
1377                  $this->xdebug("do nothing for element $name");
1378              break;
1379              case 'complexType':
1380                  array_push($this->complexTypeStack, $this->currentComplexType);
1381                  if(isset($attrs['name'])){
1382                      // TODO: what is the scope of named complexTypes that appear
1383                      //       nested within other c complexTypes?
1384                      $this->xdebug('processing named complexType '.$attrs['name']);
1385                      //$this->currentElement = false;
1386                      $this->currentComplexType = $attrs['name'];
1387                      $this->complexTypes[$this->currentComplexType] = $attrs;
1388                      $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1389                      // This is for constructs like
1390                      //           <complexType name="ListOfString" base="soap:Array">
1391                      //                <sequence>
1392                      //                    <element name="string" type="xsd:string"
1393                      //                        minOccurs="0" maxOccurs="unbounded" />
1394                      //                </sequence>
1395                      //            </complexType>
1396                      if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1397                          $this->xdebug('complexType is unusual array');
1398                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1399                      } else {
1400                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1401                      }
1402                  } else {
1403                      $name = $this->CreateTypeName($this->currentElement);
1404                      $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1405                      $this->currentComplexType = $name;
1406                      //$this->currentElement = false;
1407                      $this->complexTypes[$this->currentComplexType] = $attrs;
1408                      $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1409                      // This is for constructs like
1410                      //           <complexType name="ListOfString" base="soap:Array">
1411                      //                <sequence>
1412                      //                    <element name="string" type="xsd:string"
1413                      //                        minOccurs="0" maxOccurs="unbounded" />
1414                      //                </sequence>
1415                      //            </complexType>
1416                      if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1417                          $this->xdebug('complexType is unusual array');
1418                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1419                      } else {
1420                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1421                      }
1422                  }
1423                  $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
1424              break;
1425              case 'element':
1426                  array_push($this->elementStack, $this->currentElement);
1427                  if (!isset($attrs['form'])) {
1428                      if ($this->currentComplexType) {
1429                          $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1430                      } else {
1431                          // global
1432                          $attrs['form'] = 'qualified';
1433                      }
1434                  }
1435                  if(isset($attrs['type'])){
1436                      $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1437                      if (! $this->getPrefix($attrs['type'])) {
1438                          if ($this->defaultNamespace[$pos]) {
1439                              $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1440                              $this->xdebug('used default namespace to make type ' . $attrs['type']);
1441                          }
1442                      }
1443                      // This is for constructs like
1444                      //           <complexType name="ListOfString" base="soap:Array">
1445                      //                <sequence>
1446                      //                    <element name="string" type="xsd:string"
1447                      //                        minOccurs="0" maxOccurs="unbounded" />
1448                      //                </sequence>
1449                      //            </complexType>
1450                      if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1451                          $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1452                          $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1453                      }
1454                      $this->currentElement = $attrs['name'];
1455                      $ename = $attrs['name'];
1456                  } elseif(isset($attrs['ref'])){
1457                      $this->xdebug("processing element as ref to ".$attrs['ref']);
1458                      $this->currentElement = "ref to ".$attrs['ref'];
1459                      $ename = $this->getLocalPart($attrs['ref']);
1460                  } else {
1461                      $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1462                      $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1463                      $this->currentElement = $attrs['name'];
1464                      $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1465                      $ename = $attrs['name'];
1466                  }
1467                  if (isset($ename) && $this->currentComplexType) {
1468                      $this->xdebug("add element $ename to complexType $this->currentComplexType");
1469                      $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1470                  } elseif (!isset($attrs['ref'])) {
1471                      $this->xdebug("add element $ename to elements array");
1472                      $this->elements[ $attrs['name'] ] = $attrs;
1473                      $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1474                  }
1475              break;
1476              case 'enumeration':    //    restriction value list member
1477                  $this->xdebug('enumeration ' . $attrs['value']);
1478                  if ($this->currentSimpleType) {
1479                      $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1480                  } elseif ($this->currentComplexType) {
1481                      $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1482                  }
1483              break;
1484              case 'extension':    // simpleContent or complexContent type extension
1485                  $this->xdebug('extension ' . $attrs['base']);
1486                  if ($this->currentComplexType) {
1487                      $ns = $this->getPrefix($attrs['base']);
1488                      if ($ns == '') {
1489                          $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
1490                      } else {
1491                          $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1492                      }
1493                  } else {
1494                      $this->xdebug('no current complexType to set extensionBase');
1495                  }
1496              break;
1497              case 'import':
1498                  if (isset($attrs['schemaLocation'])) {
1499                      $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1500                      $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1501                  } else {
1502                      $this->xdebug('import namespace ' . $attrs['namespace']);
1503                      $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1504                      if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1505                          $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1506                      }
1507                  }
1508              break;
1509              case 'include':
1510                  if (isset($attrs['schemaLocation'])) {
1511                      $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
1512                      $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1513                  } else {
1514                      $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
1515                  }
1516              break;
1517              case 'list':    // simpleType value list
1518                  $this->xdebug("do nothing for element $name");
1519              break;
1520              case 'restriction':    // simpleType, simpleContent or complexContent value restriction
1521                  $this->xdebug('restriction ' . $attrs['base']);
1522                  if($this->currentSimpleType){
1523                      $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1524                  } elseif($this->currentComplexType){
1525                      $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1526                      if(strstr($attrs['base'],':') == ':Array'){
1527                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1528                      }
1529                  }
1530              break;
1531              case 'schema':
1532                  $this->schemaInfo = $attrs;
1533                  $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1534                  if (isset($attrs['targetNamespace'])) {
1535                      $this->schemaTargetNamespace = $attrs['targetNamespace'];
1536                  }
1537                  if (!isset($attrs['elementFormDefault'])) {
1538                      $this->schemaInfo['elementFormDefault'] = 'unqualified';
1539                  }
1540                  if (!isset($attrs['attributeFormDefault'])) {
1541                      $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1542                  }
1543              break;
1544              case 'simpleContent':    // (optional) content for a complexType
1545                  if ($this->currentComplexType) {    // This should *always* be
1546                      $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
1547                  } else {
1548                      $this->xdebug("do nothing for element $name because there is no current complexType");
1549                  }
1550              break;
1551              case 'simpleType':
1552                  array_push($this->simpleTypeStack, $this->currentSimpleType);
1553                  if(isset($attrs['name'])){
1554                      $this->xdebug("processing simpleType for name " . $attrs['name']);
1555                      $this->currentSimpleType = $attrs['name'];
1556                      $this->simpleTypes[ $attrs['name'] ] = $attrs;
1557                      $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1558                      $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1559                  } else {
1560                      $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1561                      $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1562                      $this->currentSimpleType = $name;
1563                      //$this->currentElement = false;
1564                      $this->simpleTypes[$this->currentSimpleType] = $attrs;
1565                      $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1566                  }
1567              break;
1568              case 'union':    // simpleType type list
1569                  $this->xdebug("do nothing for element $name");
1570              break;
1571              default:
1572                  $this->xdebug("do not have any logic to process element $name");
1573          }
1574      }
1575  
1576      /**
1577      * end-element handler
1578      *
1579      * @param    string $parser XML parser object
1580      * @param    string $name element name
1581      * @access   private
1582      */
1583  	function schemaEndElement($parser, $name) {
1584          // bring depth down a notch
1585          $this->depth--;
1586          // position of current element is equal to the last value left in depth_array for my depth
1587          if(isset($this->depth_array[$this->depth])){
1588              $pos = $this->depth_array[$this->depth];
1589          }
1590          // get element prefix
1591          if ($prefix = $this->getPrefix($name)){
1592              // get unqualified name
1593              $name = $this->getLocalPart($name);
1594          } else {
1595              $prefix = '';
1596          }
1597          // move on...
1598          if($name == 'complexType'){
1599              $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1600              $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1601              $this->currentComplexType = array_pop($this->complexTypeStack);
1602              //$this->currentElement = false;
1603          }
1604          if($name == 'element'){
1605              $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1606              $this->currentElement = array_pop($this->elementStack);
1607          }
1608          if($name == 'simpleType'){
1609              $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1610              $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1611              $this->currentSimpleType = array_pop($this->simpleTypeStack);
1612          }
1613      }
1614  
1615      /**
1616      * element content handler
1617      *
1618      * @param    string $parser XML parser object
1619      * @param    string $data element content
1620      * @access   private
1621      */
1622  	function schemaCharacterData($parser, $data){
1623          $pos = $this->depth_array[$this->depth - 1];
1624          $this->message[$pos]['cdata'] .= $data;
1625      }
1626  
1627      /**
1628      * serialize the schema
1629      *
1630      * @access   public
1631      */
1632  	function serializeSchema(){
1633  
1634          $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1635          $xml = '';
1636          // imports
1637          if (sizeof($this->imports) > 0) {
1638              foreach($this->imports as $ns => $list) {
1639                  foreach ($list as $ii) {
1640                      if ($ii['location'] != '') {
1641                          $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1642                      } else {
1643                          $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1644                      }
1645                  }
1646              } 
1647          } 
1648          // complex types
1649          foreach($this->complexTypes as $typeName => $attrs){
1650              $contentStr = '';
1651              // serialize child elements
1652              if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1653                  foreach($attrs['elements'] as $element => $eParts){
1654                      if(isset($eParts['ref'])){
1655                          $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1656                      } else {
1657                          $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1658                          foreach ($eParts as $aName => $aValue) {
1659                              // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1660                              if ($aName != 'name' && $aName != 'type') {
1661                                  $contentStr .= " $aName=\"$aValue\"";
1662                              }
1663                          }
1664                          $contentStr .= "/>\n";
1665                      }
1666                  }
1667                  // compositor wraps elements
1668                  if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1669                      $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1670                  }
1671              }
1672              // attributes
1673              if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1674                  foreach($attrs['attrs'] as $attr => $aParts){
1675                      $contentStr .= "    <$schemaPrefix:attribute";
1676                      foreach ($aParts as $a => $v) {
1677                          if ($a == 'ref' || $a == 'type') {
1678                              $contentStr .= " $a=\"".$this->contractQName($v).'"';
1679                          } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1680                              $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1681                              $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1682                          } else {
1683                              $contentStr .= " $a=\"$v\"";
1684                          }
1685                      }
1686                      $contentStr .= "/>\n";
1687                  }
1688              }
1689              // if restriction
1690              if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1691                  $contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1692                  // complex or simple content
1693                  if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1694                      $contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1695                  }
1696              }
1697              // finalize complex type
1698              if($contentStr != ''){
1699                  $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1700              } else {
1701                  $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1702              }
1703              $xml .= $contentStr;
1704          }
1705          // simple types
1706          if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1707              foreach($this->simpleTypes as $typeName => $eParts){
1708                  $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1709                  if (isset($eParts['enumeration'])) {
1710                      foreach ($eParts['enumeration'] as $e) {
1711                          $xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1712                      }
1713                  }
1714                  $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1715              }
1716          }
1717          // elements
1718          if(isset($this->elements) && count($this->elements) > 0){
1719              foreach($this->elements as $element => $eParts){
1720                  $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1721              }
1722          }
1723          // attributes
1724          if(isset($this->attributes) && count($this->attributes) > 0){
1725              foreach($this->attributes as $attr => $aParts){
1726                  $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1727              }
1728          }
1729          // finish 'er up
1730          $attr = '';
1731          foreach ($this->schemaInfo as $k => $v) {
1732              if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1733                  $attr .= " $k=\"$v\"";
1734              }
1735          }
1736          $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1737          foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1738              $el .= " xmlns:$nsp=\"$ns\"";
1739          }
1740          $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1741          return $xml;
1742      }
1743  
1744      /**
1745      * adds debug data to the clas level debug string
1746      *
1747      * @param    string $string debug data
1748      * @access   private
1749      */
1750  	function xdebug($string){
1751          $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1752      }
1753  
1754      /**
1755      * get the PHP type of a user defined type in the schema
1756      * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1757      * returns false if no type exists, or not w/ the given namespace
1758      * else returns a string that is either a native php type, or 'struct'
1759      *
1760      * @param string $type name of defined type
1761      * @param string $ns namespace of type
1762      * @return mixed
1763      * @access public
1764      * @deprecated
1765      */
1766  	function getPHPType($type,$ns){
1767          if(isset($this->typemap[$ns][$type])){
1768              //print "found type '$type' and ns $ns in typemap<br>";
1769              return $this->typemap[$ns][$type];
1770          } elseif(isset($this->complexTypes[$type])){
1771              //print "getting type '$type' and ns $ns from complexTypes array<br>";
1772              return $this->complexTypes[$type]['phpType'];
1773          }
1774          return false;
1775      }
1776  
1777      /**
1778      * returns an associative array of information about a given type
1779      * returns false if no type exists by the given name
1780      *
1781      *    For a complexType typeDef = array(
1782      *    'restrictionBase' => '',
1783      *    'phpType' => '',
1784      *    'compositor' => '(sequence|all)',
1785      *    'elements' => array(), // refs to elements array
1786      *    'attrs' => array() // refs to attributes array
1787      *    ... and so on (see addComplexType)
1788      *    )
1789      *
1790      *   For simpleType or element, the array has different keys.
1791      *
1792      * @param string $type
1793      * @return mixed
1794      * @access public
1795      * @see addComplexType
1796      * @see addSimpleType
1797      * @see addElement
1798      */
1799  	function getTypeDef($type){
1800          //$this->debug("in getTypeDef for type $type");
1801          if (substr($type, -1) == '^') {
1802              $is_element = 1;
1803              $type = substr($type, 0, -1);
1804          } else {
1805              $is_element = 0;
1806          }
1807  
1808          if((! $is_element) && isset($this->complexTypes[$type])){
1809              $this->xdebug("in getTypeDef, found complexType $type");
1810              return $this->complexTypes[$type];
1811          } elseif((! $is_element) && isset($this->simpleTypes[$type])){
1812              $this->xdebug("in getTypeDef, found simpleType $type");
1813              if (!isset($this->simpleTypes[$type]['phpType'])) {
1814                  // get info for type to tack onto the simple type
1815                  // TODO: can this ever really apply (i.e. what is a simpleType really?)
1816                  $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1817                  $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1818                  $etype = $this->getTypeDef($uqType);
1819                  if ($etype) {
1820                      $this->xdebug("in getTypeDef, found type for simpleType $type:");
1821                      $this->xdebug($this->varDump($etype));
1822                      if (isset($etype['phpType'])) {
1823                          $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1824                      }
1825                      if (isset($etype['elements'])) {
1826                          $this->simpleTypes[$type]['elements'] = $etype['elements'];
1827                      }
1828                  }
1829              }
1830              return $this->simpleTypes[$type];
1831          } elseif(isset($this->elements[$type])){
1832              $this->xdebug("in getTypeDef, found element $type");
1833              if (!isset($this->elements[$type]['phpType'])) {
1834                  // get info for type to tack onto the element
1835                  $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1836                  $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1837                  $etype = $this->getTypeDef($uqType);
1838                  if ($etype) {
1839                      $this->xdebug("in getTypeDef, found type for element $type:");
1840                      $this->xdebug($this->varDump($etype));
1841                      if (isset($etype['phpType'])) {
1842                          $this->elements[$type]['phpType'] = $etype['phpType'];
1843                      }
1844                      if (isset($etype['elements'])) {
1845                          $this->elements[$type]['elements'] = $etype['elements'];
1846                      }
1847                      if (isset($etype['extensionBase'])) {
1848                          $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1849                      }
1850                  } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1851                      $this->xdebug("in getTypeDef, element $type is an XSD type");
1852                      $this->elements[$type]['phpType'] = 'scalar';
1853                  }
1854              }
1855              return $this->elements[$type];
1856          } elseif(isset($this->attributes[$type])){
1857              $this->xdebug("in getTypeDef, found attribute $type");
1858              return $this->attributes[$type];
1859          } elseif (preg_match('/_ContainedType$/', $type)) {
1860              $this->xdebug("in getTypeDef, have an untyped element $type");
1861              $typeDef['typeClass'] = 'simpleType';
1862              $typeDef['phpType'] = 'scalar';
1863              $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1864              return $typeDef;
1865          }
1866          $this->xdebug("in getTypeDef, did not find $type");
1867          return false;
1868      }
1869  
1870      /**
1871      * returns a sample serialization of a given type, or false if no type by the given name
1872      *
1873      * @param string $type name of type
1874      * @return mixed
1875      * @access public
1876      * @deprecated
1877      */
1878      function serializeTypeDef($type){
1879          //print "in sTD() for type $type<br>";
1880      if($typeDef = $this->getTypeDef($type)){
1881          $str .= '<'.$type;
1882          if(is_array($typeDef['attrs'])){
1883          foreach($typeDef['attrs'] as $attName => $data){
1884              $str .= " $attName=\"{type = ".$data['type']."}\"";
1885          }
1886          }
1887          $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1888          if(count($typeDef['elements']) > 0){
1889          $str .= ">";
1890          foreach($typeDef['elements'] as $element => $eData){
1891              $str .= $this->serializeTypeDef($element);
1892          }
1893          $str .= "</$type>";
1894          } elseif($typeDef['typeClass'] == 'element') {
1895          $str .= "></$type>";
1896          } else {
1897          $str .= "/>";
1898          }
1899              return $str;
1900      }
1901          return false;
1902      }
1903  
1904      /**
1905      * returns HTML form elements that allow a user
1906      * to enter values for creating an instance of the given type.
1907      *
1908      * @param string $name name for type instance
1909      * @param string $type name of type
1910      * @return string
1911      * @access public
1912      * @deprecated
1913      */
1914  	function typeToForm($name,$type){
1915          // get typedef
1916          if($typeDef = $this->getTypeDef($type)){
1917              // if struct
1918              if($typeDef['phpType'] == 'struct'){
1919                  $buffer .= '<table>';
1920                  foreach($typeDef['elements'] as $child => $childDef){
1921                      $buffer .= "
1922                      <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1923                      <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1924                  }
1925                  $buffer .= '</table>';
1926              // if array
1927              } elseif($typeDef['phpType'] == 'array'){
1928                  $buffer .= '<table>';
1929                  for($i=0;$i < 3; $i++){
1930                      $buffer .= "
1931                      <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1932                      <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1933                  }
1934                  $buffer .= '</table>';
1935              // if scalar
1936              } else {
1937                  $buffer .= "<input type='text' name='parameters[$name]'>";
1938              }
1939          } else {
1940              $buffer .= "<input type='text' name='parameters[$name]'>";
1941          }
1942          return $buffer;
1943      }
1944      
1945      /**
1946      * adds a complex type to the schema
1947      * 
1948      * example: array
1949      * 
1950      * addType(
1951      *     'ArrayOfstring',
1952      *     'complexType',
1953      *     'array',
1954      *     '',
1955      *     'SOAP-ENC:Array',
1956      *     array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1957      *     'xsd:string'
1958      * );
1959      * 
1960      * example: PHP associative array ( SOAP Struct )
1961      * 
1962      * addType(
1963      *     'SOAPStruct',
1964      *     'complexType',
1965      *     'struct',
1966      *     'all',
1967      *     array('myVar'=> array('name'=>'myVar','type'=>'string')
1968      * );
1969      * 
1970      * @param name
1971      * @param typeClass (complexType|simpleType|attribute)
1972      * @param phpType: currently supported are array and struct (php assoc array)
1973      * @param compositor (all|sequence|choice)
1974      * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1975      * @param elements = array ( name = array(name=>'',type=>'') )
1976      * @param attrs = array(
1977      *     array(
1978      *        'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1979      *        "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1980      *     )
1981      * )
1982      * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1983      * @access public
1984      * @see getTypeDef
1985      */
1986  	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1987          $this->complexTypes[$name] = array(
1988          'name'        => $name,
1989          'typeClass'    => $typeClass,
1990          'phpType'    => $phpType,
1991          'compositor'=> $compositor,
1992          'restrictionBase' => $restrictionBase,
1993          'elements'    => $elements,
1994          'attrs'        => $attrs,
1995          'arrayType'    => $arrayType
1996          );
1997          
1998          $this->xdebug("addComplexType $name:");
1999          $this->appendDebug($this->varDump($this->complexTypes[$name]));
2000      }
2001      
2002      /**
2003      * adds a simple type to the schema
2004      *
2005      * @param string $name
2006      * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2007      * @param string $typeClass (should always be simpleType)
2008      * @param string $phpType (should always be scalar)
2009      * @param array $enumeration array of values
2010      * @access public
2011      * @see nusoap_xmlschema
2012      * @see getTypeDef
2013      */
2014  	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
2015          $this->simpleTypes[$name] = array(
2016          'name'            => $name,
2017          'typeClass'        => $typeClass,
2018          'phpType'        => $phpType,
2019          'type'            => $restrictionBase,
2020          'enumeration'    => $enumeration
2021          );
2022          
2023          $this->xdebug("addSimpleType $name:");
2024          $this->appendDebug($this->varDump($this->simpleTypes[$name]));
2025      }
2026  
2027      /**
2028      * adds an element to the schema
2029      *
2030      * @param array $attrs attributes that must include name and type
2031      * @see nusoap_xmlschema
2032      * @access public
2033      */
2034  	function addElement($attrs) {
2035          if (! $this->getPrefix($attrs['type'])) {
2036              $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
2037          }
2038          $this->elements[ $attrs['name'] ] = $attrs;
2039          $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
2040          
2041          $this->xdebug("addElement " . $attrs['name']);
2042          $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
2043      }
2044  }
2045  
2046  /**
2047   * Backward compatibility
2048   */
2049  class XMLSchema extends nusoap_xmlschema {
2050  }
2051  
2052  ?><?php
2053  
2054  
2055  
2056  /**
2057  * For creating serializable abstractions of native PHP types.  This class
2058  * allows element name/namespace, XSD type, and XML attributes to be
2059  * associated with a value.  This is extremely useful when WSDL is not
2060  * used, but is also useful when WSDL is used with polymorphic types, including
2061  * xsd:anyType and user-defined types.
2062  *
2063  * @author   Dietrich Ayala <dietrich@ganx4.com>
2064  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2065  * @access   public
2066  */
2067  class soapval extends nusoap_base {
2068      /**
2069       * The XML element name
2070       *
2071       * @var string
2072       * @access private
2073       */
2074      var $name;
2075      /**
2076       * The XML type name (string or false)
2077       *
2078       * @var mixed
2079       * @access private
2080       */
2081      var $type;
2082      /**
2083       * The PHP value
2084       *
2085       * @var mixed
2086       * @access private
2087       */
2088      var $value;
2089      /**
2090       * The XML element namespace (string or false)
2091       *
2092       * @var mixed
2093       * @access private
2094       */
2095      var $element_ns;
2096      /**
2097       * The XML type namespace (string or false)
2098       *
2099       * @var mixed
2100       * @access private
2101       */
2102      var $type_ns;
2103      /**
2104       * The XML element attributes (array or false)
2105       *
2106       * @var mixed
2107       * @access private
2108       */
2109      var $attributes;
2110  
2111      /**
2112      * constructor
2113      *
2114      * @param    string $name optional name
2115      * @param    mixed $type optional type name
2116      * @param    mixed $value optional value
2117      * @param    mixed $element_ns optional namespace of value
2118      * @param    mixed $type_ns optional namespace of type
2119      * @param    mixed $attributes associative array of attributes to add to element serialization
2120      * @access   public
2121      */
2122    	function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2123          parent::nusoap_base();
2124          $this->name = $name;
2125          $this->type = $type;
2126          $this->value = $value;
2127          $this->element_ns = $element_ns;
2128          $this->type_ns = $type_ns;
2129          $this->attributes = $attributes;
2130      }
2131  
2132      /**
2133      * return serialized value
2134      *
2135      * @param    string $use The WSDL use value (encoded|literal)
2136      * @return    string XML data
2137      * @access   public
2138      */
2139  	function serialize($use='encoded') {
2140          return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2141      }
2142  
2143      /**
2144      * decodes a soapval object into a PHP native type
2145      *
2146      * @return    mixed
2147      * @access   public
2148      */
2149  	function decode(){
2150          return $this->value;
2151      }
2152  }
2153  
2154  
2155  
2156  ?><?php
2157  
2158  
2159  
2160  /**
2161  * transport class for sending/receiving data via HTTP and HTTPS
2162  * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2163  *
2164  * @author   Dietrich Ayala <dietrich@ganx4.com>
2165  * @author   Scott Nichol <snichol@users.sourceforge.net>
2166  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2167  * @access public
2168  */
2169  class soap_transport_http extends nusoap_base {
2170  
2171      var $url = '';
2172      var $uri = '';
2173      var $digest_uri = '';
2174      var $scheme = '';
2175      var $host = '';
2176      var $port = '';
2177      var $path = '';
2178      var $request_method = 'POST';
2179      var $protocol_version = '1.0';
2180      var $encoding = '';
2181      var $outgoing_headers = array();
2182      var $incoming_headers = array();
2183      var $incoming_cookies = array();
2184      var $outgoing_payload = '';
2185      var $incoming_payload = '';
2186      var $response_status_line;    // HTTP response status line
2187      var $useSOAPAction = true;
2188      var $persistentConnection = false;
2189      var $ch = false;    // cURL handle
2190      var $ch_options = array();    // cURL custom options
2191      var $use_curl = false;        // force cURL use
2192      var $proxy = null;            // proxy information (associative array)
2193      var $username = '';
2194      var $password = '';
2195      var $authtype = '';
2196      var $digestRequest = array();
2197      var $certRequest = array();    // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2198                                  // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2199                                  // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2200                                  // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2201                                  // passphrase: SSL key password/passphrase
2202                                  // certpassword: SSL certificate password
2203                                  // verifypeer: default is 1
2204                                  // verifyhost: default is 1
2205  
2206      /**
2207      * constructor
2208      *
2209      * @param string $url The URL to which to connect
2210      * @param array $curl_options User-specified cURL options
2211      * @param boolean $use_curl Whether to try to force cURL use
2212      * @access public
2213      */
2214  	function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2215          parent::nusoap_base();
2216          $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2217          $this->appendDebug($this->varDump($curl_options));
2218          $this->setURL($url);
2219          if (is_array($curl_options)) {
2220              $this->ch_options = $curl_options;
2221          }
2222          $this->use_curl = $use_curl;
2223          preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2224          $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2225      }
2226  
2227      /**
2228      * sets a cURL option
2229      *
2230      * @param    mixed $option The cURL option (always integer?)
2231      * @param    mixed $value The cURL option value
2232      * @access   private
2233      */
2234  	function setCurlOption($option, $value) {
2235          $this->debug("setCurlOption option=$option, value=");
2236          $this->appendDebug($this->varDump($value));
2237          curl_setopt($this->ch, $option, $value);
2238      }
2239  
2240      /**
2241      * sets an HTTP header
2242      *
2243      * @param string $name The name of the header
2244      * @param string $value The value of the header
2245      * @access private
2246      */
2247  	function setHeader($name, $value) {
2248          $this->outgoing_headers[$name] = $value;
2249          $this->debug("set header $name: $value");
2250      }
2251  
2252      /**
2253      * unsets an HTTP header
2254      *
2255      * @param string $name The name of the header
2256      * @access private
2257      */
2258  	function unsetHeader($name) {
2259          if (isset($this->outgoing_headers[$name])) {
2260              $this->debug("unset header $name");
2261              unset($this->outgoing_headers[$name]);
2262          }
2263      }
2264  
2265      /**
2266      * sets the URL to which to connect
2267      *
2268      * @param string $url The URL to which to connect
2269      * @access private
2270      */
2271  	function setURL($url) {
2272          $this->url = $url;
2273  
2274          $u = parse_url($url);
2275          foreach($u as $k => $v){
2276              $this->debug("parsed URL $k = $v");
2277              $this->$k = $v;
2278          }
2279          
2280          // add any GET params to path
2281          if(isset($u['query']) && $u['query'] != ''){
2282              $this->path .= '?' . $u['query'];
2283          }
2284          
2285          // set default port
2286          if(!isset($u['port'])){
2287              if($u['scheme'] == 'https'){
2288                  $this->port = 443;
2289              } else {
2290                  $this->port = 80;
2291              }
2292          }
2293          
2294          $this->uri = $this->path;
2295          $this->digest_uri = $this->uri;
2296          
2297          // build headers
2298          if (!isset($u['port'])) {
2299              $this->setHeader('Host', $this->host);
2300          } else {
2301              $this->setHeader('Host', $this->host.':'.$this->port);
2302          }
2303  
2304          if (isset($u['user']) && $u['user'] != '') {
2305              $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2306          }
2307      }
2308  
2309      /**
2310      * gets the I/O method to use
2311      *
2312      * @return    string    I/O method to use (socket|curl|unknown)
2313      * @access    private
2314      */
2315  	function io_method() {
2316          if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2317              return 'curl';
2318          if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2319              return 'socket';
2320          return 'unknown';
2321      }
2322  
2323      /**
2324      * establish an HTTP connection
2325      *
2326      * @param    integer $timeout set connection timeout in seconds
2327      * @param    integer $response_timeout set response timeout in seconds
2328      * @return    boolean true if connected, false if not
2329      * @access   private
2330      */
2331  	function connect($connection_timeout=0,$response_timeout=30){
2332            // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2333            // "regular" socket.
2334            // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2335            //       loaded), and until PHP5 stream_get_wrappers is not available.
2336  //          if ($this->scheme == 'https') {
2337  //              if (version_compare(phpversion(), '4.3.0') >= 0) {
2338  //                  if (extension_loaded('openssl')) {
2339  //                      $this->scheme = 'ssl';
2340  //                      $this->debug('Using SSL over OpenSSL');
2341  //                  }
2342  //              }
2343  //        }
2344          $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2345        if ($this->io_method() == 'socket') {
2346          if (!is_array($this->proxy)) {
2347              $host = $this->host;
2348              $port = $this->port;
2349          } else {
2350              $host = $this->proxy['host'];
2351              $port = $this->proxy['port'];
2352          }
2353  
2354          // use persistent connection
2355          if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2356              if (!feof($this->fp)) {
2357                  $this->debug('Re-use persistent connection');
2358                  return true;
2359              }
2360              fclose($this->fp);
2361              $this->debug('Closed persistent connection at EOF');
2362          }
2363  
2364          // munge host if using OpenSSL
2365          if ($this->scheme == 'ssl') {
2366              $host = 'ssl://' . $host;
2367          }
2368          $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2369  
2370          // open socket
2371          if($connection_timeout > 0){
2372              $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2373          } else {
2374              $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2375          }
2376          
2377          // test pointer
2378          if(!$this->fp) {
2379              $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2380              if ($this->errno) {
2381                  $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2382              } else {
2383                  $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2384              }
2385              $this->debug($msg);
2386              $this->setError($msg);
2387              return false;
2388          }
2389          
2390          // set response timeout
2391          $this->debug('set response timeout to ' . $response_timeout);
2392          socket_set_timeout( $this->fp, $response_timeout);
2393  
2394          $this->debug('socket connected');
2395          return true;
2396        } else if ($this->io_method() == 'curl') {
2397          if (!extension_loaded('curl')) {
2398  //            $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2399              $this->setError('The PHP cURL Extension is required for HTTPS or NLTM.  You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
2400              return false;
2401          }
2402          // Avoid warnings when PHP does not have these options
2403          if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2404              $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2405          else
2406              $CURLOPT_CONNECTIONTIMEOUT = 78;
2407          if (defined('CURLOPT_HTTPAUTH'))
2408              $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2409          else
2410              $CURLOPT_HTTPAUTH = 107;
2411          if (defined('CURLOPT_PROXYAUTH'))
2412              $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2413          else
2414              $CURLOPT_PROXYAUTH = 111;
2415          if (defined('CURLAUTH_BASIC'))
2416              $CURLAUTH_BASIC = CURLAUTH_BASIC;
2417          else
2418              $CURLAUTH_BASIC = 1;
2419          if (defined('CURLAUTH_DIGEST'))
2420              $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2421          else
2422              $CURLAUTH_DIGEST = 2;
2423          if (defined('CURLAUTH_NTLM'))
2424              $CURLAUTH_NTLM = CURLAUTH_NTLM;
2425          else
2426              $CURLAUTH_NTLM = 8;
2427  
2428          $this->debug('connect using cURL');
2429          // init CURL
2430          $this->ch = curl_init();
2431          // set url
2432          $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2433          // add path
2434          $hostURL .= $this->path;
2435          $this->setCurlOption(CURLOPT_URL, $hostURL);
2436          // follow location headers (re-directs)
2437          if (ini_get('safe_mode') || ini_get('open_basedir')) {
2438              $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2439              $this->debug('safe_mode = ');
2440              $this->appendDebug($this->varDump(ini_get('safe_mode')));
2441              $this->debug('open_basedir = ');
2442              $this->appendDebug($this->varDump(ini_get('open_basedir')));
2443          } else {
2444              $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2445          }
2446          // ask for headers in the response output
2447          $this->setCurlOption(CURLOPT_HEADER, 1);
2448          // ask for the response output as the return value
2449          $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2450          // encode
2451          // We manage this ourselves through headers and encoding
2452  //        if(function_exists('gzuncompress')){
2453  //            $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2454  //        }
2455          // persistent connection
2456          if ($this->persistentConnection) {
2457              // I believe the following comment is now bogus, having applied to
2458              // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2459              // The way we send data, we cannot use persistent connections, since
2460              // there will be some "junk" at the end of our request.
2461              //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2462              $this->persistentConnection = false;
2463              $this->setHeader('Connection', 'close');
2464          }
2465          // set timeouts
2466          if ($connection_timeout != 0) {
2467              $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2468          }
2469          if ($response_timeout != 0) {
2470              $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2471          }
2472  
2473          if ($this->scheme == 'https') {
2474              $this->debug('set cURL SSL verify options');
2475              // recent versions of cURL turn on peer/host checking by default,
2476              // while PHP binaries are not compiled with a default location for the
2477              // CA cert bundle, so disable peer/host checking.
2478              //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');        
2479              $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2480              $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2481      
2482              // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2483              if ($this->authtype == 'certificate') {
2484                  $this->debug('set cURL certificate options');
2485                  if (isset($this->certRequest['cainfofile'])) {
2486                      $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2487                  }
2488                  if (isset($this->certRequest['verifypeer'])) {
2489                      $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2490                  } else {
2491                      $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2492                  }
2493                  if (isset($this->certRequest['verifyhost'])) {
2494                      $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2495                  } else {
2496                      $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2497                  }
2498                  if (isset($this->certRequest['sslcertfile'])) {
2499                      $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2500                  }
2501                  if (isset($this->certRequest['sslkeyfile'])) {
2502                      $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2503                  }
2504                  if (isset($this->certRequest['passphrase'])) {
2505                      $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2506                  }
2507                  if (isset($this->certRequest['certpassword'])) {
2508                      $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2509                  }
2510              }
2511          }
2512          if ($this->authtype && ($this->authtype != 'certificate')) {
2513              if ($this->username) {
2514                  $this->debug('set cURL username/password');
2515                  $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2516              }
2517              if ($this->authtype == 'basic') {
2518                  $this->debug('set cURL for Basic authentication');
2519                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2520              }
2521              if ($this->authtype == 'digest') {
2522                  $this->debug('set cURL for digest authentication');
2523                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2524              }
2525              if ($this->authtype == 'ntlm') {
2526                  $this->debug('set cURL for NTLM authentication');
2527                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2528              }
2529          }
2530          if (is_array($this->proxy)) {
2531              $this->debug('set cURL proxy options');
2532              if ($this->proxy['port'] != '') {
2533                  $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2534              } else {
2535                  $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2536              }
2537              if ($this->proxy['username'] || $this->proxy['password']) {
2538                  $this->debug('set cURL proxy authentication options');
2539                  $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2540                  if ($this->proxy['authtype'] == 'basic') {
2541                      $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2542                  }
2543                  if ($this->proxy['authtype'] == 'ntlm') {
2544                      $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2545                  }
2546              }
2547          }
2548          $this->debug('cURL connection set up');
2549          return true;
2550        } else {
2551          $this->setError('Unknown scheme ' . $this->scheme);
2552          $this->debug('Unknown scheme ' . $this->scheme);
2553          return false;
2554        }
2555      }
2556  
2557      /**
2558      * sends the SOAP request and gets the SOAP response via HTTP[S]
2559      *
2560      * @param    string $data message data
2561      * @param    integer $timeout set connection timeout in seconds
2562      * @param    integer $response_timeout set response timeout in seconds
2563      * @param    array $cookies cookies to send
2564      * @return    string data
2565      * @access   public
2566      */
2567  	function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2568          
2569          $this->debug('entered send() with data of length: '.strlen($data));
2570  
2571          $this->tryagain = true;
2572          $tries = 0;
2573          while ($this->tryagain) {
2574              $this->tryagain = false;
2575              if ($tries++ < 2) {
2576                  // make connnection
2577                  if (!$this->connect($timeout, $response_timeout)){
2578                      return false;
2579                  }
2580                  
2581                  // send request
2582                  if (!$this->sendRequest($data, $cookies)){
2583                      return false;
2584                  }
2585                  
2586                  // get response
2587                  $respdata = $this->getResponse();
2588              } else {
2589                  $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2590              }
2591          }        
2592          $this->debug('end of send()');
2593          return $respdata;
2594      }
2595  
2596  
2597      /**
2598      * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2599      *
2600      * @param    string $data message data
2601      * @param    integer $timeout set connection timeout in seconds
2602      * @param    integer $response_timeout set response timeout in seconds
2603      * @param    array $cookies cookies to send
2604      * @return    string data
2605      * @access   public
2606      * @deprecated
2607      */
2608  	function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2609          return $this->send($data, $timeout, $response_timeout, $cookies);
2610      }
2611      
2612      /**
2613      * if authenticating, set user credentials here
2614      *
2615      * @param    string $username
2616      * @param    string $password
2617      * @param    string $authtype (basic|digest|certificate|ntlm)
2618      * @param    array $digestRequest (keys must be nonce, nc, realm, qop)
2619      * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2620      * @access   public
2621      */
2622  	function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2623          $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2624          $this->appendDebug($this->varDump($digestRequest));
2625          $this->debug("certRequest=");
2626          $this->appendDebug($this->varDump($certRequest));
2627          // cf. RFC 2617
2628          if ($authtype == 'basic') {
2629              $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2630          } elseif ($authtype == 'digest') {
2631              if (isset($digestRequest['nonce'])) {
2632                  $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2633                  
2634                  // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2635      
2636                  // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2637                  $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2638      
2639                  // H(A1) = MD5(A1)
2640                  $HA1 = md5($A1);
2641      
2642                  // A2 = Method ":" digest-uri-value
2643                  $A2 = $this->request_method . ':' . $this->digest_uri;
2644      
2645                  // H(A2)
2646                  $HA2 =  md5($A2);
2647      
2648                  // KD(secret, data) = H(concat(secret, ":", data))
2649                  // if qop == auth:
2650                  // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2651                  //                              ":" nc-value
2652                  //                              ":" unq(cnonce-value)
2653                  //                              ":" unq(qop-value)
2654                  //                              ":" H(A2)
2655                  //                            ) <">
2656                  // if qop is missing,
2657                  // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2658      
2659                  $unhashedDigest = '';
2660                  $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2661                  $cnonce = $nonce;
2662                  if ($digestRequest['qop'] != '') {
2663                      $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2664                  } else {
2665                      $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2666                  }
2667      
2668                  $hashedDigest = md5($unhashedDigest);
2669      
2670                  $opaque = '';    
2671                  if (isset($digestRequest['opaque'])) {
2672                      $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2673                  }
2674  
2675                  $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2676              }
2677          } elseif ($authtype == 'certificate') {
2678              $this->certRequest = $certRequest;
2679              $this->debug('Authorization header not set for certificate');
2680          } elseif ($authtype == 'ntlm') {
2681              // do nothing
2682              $this->debug('Authorization header not set for ntlm');
2683          }
2684          $this->username = $username;
2685          $this->password = $password;
2686          $this->authtype = $authtype;
2687          $this->digestRequest = $digestRequest;
2688      }
2689      
2690      /**
2691      * set the soapaction value
2692      *
2693      * @param    string $soapaction
2694      * @access   public
2695      */
2696  	function setSOAPAction($soapaction) {
2697          $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2698      }
2699      
2700      /**
2701      * use http encoding
2702      *
2703      * @param    string $enc encoding style. supported values: gzip, deflate, or both
2704      * @access   public
2705      */
2706  	function setEncoding($enc='gzip, deflate') {
2707          if (function_exists('gzdeflate')) {
2708              $this->protocol_version = '1.1';
2709              $this->setHeader('Accept-Encoding', $enc);
2710              if (!isset($this->outgoing_headers['Connection'])) {
2711                  $this->setHeader('Connection', 'close');
2712                  $this->persistentConnection = false;
2713              }
2714              // deprecated as of PHP 5.3.0
2715              //set_magic_quotes_runtime(0);
2716              $this->encoding = $enc;
2717          }
2718      }
2719      
2720      /**
2721      * set proxy info here
2722      *
2723      * @param    string $proxyhost use an empty string to remove proxy
2724      * @param    string $proxyport
2725      * @param    string $proxyusername
2726      * @param    string $proxypassword
2727      * @param    string $proxyauthtype (basic|ntlm)
2728      * @access   public
2729      */
2730  	function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2731          if ($proxyhost) {
2732              $this->proxy = array(
2733                  'host' => $proxyhost,
2734                  'port' => $proxyport,
2735                  'username' => $proxyusername,
2736                  'password' => $proxypassword,
2737                  'authtype' => $proxyauthtype
2738              );
2739              if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2740                  $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2741              }
2742          } else {
2743              $this->debug('remove proxy');
2744              $proxy = null;
2745              unsetHeader('Proxy-Authorization');
2746          }
2747      }
2748      
2749  
2750      /**
2751       * Test if the given string starts with a header that is to be skipped.
2752       * Skippable headers result from chunked transfer and proxy requests.
2753       *
2754       * @param    string $data The string to check.
2755       * @returns    boolean    Whether a skippable header was found.
2756       * @access    private
2757       */
2758  	function isSkippableCurlHeader(&$data) {
2759          $skipHeaders = array(    'HTTP/1.1 100',
2760                                  'HTTP/1.0 301',
2761                                  'HTTP/1.1 301',
2762                                  'HTTP/1.0 302',
2763                                  'HTTP/1.1 302',
2764                                  'HTTP/1.0 401',
2765                                  'HTTP/1.1 401',
2766                                  'HTTP/1.0 200 Connection established');
2767          foreach ($skipHeaders as $hd) {
2768              $prefix = substr($data, 0, strlen($hd));
2769              if ($prefix == $hd) return true;
2770          }
2771  
2772          return false;
2773      }
2774  
2775      /**
2776      * decode a string that is encoded w/ "chunked' transfer encoding
2777       * as defined in RFC2068 19.4.6
2778      *
2779      * @param    string $buffer
2780      * @param    string $lb
2781      * @returns    string
2782      * @access   public
2783      * @deprecated
2784      */
2785  	function decodeChunked($buffer, $lb){
2786          // length := 0
2787          $length = 0;
2788          $new = '';
2789          
2790          // read chunk-size, chunk-extension (if any) and CRLF
2791          // get the position of the linebreak
2792          $chunkend = strpos($buffer, $lb);
2793          if ($chunkend == FALSE) {
2794              $this->debug('no linebreak found in decodeChunked');
2795              return $new;
2796          }
2797          $temp = substr($buffer,0,$chunkend);
2798          $chunk_size = hexdec( trim($temp) );
2799          $chunkstart = $chunkend + strlen($lb);
2800          // while (chunk-size > 0) {
2801          while ($chunk_size > 0) {
2802              $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2803              $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2804                
2805              // Just in case we got a broken connection
2806                if ($chunkend == FALSE) {
2807                    $chunk = substr($buffer,$chunkstart);
2808                  // append chunk-data to entity-body
2809                  $new .= $chunk;
2810                    $length += strlen($chunk);
2811                    break;
2812              }
2813              
2814                // read chunk-data and CRLF
2815                $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2816                // append chunk-data to entity-body
2817                $new .= $chunk;
2818                // length := length + chunk-size
2819                $length += strlen($chunk);
2820                // read chunk-size and CRLF
2821                $chunkstart = $chunkend + strlen($lb);
2822              
2823                $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2824              if ($chunkend == FALSE) {
2825                  break; //Just in case we got a broken connection
2826              }
2827              $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2828              $chunk_size = hexdec( trim($temp) );
2829              $chunkstart = $chunkend;
2830          }
2831          return $new;
2832      }
2833      
2834      /**
2835       * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2836       *
2837       * @param    string $data HTTP body
2838       * @param    string $cookie_str data for HTTP Cookie header
2839       * @return    void
2840       * @access    private
2841       */
2842  	function buildPayload($data, $cookie_str = '') {
2843          // Note: for cURL connections, $this->outgoing_payload is ignored,
2844          // as is the Content-Length header, but these are still created as
2845          // debugging guides.
2846  
2847          // add content-length header
2848          if ($this->request_method != 'GET') {
2849              $this->setHeader('Content-Length', strlen($data));
2850          }
2851  
2852          // start building outgoing payload:
2853          if ($this->proxy) {
2854              $uri = $this->url;
2855          } else {
2856              $uri = $this->uri;
2857          }
2858          $req = "$this->request_method $uri HTTP/$this->protocol_version";
2859          $this->debug("HTTP request: $req");
2860          $this->outgoing_payload = "$req\r\n";
2861  
2862          // loop thru headers, serializing
2863          foreach($this->outgoing_headers as $k => $v){
2864              $hdr = $k.': '.$v;
2865              $this->debug("HTTP header: $hdr");
2866              $this->outgoing_payload .= "$hdr\r\n";
2867          }
2868  
2869          // add any cookies
2870          if ($cookie_str != '') {
2871              $hdr = 'Cookie: '.$cookie_str;
2872              $this->debug("HTTP header: $hdr");
2873              $this->outgoing_payload .= "$hdr\r\n";
2874          }
2875  
2876          // header/body separator
2877          $this->outgoing_payload .= "\r\n";
2878          
2879          // add data
2880          $this->outgoing_payload .= $data;
2881      }
2882  
2883      /**
2884      * sends the SOAP request via HTTP[S]
2885      *
2886      * @param    string $data message data
2887      * @param    array $cookies cookies to send
2888      * @return    boolean    true if OK, false if problem
2889      * @access   private
2890      */
2891  	function sendRequest($data, $cookies = NULL) {
2892          // build cookie string
2893          $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2894  
2895          // build payload
2896          $this->buildPayload($data, $cookie_str);
2897  
2898        if ($this->io_method() == 'socket') {
2899          // send payload
2900          if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2901              $this->setError('couldn\'t write message data to socket');
2902              $this->debug('couldn\'t write message data to socket');
2903              return false;
2904          }
2905          $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2906          return true;
2907        } else if ($this->io_method() == 'curl') {
2908          // set payload
2909          // cURL does say this should only be the verb, and in fact it
2910          // turns out that the URI and HTTP version are appended to this, which
2911          // some servers refuse to work with (so we no longer use this method!)
2912          //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2913          $curl_headers = array();
2914          foreach($this->outgoing_headers as $k => $v){
2915              if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2916                  $this->debug("Skip cURL header $k: $v");
2917              } else {
2918                  $curl_headers[] = "$k: $v";
2919              }
2920          }
2921          if ($cookie_str != '') {
2922              $curl_headers[] = 'Cookie: ' . $cookie_str;
2923          }
2924          $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2925          $this->debug('set cURL HTTP headers');
2926          if ($this->request_method == "POST") {
2927                $this->setCurlOption(CURLOPT_POST, 1);
2928                $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2929              $this->debug('set cURL POST data');
2930            } else {
2931            }
2932          // insert custom user-set cURL options
2933          foreach ($this->ch_options as $key => $val) {
2934              $this->setCurlOption($key, $val);
2935          }
2936  
2937          $this->debug('set cURL payload');
2938          return true;
2939        }
2940      }
2941  
2942      /**
2943      * gets the SOAP response via HTTP[S]
2944      *
2945      * @return    string the response (also sets member variables like incoming_payload)
2946      * @access   private
2947      */
2948  	function getResponse(){
2949          $this->incoming_payload = '';
2950          
2951        if ($this->io_method() == 'socket') {
2952          // loop until headers have been retrieved
2953          $data = '';
2954          while (!isset($lb)){
2955  
2956              // We might EOF during header read.
2957              if(feof($this->fp)) {
2958                  $this->incoming_payload = $data;
2959                  $this->debug('found no headers before EOF after length ' . strlen($data));
2960                  $this->debug("received before EOF:\n" . $data);
2961                  $this->setError('server failed to send headers');
2962                  return false;
2963              }
2964  
2965              $tmp = fgets($this->fp, 256);
2966              $tmplen = strlen($tmp);
2967              $this->debug("read line of $tmplen bytes: " . trim($tmp));
2968  
2969              if ($tmplen == 0) {
2970                  $this->incoming_payload = $data;
2971                  $this->debug('socket read of headers timed out after length ' . strlen($data));
2972                  $this->debug("read before timeout: " . $data);
2973                  $this->setError('socket read of headers timed out');
2974                  return false;
2975              }
2976  
2977              $data .= $tmp;
2978              $pos = strpos($data,"\r\n\r\n");
2979              if($pos > 1){
2980                  $lb = "\r\n";
2981              } else {
2982                  $pos = strpos($data,"\n\n");
2983                  if($pos > 1){
2984                      $lb = "\n";
2985                  }
2986              }
2987              // remove 100 headers
2988              if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
2989                  unset($lb);
2990                  $data = '';
2991              }//
2992          }
2993          // store header data
2994          $this->incoming_payload .= $data;
2995          $this->debug('found end of headers after length ' . strlen($data));
2996          // process headers
2997          $header_data = trim(substr($data,0,$pos));
2998          $header_array = explode($lb,$header_data);
2999          $this->incoming_headers = array();
3000          $this->incoming_cookies = array();
3001          foreach($header_array as $header_line){
3002              $arr = explode(':',$header_line, 2);
3003              if(count($arr) > 1){
3004                  $header_name = strtolower(trim($arr[0]));
3005                  $this->incoming_headers[$header_name] = trim($arr[1]);
3006                  if ($header_name == 'set-cookie') {
3007                      // TODO: allow multiple cookies from parseCookie
3008                      $cookie = $this->parseCookie(trim($arr[1]));
3009                      if ($cookie) {
3010                          $this->incoming_cookies[] = $cookie;
3011                          $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3012                      } else {
3013                          $this->debug('did not find cookie in ' . trim($arr[1]));
3014                      }
3015                  }
3016              } else if (isset($header_name)) {
3017                  // append continuation line to previous header
3018                  $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3019              }
3020          }
3021          
3022          // loop until msg has been received
3023          if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
3024              $content_length =  2147483647;    // ignore any content-length header
3025              $chunked = true;
3026              $this->debug("want to read chunked content");
3027          } elseif (isset($this->incoming_headers['content-length'])) {
3028              $content_length = $this->incoming_headers['content-length'];
3029              $chunked = false;
3030              $this->debug("want to read content of length $content_length");
3031          } else {
3032              $content_length =  2147483647;
3033              $chunked = false;
3034              $this->debug("want to read content to EOF");
3035          }
3036          $data = '';
3037          do {
3038              if ($chunked) {
3039                  $tmp = fgets($this->fp, 256);
3040                  $tmplen = strlen($tmp);
3041                  $this->debug("read chunk line of $tmplen bytes");
3042                  if ($tmplen == 0) {
3043                      $this->incoming_payload = $data;
3044                      $this->debug('socket read of chunk length timed out after length ' . strlen($data));
3045                      $this->debug("read before timeout:\n" . $data);
3046                      $this->setError('socket read of chunk length timed out');
3047                      return false;
3048                  }
3049                  $content_length = hexdec(trim($tmp));
3050                  $this->debug("chunk length $content_length");
3051              }
3052              $strlen = 0;
3053              while (($strlen < $content_length) && (!feof($this->fp))) {
3054                  $readlen = min(8192, $content_length - $strlen);
3055                  $tmp = fread($this->fp, $readlen);
3056                  $tmplen = strlen($tmp);
3057                  $this->debug("read buffer of $tmplen bytes");
3058                  if (($tmplen == 0) && (!feof($this->fp))) {
3059                      $this->incoming_payload = $data;
3060                      $this->debug('socket read of body timed out after length ' . strlen($data));
3061                      $this->debug("read before timeout:\n" . $data);
3062                      $this->setError('socket read of body timed out');
3063                      return false;
3064                  }
3065                  $strlen += $tmplen;
3066                  $data .= $tmp;
3067              }
3068              if ($chunked && ($content_length > 0)) {
3069                  $tmp = fgets($this->fp, 256);
3070                  $tmplen = strlen($tmp);
3071                  $this->debug("read chunk terminator of $tmplen bytes");
3072                  if ($tmplen == 0) {
3073                      $this->incoming_payload = $data;
3074                      $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3075                      $this->debug("read before timeout:\n" . $data);
3076                      $this->setError('socket read of chunk terminator timed out');
3077                      return false;
3078                  }
3079              }
3080          } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3081          if (feof($this->fp)) {
3082              $this->debug('read to EOF');
3083          }
3084          $this->debug('read body of length ' . strlen($data));
3085          $this->incoming_payload .= $data;
3086          $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3087          
3088          // close filepointer
3089          if(
3090              (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') || 
3091              (! $this->persistentConnection) || feof($this->fp)){
3092              fclose($this->fp);
3093              $this->fp = false;
3094              $this->debug('closed socket');
3095          }
3096          
3097          // connection was closed unexpectedly
3098          if($this->incoming_payload == ''){
3099              $this->setError('no response from server');
3100              return false;
3101          }
3102          
3103          // decode transfer-encoding
3104  //        if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3105  //            if(!$data = $this->decodeChunked($data, $lb)){
3106  //                $this->setError('Decoding of chunked data failed');
3107  //                return false;
3108  //            }
3109              //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3110              // set decoded payload
3111  //            $this->incoming_payload = $header_data.$lb.$lb.$data;
3112  //        }
3113      
3114        } else if ($this->io_method() == 'curl') {
3115          // send and receive
3116          $this->debug('send and receive with cURL');
3117          $this->incoming_payload = curl_exec($this->ch);
3118          $data = $this->incoming_payload;
3119  
3120          $cErr = curl_error($this->ch);
3121          if ($cErr != '') {
3122              $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3123              // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3124              foreach(curl_getinfo($this->ch) as $k => $v){
3125                  $err .= "$k: $v<br>";
3126              }
3127              $this->debug($err);
3128              $this->setError($err);
3129              curl_close($this->ch);
3130              return false;
3131          } else {
3132              //echo '<pre>';
3133              //var_dump(curl_getinfo($this->ch));
3134              //echo '</pre>';
3135          }
3136          // close curl
3137          $this->debug('No cURL error, closing cURL');
3138          curl_close($this->ch);
3139          
3140          // try removing skippable headers
3141          $savedata = $data;
3142          while ($this->isSkippableCurlHeader($data)) {
3143              $this->debug("Found HTTP header to skip");
3144              if ($pos = strpos($data,"\r\n\r\n")) {
3145                  $data = ltrim(substr($data,$pos));
3146              } elseif($pos = strpos($data,"\n\n") ) {
3147                  $data = ltrim(substr($data,$pos));
3148              }
3149          }
3150  
3151          if ($data == '') {
3152              // have nothing left; just remove 100 header(s)
3153              $data = $savedata;
3154              while (preg_match('/^HTTP\/1.1 100/',$data)) {
3155                  if ($pos = strpos($data,"\r\n\r\n")) {
3156                      $data = ltrim(substr($data,$pos));
3157                  } elseif($pos = strpos($data,"\n\n") ) {
3158                      $data = ltrim(substr($data,$pos));
3159                  }
3160              }
3161          }
3162          
3163          // separate content from HTTP headers
3164          if ($pos = strpos($data,"\r\n\r\n")) {
3165              $lb = "\r\n";
3166          } elseif( $pos = strpos($data,"\n\n")) {
3167              $lb = "\n";
3168          } else {
3169              $this->debug('no proper separation of headers and document');
3170              $this->setError('no proper separation of headers and document');
3171              return false;
3172          }
3173          $header_data = trim(substr($data,0,$pos));
3174          $header_array = explode($lb,$header_data);
3175          $data = ltrim(substr($data,$pos));
3176          $this->debug('found proper separation of headers and document');
3177          $this->debug('cleaned data, stringlen: '.strlen($data));
3178          // clean headers
3179          foreach ($header_array as $header_line) {
3180              $arr = explode(':',$header_line,2);
3181              if(count($arr) > 1){
3182                  $header_name = strtolower(trim($arr[0]));
3183                  $this->incoming_headers[$header_name] = trim($arr[1]);
3184                  if ($header_name == 'set-cookie') {
3185                      // TODO: allow multiple cookies from parseCookie
3186                      $cookie = $this->parseCookie(trim($arr[1]));
3187                      if ($cookie) {
3188                          $this->incoming_cookies[] = $cookie;
3189                          $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3190                      } else {
3191                          $this->debug('did not find cookie in ' . trim($arr[1]));
3192                      }
3193                  }
3194              } else if (isset($header_name)) {
3195                  // append continuation line to previous header
3196                  $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3197              }
3198          }
3199        }
3200  
3201          $this->response_status_line = $header_array[0];
3202          $arr = explode(' ', $this->response_status_line, 3);
3203          $http_version = $arr[0];
3204          $http_status = intval($arr[1]);
3205          $http_reason = count($arr) > 2 ? $arr[2] : '';
3206  
3207           // see if we need to resend the request with http digest authentication
3208           if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3209               $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3210               $this->setURL($this->incoming_headers['location']);
3211              $this->tryagain = true;
3212              return false;
3213          }
3214  
3215           // see if we need to resend the request with http digest authentication
3216           if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3217               $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3218               if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3219                   $this->debug('Server wants digest authentication');
3220                   // remove "Digest " from our elements
3221                   $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3222                   
3223                   // parse elements into array
3224                   $digestElements = explode(',', $digestString);
3225                   foreach ($digestElements as $val) {
3226                       $tempElement = explode('=', trim($val), 2);
3227                       $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3228                   }
3229  
3230                  // should have (at least) qop, realm, nonce
3231                   if (isset($digestRequest['nonce'])) {
3232                       $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3233                       $this->tryagain = true;
3234                       return false;
3235                   }
3236               }
3237              $this->debug('HTTP authentication failed');
3238              $this->setError('HTTP authentication failed');
3239              return false;
3240           }
3241          
3242          if (
3243              ($http_status >= 300 && $http_status <= 307) ||
3244              ($http_status >= 400 && $http_status <= 417) ||
3245              ($http_status >= 501 && $http_status <= 505)
3246             ) {
3247              $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3248              return false;
3249          }
3250  
3251          // decode content-encoding
3252          if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3253              if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3254                  // if decoding works, use it. else assume data wasn't gzencoded
3255                  if(function_exists('gzinflate')){
3256                      //$timer->setMarker('starting decoding of gzip/deflated content');
3257                      // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3258                      // this means there are no Zlib headers, although there should be
3259                      $this->debug('The gzinflate function exists');
3260                      $datalen = strlen($data);
3261                      if ($this->incoming_headers['content-encoding'] == 'deflate') {
3262                          if ($degzdata = @gzinflate($data)) {
3263                              $data = $degzdata;
3264                              $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3265                              if (strlen($data) < $datalen) {
3266                                  // test for the case that the payload has been compressed twice
3267                                  $this->debug('The inflated payload is smaller than the gzipped one; try again');
3268                                  if ($degzdata = @gzinflate($data)) {
3269                                      $data = $degzdata;
3270                                      $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3271                                  }
3272                              }
3273                          } else {
3274                              $this->debug('Error using gzinflate to inflate the payload');
3275                              $this->setError('Error using gzinflate to inflate the payload');
3276                          }
3277                      } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3278                          if ($degzdata = @gzinflate(substr($data, 10))) {    // do our best
3279                              $data = $degzdata;
3280                              $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3281                              if (strlen($data) < $datalen) {
3282                                  // test for the case that the payload has been compressed twice
3283                                  $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3284                                  if ($degzdata = @gzinflate(substr($data, 10))) {
3285                                      $data = $degzdata;
3286                                      $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3287                                  }
3288                              }
3289                          } else {
3290                              $this->debug('Error using gzinflate to un-gzip the payload');
3291                              $this->setError('Error using gzinflate to un-gzip the payload');
3292                          }
3293                      }
3294                      //$timer->setMarker('finished decoding of gzip/deflated content');
3295                      //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3296                      // set decoded payload
3297                      $this->incoming_payload = $header_data.$lb.$lb.$data;
3298                  } else {
3299                      $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3300                      $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3301                  }
3302              } else {
3303                  $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3304                  $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3305              }
3306          } else {
3307              $this->debug('No Content-Encoding header');
3308          }
3309          
3310          if(strlen($data) == 0){
3311              $this->debug('no data after headers!');
3312              $this->setError('no data present after HTTP headers');
3313              return false;
3314          }
3315          
3316          return $data;
3317      }
3318  
3319      /**
3320       * sets the content-type for the SOAP message to be sent
3321       *
3322       * @param    string $type the content type, MIME style
3323       * @param    mixed $charset character set used for encoding (or false)
3324       * @access    public
3325       */
3326  	function setContentType($type, $charset = false) {
3327          $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3328      }
3329  
3330      /**
3331       * specifies that an HTTP persistent connection should be used
3332       *
3333       * @return    boolean whether the request was honored by this method.
3334       * @access    public
3335       */
3336  	function usePersistentConnection(){
3337          if (isset($this->outgoing_headers['Accept-Encoding'])) {
3338              return false;
3339          }
3340          $this->protocol_version = '1.1';
3341          $this->persistentConnection = true;
3342          $this->setHeader('Connection', 'Keep-Alive');
3343          return true;
3344      }
3345  
3346      /**
3347       * parse an incoming Cookie into it's parts
3348       *
3349       * @param    string $cookie_str content of cookie
3350       * @return    array with data of that cookie
3351       * @access    private
3352       */
3353      /*
3354       * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3355       */
3356  	function parseCookie($cookie_str) {
3357          $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3358          $data = preg_split('/;/', $cookie_str);
3359          $value_str = $data[0];
3360  
3361          $cookie_param = 'domain=';
3362          $start = strpos($cookie_str, $cookie_param);
3363          if ($start > 0) {
3364              $domain = substr($cookie_str, $start + strlen($cookie_param));
3365              $domain = substr($domain, 0, strpos($domain, ';'));
3366          } else {
3367              $domain = '';
3368          }
3369  
3370          $cookie_param = 'expires=';
3371          $start = strpos($cookie_str, $cookie_param);
3372          if ($start > 0) {
3373              $expires = substr($cookie_str, $start + strlen($cookie_param));
3374              $expires = substr($expires, 0, strpos($expires, ';'));
3375          } else {
3376              $expires = '';
3377          }
3378  
3379          $cookie_param = 'path=';
3380          $start = strpos($cookie_str, $cookie_param);
3381          if ( $start > 0 ) {
3382              $path = substr($cookie_str, $start + strlen($cookie_param));
3383              $path = substr($path, 0, strpos($path, ';'));
3384          } else {
3385              $path = '/';
3386          }
3387                          
3388          $cookie_param = ';secure;';
3389          if (strpos($cookie_str, $cookie_param) !== FALSE) {
3390              $secure = true;
3391          } else {
3392              $secure = false;
3393          }
3394  
3395          $sep_pos = strpos($value_str, '=');
3396  
3397          if ($sep_pos) {
3398              $name = substr($value_str, 0, $sep_pos);
3399              $value = substr($value_str, $sep_pos + 1);
3400              $cookie= array(    'name' => $name,
3401                              'value' => $value,
3402                              'domain' => $domain,
3403                              'path' => $path,
3404                              'expires' => $expires,
3405                              'secure' => $secure
3406                              );        
3407              return $cookie;
3408          }
3409          return false;
3410      }
3411    
3412      /**
3413       * sort out cookies for the current request
3414       *
3415       * @param    array $cookies array with all cookies
3416       * @param    boolean $secure is the send-content secure or not?
3417       * @return    string for Cookie-HTTP-Header
3418       * @access    private
3419       */
3420  	function getCookiesForRequest($cookies, $secure=false) {
3421          $cookie_str = '';
3422          if ((! is_null($cookies)) && (is_array($cookies))) {
3423              foreach ($cookies as $cookie) {
3424                  if (! is_array($cookie)) {
3425                      continue;
3426                  }
3427                  $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3428                  if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3429                      if (strtotime($cookie['expires']) <= time()) {
3430                          $this->debug('cookie has expired');
3431                          continue;
3432                      }
3433                  }
3434                  if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3435                      $domain = preg_quote($cookie['domain']);
3436                      if (! preg_match("'.*$domain$'i", $this->host)) {
3437                          $this->debug('cookie has different domain');
3438                          continue;
3439                      }
3440                  }
3441                  if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3442                      $path = preg_quote($cookie['path']);
3443                      if (! preg_match("'^$path.*'i", $this->path)) {
3444                          $this->debug('cookie is for a different path');
3445                          continue;
3446                      }
3447                  }
3448                  if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3449                      $this->debug('cookie is secure, transport is not');
3450                      continue;
3451                  }
3452                  $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3453                  $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3454              }
3455          }
3456          return $cookie_str;
3457    }
3458  }
3459  
3460  ?><?php
3461  
3462  
3463  
3464  /**
3465  *
3466  * nusoap_server allows the user to create a SOAP server
3467  * that is capable of receiving messages and returning responses
3468  *
3469  * @author   Dietrich Ayala <dietrich@ganx4.com>
3470  * @author   Scott Nichol <snichol@users.sourceforge.net>
3471  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
3472  * @access   public
3473  */
3474  class nusoap_server extends nusoap_base {
3475      /**
3476       * HTTP headers of request
3477       * @var array
3478       * @access private
3479       */
3480      var $headers = array();
3481      /**
3482       * HTTP request
3483       * @var string
3484       * @access private
3485       */
3486      var $request = '';
3487      /**
3488       * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3489       * @var string
3490       * @access public
3491       */
3492      var $requestHeaders = '';
3493      /**
3494       * SOAP Headers from request (parsed)
3495       * @var mixed
3496       * @access public
3497       */
3498      var $requestHeader = NULL;
3499      /**
3500       * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3501       * @var string
3502       * @access public
3503       */
3504      var $document = '';
3505      /**
3506       * SOAP payload for request (text)
3507       * @var string
3508       * @access public
3509       */
3510      var $requestSOAP = '';
3511      /**
3512       * requested method namespace URI
3513       * @var string
3514       * @access private
3515       */
3516      var $methodURI = '';
3517      /**
3518       * name of method requested
3519       * @var string
3520       * @access private
3521       */
3522      var $methodname = '';
3523      /**
3524       * method parameters from request
3525       * @var array
3526       * @access private
3527       */
3528      var $methodparams = array();
3529      /**
3530       * SOAP Action from request
3531       * @var string
3532       * @access private
3533       */
3534      var $SOAPAction = '';
3535      /**
3536       * character set encoding of incoming (request) messages
3537       * @var string
3538       * @access public
3539       */
3540      var $xml_encoding = '';
3541      /**
3542       * toggles whether the parser decodes element content w/ utf8_decode()
3543       * @var boolean
3544       * @access public
3545       */
3546      var $decode_utf8 = true;
3547  
3548      /**
3549       * HTTP headers of response
3550       * @var array
3551       * @access public
3552       */
3553      var $outgoing_headers = array();
3554      /**
3555       * HTTP response
3556       * @var string
3557       * @access private
3558       */
3559      var $response = '';
3560      /**
3561       * SOAP headers for response (text or array of soapval or associative array)
3562       * @var mixed
3563       * @access public
3564       */
3565      var $responseHeaders = '';
3566      /**
3567       * SOAP payload for response (text)
3568       * @var string
3569       * @access private
3570       */
3571      var $responseSOAP = '';
3572      /**
3573       * method return value to place in response
3574       * @var mixed
3575       * @access private
3576       */
3577      var $methodreturn = false;
3578      /**
3579       * whether $methodreturn is a string of literal XML
3580       * @var boolean
3581       * @access public
3582       */
3583      var $methodreturnisliteralxml = false;
3584      /**
3585       * SOAP fault for response (or false)
3586       * @var mixed
3587       * @access private
3588       */
3589      var $fault = false;
3590      /**
3591       * text indication of result (for debugging)
3592       * @var string
3593       * @access private
3594       */
3595      var $result = 'successful';
3596  
3597      /**
3598       * assoc array of operations => opData; operations are added by the register()
3599       * method or by parsing an external WSDL definition
3600       * @var array
3601       * @access private
3602       */
3603      var $operations = array();
3604      /**
3605       * wsdl instance (if one)
3606       * @var mixed
3607       * @access private
3608       */
3609      var $wsdl = false;
3610      /**
3611       * URL for WSDL (if one)
3612       * @var mixed
3613       * @access private
3614       */
3615      var $externalWSDLURL = false;
3616      /**
3617       * whether to append debug to response as XML comment
3618       * @var boolean
3619       * @access public
3620       */
3621      var $debug_flag = false;
3622  
3623  
3624      /**
3625      * constructor
3626      * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3627      *
3628      * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3629      * @access   public
3630      */
3631  	function nusoap_server($wsdl=false){
3632          parent::nusoap_base();
3633          // turn on debugging?
3634          global $debug;
3635          global $HTTP_SERVER_VARS;
3636  
3637          if (isset($_SERVER)) {
3638              $this->debug("_SERVER is defined:");
3639              $this->appendDebug($this->varDump($_SERVER));
3640          } elseif (isset($HTTP_SERVER_VARS)) {
3641              $this->debug("HTTP_SERVER_VARS is defined:");
3642              $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3643          } else {
3644              $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3645          }
3646  
3647          if (isset($debug)) {
3648              $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3649              $this->debug_flag = $debug;
3650          } elseif (isset($_SERVER['QUERY_STRING'])) {
3651              $qs = explode('&', $_SERVER['QUERY_STRING']);
3652              foreach ($qs as $v) {
3653                  if (substr($v, 0, 6) == 'debug=') {
3654                      $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3655                      $this->debug_flag = substr($v, 6);
3656                  }
3657              }
3658          } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3659              $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3660              foreach ($qs as $v) {
3661                  if (substr($v, 0, 6) == 'debug=') {
3662                      $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3663                      $this->debug_flag = substr($v, 6);
3664                  }
3665              }
3666          }
3667  
3668          // wsdl
3669          if($wsdl){
3670              $this->debug("In nusoap_server, WSDL is specified");
3671              if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3672                  $this->wsdl = $wsdl;
3673                  $this->externalWSDLURL = $this->wsdl->wsdl;
3674                  $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3675              } else {
3676                  $this->debug('Create wsdl from ' . $wsdl);
3677                  $this->wsdl = new wsdl($wsdl);
3678                  $this->externalWSDLURL = $wsdl;
3679              }
3680              $this->appendDebug($this->wsdl->getDebug());
3681              $this->wsdl->clearDebug();
3682              if($err = $this->wsdl->getError()){
3683                  die('WSDL ERROR: '.$err);
3684              }
3685          }
3686      }
3687  
3688      /**
3689      * processes request and returns response
3690      *
3691      * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3692      * @access   public
3693      */
3694  	function service($data){
3695          global $HTTP_SERVER_VARS;
3696  
3697          if (isset($_SERVER['REQUEST_METHOD'])) {
3698              $rm = $_SERVER['REQUEST_METHOD'];
3699          } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
3700              $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
3701          } else {
3702              $rm = '';
3703          }
3704  
3705          if (isset($_SERVER['QUERY_STRING'])) {
3706              $qs = $_SERVER['QUERY_STRING'];
3707          } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3708              $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3709          } else {
3710              $qs = '';
3711          }
3712          $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
3713  
3714          if ($rm == 'POST') {
3715              $this->debug("In service, invoke the request");
3716              $this->parse_request($data);
3717              if (! $this->fault) {
3718                  $this->invoke_method();
3719              }
3720              if (! $this->fault) {
3721                  $this->serialize_return();
3722              }
3723              $this->send_response();
3724          } elseif (preg_match('/wsdl/', $qs) ){
3725              $this->debug("In service, this is a request for WSDL");
3726              if ($this->externalWSDLURL){
3727                if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
3728                  $this->debug("In service, re-direct for WSDL");
3729                  header('Location: '.$this->externalWSDLURL);
3730                } else { // assume file
3731                  $this->debug("In service, use file passthru for WSDL");
3732                  header("Content-Type: text/xml\r\n");
3733                  $pos = strpos($this->externalWSDLURL, "file://");
3734                  if ($pos === false) {
3735                      $filename = $this->externalWSDLURL;
3736                  } else {
3737                      $filename = substr($this->externalWSDLURL, $pos + 7);
3738                  }
3739                  $fp = fopen($this->externalWSDLURL, 'r');
3740                  fpassthru($fp);
3741                }
3742              } elseif ($this->wsdl) {
3743                  $this->debug("In service, serialize WSDL");
3744                  header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3745                  print $this->wsdl->serialize($this->debug_flag);
3746                  if ($this->debug_flag) {
3747                      $this->debug('wsdl:');
3748                      $this->appendDebug($this->varDump($this->wsdl));
3749                      print $this->getDebugAsXMLComment();
3750                  }
3751              } else {
3752                  $this->debug("In service, there is no WSDL");
3753                  header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3754                  print "This service does not provide WSDL";
3755              }
3756          } elseif ($this->wsdl) {
3757              $this->debug("In service, return Web description");
3758              print $this->wsdl->webDescription();
3759          } else {
3760              $this->debug("In service, no Web description");
3761              header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3762              print "This service does not provide a Web description";
3763          }
3764      }
3765  
3766      /**
3767      * parses HTTP request headers.
3768      *
3769      * The following fields are set by this function (when successful)
3770      *
3771      * headers
3772      * request
3773      * xml_encoding
3774      * SOAPAction
3775      *
3776      * @access   private
3777      */
3778  	function parse_http_headers() {
3779          global $HTTP_SERVER_VARS;
3780  
3781          $this->request = '';
3782          $this->SOAPAction = '';
3783          if(function_exists('getallheaders')){
3784              $this->debug("In parse_http_headers, use getallheaders");
3785              $headers = getallheaders();
3786              foreach($headers as $k=>$v){
3787                  $k = strtolower($k);
3788                  $this->headers[$k] = $v;
3789                  $this->request .= "$k: $v\r\n";
3790                  $this->debug("$k: $v");
3791              }
3792              // get SOAPAction header
3793              if(isset($this->headers['soapaction'])){
3794                  $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3795              }
3796              // get the character encoding of the incoming request
3797              if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3798                  $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3799                  if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
3800                      $this->xml_encoding = strtoupper($enc);
3801                  } else {
3802                      $this->xml_encoding = 'US-ASCII';
3803                  }
3804              } else {
3805                  // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3806                  $this->xml_encoding = 'ISO-8859-1';
3807              }
3808          } elseif(isset($_SERVER) && is_array($_SERVER)){
3809              $this->debug("In parse_http_headers, use _SERVER");
3810              foreach ($_SERVER as $k => $v) {
3811                  if (substr($k, 0, 5) == 'HTTP_') {
3812                      $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3813                  } else {
3814                      $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3815                  }
3816                  if ($k == 'soapaction') {
3817                      // get SOAPAction header
3818                      $k = 'SOAPAction';
3819                      $v = str_replace('"', '', $v);
3820                      $v = str_replace('\\', '', $v);
3821                      $this->SOAPAction = $v;
3822                  } else if ($k == 'content-type') {
3823                      // get the character encoding of the incoming request
3824                      if (strpos($v, '=')) {
3825                          $enc = substr(strstr($v, '='), 1);
3826                          $enc = str_replace('"', '', $enc);
3827                          $enc = str_replace('\\', '', $enc);
3828                          if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3829                              $this->xml_encoding = strtoupper($enc);
3830                          } else {
3831                              $this->xml_encoding = 'US-ASCII';
3832                          }
3833                      } else {
3834                          // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3835                          $this->xml_encoding = 'ISO-8859-1';
3836                      }
3837                  }
3838                  $this->headers[$k] = $v;
3839                  $this->request .= "$k: $v\r\n";
3840                  $this->debug("$k: $v");
3841              }
3842          } elseif (is_array($HTTP_SERVER_VARS)) {
3843              $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3844              foreach ($HTTP_SERVER_VARS as $k => $v) {
3845                  if (substr($k, 0, 5) == 'HTTP_') {
3846                      $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));                                              $k = strtolower(substr($k, 5));
3847                  } else {
3848                      $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));                                              $k = strtolower($k);
3849                  }
3850                  if ($k == 'soapaction') {
3851                      // get SOAPAction header
3852                      $k = 'SOAPAction';
3853                      $v = str_replace('"', '', $v);
3854                      $v = str_replace('\\', '', $v);
3855                      $this->SOAPAction = $v;
3856                  } else if ($k == 'content-type') {
3857                      // get the character encoding of the incoming request
3858                      if (strpos($v, '=')) {
3859                          $enc = substr(strstr($v, '='), 1);
3860                          $enc = str_replace('"', '', $enc);
3861                          $enc = str_replace('\\', '', $enc);
3862                          if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3863                              $this->xml_encoding = strtoupper($enc);
3864                          } else {
3865                              $this->xml_encoding = 'US-ASCII';
3866                          }
3867                      } else {
3868                          // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3869                          $this->xml_encoding = 'ISO-8859-1';
3870                      }
3871                  }
3872                  $this->headers[$k] = $v;
3873                  $this->request .= "$k: $v\r\n";
3874                  $this->debug("$k: $v");
3875              }
3876          } else {
3877              $this->debug("In parse_http_headers, HTTP headers not accessible");
3878              $this->setError("HTTP headers not accessible");
3879          }
3880      }
3881  
3882      /**
3883      * parses a request
3884      *
3885      * The following fields are set by this function (when successful)
3886      *
3887      * headers
3888      * request
3889      * xml_encoding
3890      * SOAPAction
3891      * request
3892      * requestSOAP
3893      * methodURI
3894      * methodname
3895      * methodparams
3896      * requestHeaders
3897      * document
3898      *
3899      * This sets the fault field on error
3900      *
3901      * @param    string $data XML string
3902      * @access   private
3903      */
3904  	function parse_request($data='') {
3905          $this->debug('entering parse_request()');
3906          $this->parse_http_headers();
3907          $this->debug('got character encoding: '.$this->xml_encoding);
3908          // uncompress if necessary
3909          if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3910              $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3911              if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3912                  // if decoding works, use it. else assume data wasn't gzencoded
3913                  if (function_exists('gzuncompress')) {
3914                      if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3915                          $data = $degzdata;
3916                      } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3917                          $data = $degzdata;
3918                      } else {
3919                          $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3920                          return;
3921                      }
3922                  } else {
3923                      $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3924                      return;
3925                  }
3926              }
3927          }
3928          $this->request .= "\r\n".$data;
3929          $data = $this->parseRequest($this->headers, $data);
3930          $this->requestSOAP = $data;
3931          $this->debug('leaving parse_request');
3932      }
3933  
3934      /**
3935      * invokes a PHP function for the requested SOAP method
3936      *
3937      * The following fields are set by this function (when successful)
3938      *
3939      * methodreturn
3940      *
3941      * Note that the PHP function that is called may also set the following
3942      * fields to affect the response sent to the client
3943      *
3944      * responseHeaders
3945      * outgoing_headers
3946      *
3947      * This sets the fault field on error
3948      *
3949      * @access   private
3950      */
3951  	function invoke_method() {
3952          $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3953  
3954          //
3955          // if you are debugging in this area of the code, your service uses a class to implement methods,
3956          // you use SOAP RPC, and the client is .NET, please be aware of the following...
3957          // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
3958          // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
3959          // the XML request and reading the XML response.  you need to add the RequestElementName and
3960          // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
3961          // generates for the method.  these parameters are used to specify the correct XML element names
3962          // for .NET to use, i.e. the names with the '.' in them.
3963          //
3964          $orig_methodname = $this->methodname;
3965          if ($this->wsdl) {
3966              if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3967                  $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3968                  $this->appendDebug('opData=' . $this->varDump($this->opData));
3969              } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3970                  // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3971                  $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3972                  $this->appendDebug('opData=' . $this->varDump($this->opData));
3973                  $this->methodname = $this->opData['name'];
3974              } else {
3975                  $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3976                  $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3977                  return;
3978              }
3979          } else {
3980              $this->debug('in invoke_method, no WSDL to validate method');
3981          }
3982  
3983          // if a . is present in $this->methodname, we see if there is a class in scope,
3984          // which could be referred to. We will also distinguish between two deliminators,
3985          // to allow methods to be called a the class or an instance
3986          if (strpos($this->methodname, '..') > 0) {
3987              $delim = '..';
3988          } else if (strpos($this->methodname, '.') > 0) {
3989              $delim = '.';
3990          } else {
3991              $delim = '';
3992          }
3993          $this->debug("in invoke_method, delim=$delim");
3994  
3995          $class = '';
3996          $method = '';
3997          if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
3998              $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3999              if (class_exists($try_class)) {
4000                  // get the class and method name
4001                  $class = $try_class;
4002                  $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
4003                  $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4004              } else {
4005                  $this->debug("in invoke_method, class=$try_class not found");
4006              }
4007          } else {
4008              $try_class = '';
4009              $this->debug("in invoke_method, no class to try");
4010          }
4011  
4012          // does method exist?
4013          if ($class == '') {
4014              if (!function_exists($this->methodname)) {
4015                  $this->debug("in invoke_method, function '$this->methodname' not found!");
4016                  $this->result = 'fault: method not found';
4017                  $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
4018                  return;
4019              }
4020          } else {
4021              $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
4022              if (!in_array($method_to_compare, get_class_methods($class))) {
4023                  $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4024                  $this->result = 'fault: method not found';
4025                  $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4026                  return;
4027              }
4028          }
4029  
4030          // evaluate message, getting back parameters
4031          // verify that request parameters match the method's signature
4032          if(! $this->verify_method($this->methodname,$this->methodparams)){
4033              // debug
4034              $this->debug('ERROR: request not verified against method signature');
4035              $this->result = 'fault: request failed validation against method signature';
4036              // return fault
4037              $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
4038              return;
4039          }
4040  
4041          // if there are parameters to pass
4042          $this->debug('in invoke_method, params:');
4043          $this->appendDebug($this->varDump($this->methodparams));
4044          $this->debug("in invoke_method, calling '$this->methodname'");
4045          if (!function_exists('call_user_func_array')) {
4046              if ($class == '') {
4047                  $this->debug('in invoke_method, calling function using eval()');
4048                  $funcCall = "\$this->methodreturn = $this->methodname(";
4049              } else {
4050                  if ($delim == '..') {
4051                      $this->debug('in invoke_method, calling class method using eval()');
4052                      $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
4053                  } else {
4054                      $this->debug('in invoke_method, calling instance method using eval()');
4055                      // generate unique instance name
4056                      $instname = "\$inst_".time();
4057                      $funcCall = $instname." = new ".$class."(); ";
4058                      $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
4059                  }
4060              }
4061              if ($this->methodparams) {
4062                  foreach ($this->methodparams as $param) {
4063                      if (is_array($param) || is_object($param)) {
4064                          $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4065                          return;
4066                      }
4067                      $funcCall .= "\"$param\",";
4068                  }
4069                  $funcCall = substr($funcCall, 0, -1);
4070              }
4071              $funcCall .= ');';
4072              $this->debug('in invoke_method, function call: '.$funcCall);
4073              @eval($funcCall);
4074          } else {
4075              if ($class == '') {
4076                  $this->debug('in invoke_method, calling function using call_user_func_array()');
4077                  $call_arg = "$this->methodname";    // straight assignment changes $this->methodname to lower case after call_user_func_array()
4078              } elseif ($delim == '..') {
4079                  $this->debug('in invoke_method, calling class method using call_user_func_array()');
4080                  $call_arg = array ($class, $method);
4081              } else {
4082                  $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4083                  $instance = new $class ();
4084                  $call_arg = array(&$instance, $method);
4085              }
4086              if (is_array($this->methodparams)) {
4087                  $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4088              } else {
4089                  $this->methodreturn = call_user_func_array($call_arg, array());
4090              }
4091          }
4092          $this->debug('in invoke_method, methodreturn:');
4093          $this->appendDebug($this->varDump($this->methodreturn));
4094          $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
4095      }
4096  
4097      /**
4098      * serializes the return value from a PHP function into a full SOAP Envelope
4099      *
4100      * The following fields are set by this function (when successful)
4101      *
4102      * responseSOAP
4103      *
4104      * This sets the fault field on error
4105      *
4106      * @access   private
4107      */
4108  	function serialize_return() {
4109          $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4110          // if fault
4111          if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
4112              $this->debug('got a fault object from method');
4113              $this->fault = $this->methodreturn;
4114              return;
4115          } elseif ($this->methodreturnisliteralxml) {
4116              $return_val = $this->methodreturn;
4117          // returned value(s)
4118          } else {
4119              $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
4120              $this->debug('serializing return value');
4121              if($this->wsdl){
4122                  if (sizeof($this->opData['output']['parts']) > 1) {
4123                      $this->debug('more than one output part, so use the method return unchanged');
4124                      $opParams = $this->methodreturn;
4125                  } elseif (sizeof($this->opData['output']['parts']) == 1) {
4126                      $this->debug('exactly one output part, so wrap the method return in a simple array');
4127                      // TODO: verify that it is not already wrapped!
4128                      //foreach ($this->opData['output']['parts'] as $name => $type) {
4129                      //    $this->debug('wrap in element named ' . $name);
4130                      //}
4131                      $opParams = array($this->methodreturn);
4132                  }
4133                  $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
4134                  $this->appendDebug($this->wsdl->getDebug());
4135                  $this->wsdl->clearDebug();
4136                  if($errstr = $this->wsdl->getError()){
4137                      $this->debug('got wsdl error: '.$errstr);
4138                      $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4139                      return;
4140                  }
4141              } else {
4142                  if (isset($this->methodreturn)) {
4143                      $return_val = $this->serialize_val($this->methodreturn, 'return');
4144                  } else {
4145                      $return_val = '';
4146                      $this->debug('in absence of WSDL, assume void return for backward compatibility');
4147                  }
4148              }
4149          }
4150          $this->debug('return value:');
4151          $this->appendDebug($this->varDump($return_val));
4152  
4153          $this->debug('serializing response');
4154          if ($this->wsdl) {
4155              $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4156              if ($this->opData['style'] == 'rpc') {
4157                  $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4158                  if ($this->opData['output']['use'] == 'literal') {
4159                      // 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
4160                      if ($this->methodURI) {
4161                          $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4162                      } else {
4163                          $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4164                      }
4165                  } else {
4166                      if ($this->methodURI) {
4167                          $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4168                      } else {
4169                          $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4170                      }
4171                  }
4172              } else {
4173                  $this->debug('style is not rpc for serialization: assume document');
4174                  $payload = $return_val;
4175              }
4176          } else {
4177              $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4178              $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4179          }
4180          $this->result = 'successful';
4181          if($this->wsdl){
4182              //if($this->debug_flag){
4183                  $this->appendDebug($this->wsdl->getDebug());
4184              //    }
4185              if (isset($this->opData['output']['encodingStyle'])) {
4186                  $encodingStyle = $this->opData['output']['encodingStyle'];
4187              } else {
4188                  $encodingStyle = '';
4189              }
4190              // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4191              $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
4192          } else {
4193              $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
4194          }
4195          $this->debug("Leaving serialize_return");
4196      }
4197  
4198      /**
4199      * sends an HTTP response
4200      *
4201      * The following fields are set by this function (when successful)
4202      *
4203      * outgoing_headers
4204      * response
4205      *
4206      * @access   private
4207      */
4208  	function send_response() {
4209          $this->debug('Enter send_response');
4210          if ($this->fault) {
4211              $payload = $this->fault->serialize();
4212              $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4213              $this->outgoing_headers[] = "Status: 500 Internal Server Error";
4214          } else {
4215              $payload = $this->responseSOAP;
4216              // Some combinations of PHP+Web server allow the Status
4217              // to come through as a header.  Since OK is the default
4218              // just do nothing.
4219              // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4220              // $this->outgoing_headers[] = "Status: 200 OK";
4221          }
4222          // add debug data if in debug mode
4223          if(isset($this->debug_flag) && $this->debug_flag){
4224              $payload .= $this->getDebugAsXMLComment();
4225          }
4226          $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4227          preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4228          $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
4229          // Let the Web server decide about this
4230          //$this->outgoing_headers[] = "Connection: Close\r\n";
4231          $payload = $this->getHTTPBody($payload);
4232          $type = $this->getHTTPContentType();
4233          $charset = $this->getHTTPContentTypeCharset();
4234          $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4235          //begin code to compress payload - by John
4236          // NOTE: there is no way to know whether the Web server will also compress
4237          // this data.
4238          if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {    
4239              if (strstr($this->headers['accept-encoding'], 'gzip')) {
4240                  if (function_exists('gzencode')) {
4241                      if (isset($this->debug_flag) && $this->debug_flag) {
4242                          $payload .= "<!-- Content being gzipped -->";
4243                      }
4244                      $this->outgoing_headers[] = "Content-Encoding: gzip";
4245                      $payload = gzencode($payload);
4246                  } else {
4247                      if (isset($this->debug_flag) && $this->debug_flag) {
4248                          $payload .= "<!-- Content will not be gzipped: no gzencode -->";
4249                      }
4250                  }
4251              } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
4252                  // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4253                  // instead of gzcompress output,
4254                  // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4255                  if (function_exists('gzdeflate')) {
4256                      if (isset($this->debug_flag) && $this->debug_flag) {
4257                          $payload .= "<!-- Content being deflated -->";
4258                      }
4259                      $this->outgoing_headers[] = "Content-Encoding: deflate";
4260                      $payload = gzdeflate($payload);
4261                  } else {
4262                      if (isset($this->debug_flag) && $this->debug_flag) {
4263                          $payload .= "<!-- Content will not be deflated: no gzcompress -->";
4264                      }
4265                  }
4266              }
4267          }
4268          //end code
4269          $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
4270          reset($this->outgoing_headers);
4271          foreach($this->outgoing_headers as $hdr){
4272              header($hdr, false);
4273          }
4274          print $payload;
4275          $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
4276      }
4277  
4278      /**
4279      * takes the value that was created by parsing the request
4280      * and compares to the method's signature, if available.
4281      *
4282      * @param    string    $operation    The operation to be invoked
4283      * @param    array    $request    The array of parameter values
4284      * @return    boolean    Whether the operation was found
4285      * @access   private
4286      */
4287  	function verify_method($operation,$request){
4288          if(isset($this->wsdl) && is_object($this->wsdl)){
4289              if($this->wsdl->getOperationData($operation)){
4290                  return true;
4291              }
4292          } elseif(isset($this->operations[$operation])){
4293              return true;
4294          }
4295          return false;
4296      }
4297  
4298      /**
4299      * processes SOAP message received from client
4300      *
4301      * @param    array    $headers    The HTTP headers
4302      * @param    string    $data        unprocessed request data from client
4303      * @return    mixed    value of the message, decoded into a PHP type
4304      * @access   private
4305      */
4306      function parseRequest($headers, $data) {
4307          $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
4308          $this->appendDebug($this->varDump($headers));
4309          if (!isset($headers['content-type'])) {
4310              $this->setError('Request not of type text/xml (no content-type header)');
4311              return false;
4312          }
4313          if (!strstr($headers['content-type'], 'text/xml')) {
4314              $this->setError('Request not of type text/xml');
4315              return false;
4316          }
4317          if (strpos($headers['content-type'], '=')) {
4318              $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
4319              $this->debug('Got response encoding: ' . $enc);
4320              if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
4321                  $this->xml_encoding = strtoupper($enc);
4322              } else {
4323                  $this->xml_encoding = 'US-ASCII';
4324              }
4325          } else {
4326              // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4327              $this->xml_encoding = 'ISO-8859-1';
4328          }
4329          $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4330          // parse response, get soap parser obj
4331          $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
4332          // parser debug
4333          $this->debug("parser debug: \n".$parser->getDebug());
4334          // if fault occurred during message parsing
4335          if($err = $parser->getError()){
4336              $this->result = 'fault: error in msg parsing: '.$err;
4337              $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
4338          // else successfully parsed request into soapval object
4339          } else {
4340              // get/set methodname
4341              $this->methodURI = $parser->root_struct_namespace;
4342              $this->methodname = $parser->root_struct_name;
4343              $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
4344              $this->debug('calling parser->get_soapbody()');
4345              $this->methodparams = $parser->get_soapbody();
4346              // get SOAP headers
4347              $this->requestHeaders = $parser->getHeaders();
4348              // get SOAP Header
4349              $this->requestHeader = $parser->get_soapheader();
4350              // add document for doclit support
4351              $this->document = $parser->document;
4352          }
4353       }
4354  
4355      /**
4356      * gets the HTTP body for the current response.
4357      *
4358      * @param string $soapmsg The SOAP payload
4359      * @return string The HTTP body, which includes the SOAP payload
4360      * @access private
4361      */
4362  	function getHTTPBody($soapmsg) {
4363          return $soapmsg;
4364      }
4365      
4366      /**
4367      * gets the HTTP content type for the current response.
4368      *
4369      * Note: getHTTPBody must be called before this.
4370      *
4371      * @return string the HTTP content type for the current response.
4372      * @access private
4373      */
4374  	function getHTTPContentType() {
4375          return 'text/xml';
4376      }
4377      
4378      /**
4379      * gets the HTTP content type charset for the current response.
4380      * returns false for non-text content types.
4381      *
4382      * Note: getHTTPBody must be called before this.
4383      *
4384      * @return string the HTTP content type charset for the current response.
4385      * @access private
4386      */
4387  	function getHTTPContentTypeCharset() {
4388          return $this->soap_defencoding;
4389      }
4390  
4391      /**
4392      * add a method to the dispatch map (this has been replaced by the register method)
4393      *
4394      * @param    string $methodname
4395      * @param    string $in array of input values
4396      * @param    string $out array of output values
4397      * @access   public
4398      * @deprecated
4399      */
4400  	function add_to_map($methodname,$in,$out){
4401              $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
4402      }
4403  
4404      /**
4405      * register a service function with the server
4406      *
4407      * @param    string $name the name of the PHP function, class.method or class..method
4408      * @param    array $in assoc array of input values: key = param name, value = param type
4409      * @param    array $out assoc array of output values: key = param name, value = param type
4410      * @param    mixed $namespace the element namespace for the method or false
4411      * @param    mixed $soapaction the soapaction for the method or false
4412      * @param    mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4413      * @param    mixed $use optional (encoded|literal) or false
4414      * @param    string $documentation optional Description to include in WSDL
4415      * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4416      * @access   public
4417      */
4418  	function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
4419          global $HTTP_SERVER_VARS;
4420  
4421          if($this->externalWSDLURL){
4422              die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
4423          }
4424          if (! $name) {
4425              die('You must specify a name when you register an operation');
4426          }
4427          if (!is_array($in)) {
4428              die('You must provide an array for operation inputs');
4429          }
4430          if (!is_array($out)) {
4431              die('You must provide an array for operation outputs');
4432          }
4433          if(false == $namespace) {
4434          }
4435          if(false == $soapaction) {
4436              if (isset($_SERVER)) {
4437                  $SERVER_NAME = $_SERVER['SERVER_NAME'];
4438                  $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4439                  $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4440              } elseif (isset($HTTP_SERVER_VARS)) {
4441                  $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4442                  $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4443                  $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4444              } else {
4445                  $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4446              }
4447              if ($HTTPS == '1' || $HTTPS == 'on') {
4448                  $SCHEME = 'https';
4449              } else {
4450                  $SCHEME = 'http';
4451              }
4452              $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
4453          }
4454          if(false == $style) {
4455              $style = "rpc";
4456          }
4457          if(false == $use) {
4458              $use = "encoded";
4459          }
4460          if ($use == 'encoded' && $encodingStyle == '') {
4461              $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4462          }
4463  
4464          $this->operations[$name] = array(
4465          'name' => $name,
4466          'in' => $in,
4467          'out' => $out,
4468          'namespace' => $namespace,
4469          'soapaction' => $soapaction,
4470          'style' => $style);
4471          if($this->wsdl){
4472              $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
4473          }
4474          return true;
4475      }
4476  
4477      /**
4478      * Specify a fault to be returned to the client.
4479      * This also acts as a flag to the server that a fault has occured.
4480      *
4481      * @param    string $faultcode
4482      * @param    string $faultstring
4483      * @param    string $faultactor
4484      * @param    string $faultdetail
4485      * @access   public
4486      */
4487  	function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
4488          if ($faultdetail == '' && $this->debug_flag) {
4489              $faultdetail = $this->getDebug();
4490          }
4491          $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
4492          $this->fault->soap_defencoding = $this->soap_defencoding;
4493      }
4494  
4495      /**
4496      * Sets up wsdl object.
4497      * Acts as a flag to enable internal WSDL generation
4498      *
4499      * @param string $serviceName, name of the service
4500      * @param mixed $namespace optional 'tns' service namespace or false
4501      * @param mixed $endpoint optional URL of service endpoint or false
4502      * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4503      * @param string $transport optional SOAP transport
4504      * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4505      */
4506      function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4507      {
4508          global $HTTP_SERVER_VARS;
4509  
4510          if (isset($_SERVER)) {
4511              $SERVER_NAME = $_SERVER['SERVER_NAME'];
4512              $SERVER_PORT = $_SERVER['SERVER_PORT'];
4513              $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4514              $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4515          } elseif (isset($HTTP_SERVER_VARS)) {
4516              $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4517              $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4518              $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4519              $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4520          } else {
4521              $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4522          }
4523          // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4524          $colon = strpos($SERVER_NAME,":");
4525          if ($colon) {
4526              $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4527          }
4528          if ($SERVER_PORT == 80) {
4529              $SERVER_PORT = '';
4530          } else {
4531              $SERVER_PORT = ':' . $SERVER_PORT;
4532          }
4533          if(false == $namespace) {
4534              $namespace = "http://$SERVER_NAME/soap/$serviceName";
4535          }
4536          
4537          if(false == $endpoint) {
4538              if ($HTTPS == '1' || $HTTPS == 'on') {
4539                  $SCHEME = 'https';
4540              } else {
4541                  $SCHEME = 'http';
4542              }
4543              $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4544          }
4545          
4546          if(false == $schemaTargetNamespace) {
4547              $schemaTargetNamespace = $namespace;
4548          }
4549          
4550          $this->wsdl = new wsdl;
4551          $this->wsdl->serviceName = $serviceName;
4552          $this->wsdl->endpoint = $endpoint;
4553          $this->wsdl->namespaces['tns'] = $namespace;
4554          $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4555          $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4556          if ($schemaTargetNamespace != $namespace) {
4557              $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4558          }
4559          $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
4560          if ($style == 'document') {
4561              $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4562          }
4563          $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4564          $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4565          $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4566          $this->wsdl->bindings[$serviceName.'Binding'] = array(
4567              'name'=>$serviceName.'Binding',
4568              'style'=>$style,
4569              'transport'=>$transport,
4570              'portType'=>$serviceName.'PortType');
4571          $this->wsdl->ports[$serviceName.'Port'] = array(
4572              'binding'=>$serviceName.'Binding',
4573              'location'=>$endpoint,
4574              'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4575      }
4576  }
4577  
4578  /**
4579   * Backward compatibility
4580   */
4581  class soap_server extends nusoap_server {
4582  }
4583  
4584  ?><?php
4585  
4586  
4587  
4588  /**
4589  * parses a WSDL file, allows access to it's data, other utility methods.
4590  * also builds WSDL structures programmatically.
4591  * 
4592  * @author   Dietrich Ayala <dietrich@ganx4.com>
4593  * @author   Scott Nichol <snichol@users.sourceforge.net>
4594  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
4595  * @access public 
4596  */
4597  class wsdl extends nusoap_base {
4598      // URL or filename of the root of this WSDL
4599      var $wsdl; 
4600      // define internal arrays of bindings, ports, operations, messages, etc.
4601      var $schemas = array();
4602      var $currentSchema;
4603      var $message = array();
4604      var $complexTypes = array();
4605      var $messages = array();
4606      var $currentMessage;
4607      var $currentOperation;
4608      var $portTypes = array();
4609      var $currentPortType;
4610      var $bindings = array();
4611      var $currentBinding;
4612      var $ports = array();
4613      var $currentPort;
4614      var $opData = array();
4615      var $status = '';
4616      var $documentation = false;
4617      var $endpoint = ''; 
4618      // array of wsdl docs to import
4619      var $import = array(); 
4620      // parser vars
4621      var $parser;
4622      var $position = 0;
4623      var $depth = 0;
4624      var $depth_array = array();
4625      // for getting wsdl
4626      var $proxyhost = '';
4627      var $proxyport = '';
4628      var $proxyusername = '';
4629      var $proxypassword = '';
4630      var $timeout = 0;
4631      var $response_timeout = 30;
4632      var $curl_options = array();    // User-specified cURL options
4633      var $use_curl = false;            // whether to always try to use cURL
4634      // for HTTP authentication
4635      var $username = '';                // Username for HTTP authentication
4636      var $password = '';                // Password for HTTP authentication
4637      var $authtype = '';                // Type of HTTP authentication
4638      var $certRequest = array();        // Certificate for HTTP SSL authentication
4639  
4640      /**
4641       * constructor
4642       * 
4643       * @param string $wsdl WSDL document URL
4644       * @param string $proxyhost
4645       * @param string $proxyport
4646       * @param string $proxyusername
4647       * @param string $proxypassword
4648       * @param integer $timeout set the connection timeout
4649       * @param integer $response_timeout set the response timeout
4650       * @param array $curl_options user-specified cURL options
4651       * @param boolean $use_curl try to use cURL
4652       * @access public 
4653       */
4654      function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
4655          parent::nusoap_base();
4656          $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4657          $this->proxyhost = $proxyhost;
4658          $this->proxyport = $proxyport;
4659          $this->proxyusername = $proxyusername;
4660          $this->proxypassword = $proxypassword;
4661          $this->timeout = $timeout;
4662          $this->response_timeout = $response_timeout;
4663          if (is_array($curl_options))
4664              $this->curl_options = $curl_options;
4665          $this->use_curl = $use_curl;
4666          $this->fetchWSDL($wsdl);
4667      }
4668  
4669      /**
4670       * fetches the WSDL document and parses it
4671       *
4672       * @access public
4673       */
4674  	function fetchWSDL($wsdl) {
4675          $this->debug("parse and process WSDL path=$wsdl");
4676          $this->wsdl = $wsdl;
4677          // parse wsdl file
4678          if ($this->wsdl != "") {
4679              $this->parseWSDL($this->wsdl);
4680          }
4681          // imports
4682          // TODO: handle imports more properly, grabbing them in-line and nesting them
4683          $imported_urls = array();
4684          $imported = 1;
4685          while ($imported > 0) {
4686              $imported = 0;
4687              // Schema imports
4688              foreach ($this->schemas as $ns => $list) {
4689                  foreach ($list as $xs) {
4690                      $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4691                      foreach ($xs->imports as $ns2 => $list2) {
4692                          for ($ii = 0; $ii < count($list2); $ii++) {
4693                              if (! $list2[$ii]['loaded']) {
4694                                  $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4695                                  $url = $list2[$ii]['location'];
4696                                  if ($url != '') {
4697                                      $urlparts = parse_url($url);
4698                                      if (!isset($urlparts['host'])) {
4699                                          $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4700                                                  substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4701                                      }
4702                                      if (! in_array($url, $imported_urls)) {
4703                                          $this->parseWSDL($url);
4704                                          $imported++;
4705                                          $imported_urls[] = $url;
4706                                      }
4707                                  } else {
4708                                      $this->debug("Unexpected scenario: empty URL for unloaded import");
4709                                  }
4710                              }
4711                          }
4712                      } 
4713                  }
4714              }
4715              // WSDL imports
4716              $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4717              foreach ($this->import as $ns => $list) {
4718                  for ($ii = 0; $ii < count($list); $ii++) {
4719                      if (! $list[$ii]['loaded']) {
4720                          $this->import[$ns][$ii]['loaded'] = true;
4721                          $url = $list[$ii]['location'];
4722                          if ($url != '') {
4723                              $urlparts = parse_url($url);
4724                              if (!isset($urlparts['host'])) {
4725                                  $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4726                                          substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4727                              }
4728                              if (! in_array($url, $imported_urls)) {
4729                                  $this->parseWSDL($url);
4730                                  $imported++;
4731                                  $imported_urls[] = $url;
4732                              }
4733                          } else {
4734                              $this->debug("Unexpected scenario: empty URL for unloaded import");
4735                          }
4736                      }
4737                  }
4738              } 
4739          }
4740          // add new data to operation data
4741          foreach($this->bindings as $binding => $bindingData) {
4742              if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4743                  foreach($bindingData['operations'] as $operation => $data) {
4744                      $this->debug('post-parse data gathering for ' . $operation);
4745                      $this->bindings[$binding]['operations'][$operation]['input'] = 
4746                          isset($this->bindings[$binding]['operations'][$operation]['input']) ? 
4747                          array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4748                          $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4749                      $this->bindings[$binding]['operations'][$operation]['output'] = 
4750                          isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4751                          array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4752                          $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4753                      if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4754                          $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4755                      }
4756                      if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4757                             $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4758                      }
4759                      // Set operation style if necessary, but do not override one already provided
4760                      if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4761                          $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4762                      }
4763                      $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4764                      $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4765                      $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4766                  } 
4767              } 
4768          }
4769      }
4770  
4771      /**
4772       * parses the wsdl document
4773       * 
4774       * @param string $wsdl path or URL
4775       * @access private 
4776       */
4777      function parseWSDL($wsdl = '') {
4778          $this->debug("parse WSDL at path=$wsdl");
4779  
4780          if ($wsdl == '') {
4781              $this->debug('no wsdl passed to parseWSDL()!!');
4782              $this->setError('no wsdl passed to parseWSDL()!!');
4783              return false;
4784          }
4785          
4786          // parse $wsdl for url format
4787          $wsdl_props = parse_url($wsdl);
4788  
4789          if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4790              $this->debug('getting WSDL http(s) URL ' . $wsdl);
4791              // get wsdl
4792              $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4793              $tr->request_method = 'GET';
4794              $tr->useSOAPAction = false;
4795              if($this->proxyhost && $this->proxyport){
4796                  $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4797              }
4798              if ($this->authtype != '') {
4799                  $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
4800              }
4801              $tr->setEncoding('gzip, deflate');
4802              $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4803              //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4804              //$this->debug("WSDL response\n" . $tr->incoming_payload);
4805              $this->appendDebug($tr->getDebug());
4806              // catch errors
4807              if($err = $tr->getError() ){
4808                  $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
4809                  $this->debug($errstr);
4810                  $this->setError($errstr);
4811                  unset($tr);
4812                  return false;
4813              }
4814              unset($tr);
4815              $this->debug("got WSDL URL");
4816          } else {
4817              // $wsdl is not http(s), so treat it as a file URL or plain file path
4818              if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4819                  $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4820              } else {
4821                  $path = $wsdl;
4822              }
4823              $this->debug('getting WSDL file ' . $path);
4824              if ($fp = @fopen($path, 'r')) {
4825                  $wsdl_string = '';
4826                  while ($data = fread($fp, 32768)) {
4827                      $wsdl_string .= $data;
4828                  } 
4829                  fclose($fp);
4830              } else {
4831                  $errstr = "Bad path to WSDL file $path";
4832                  $this->debug($errstr);
4833                  $this->setError($errstr);
4834                  return false;
4835              } 
4836          }
4837          $this->debug('Parse WSDL');
4838          // end new code added
4839          // Create an XML parser.
4840          $this->parser = xml_parser_create(); 
4841          // Set the options for parsing the XML data.
4842          // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4843          xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
4844          // Set the object for the parser.
4845          xml_set_object($this->parser, $this); 
4846          // Set the element handlers for the parser.
4847          xml_set_element_handler($this->parser, 'start_element', 'end_element');
4848          xml_set_character_data_handler($this->parser, 'character_data');
4849          // Parse the XML file.
4850          if (!xml_parse($this->parser, $wsdl_string, true)) {
4851              // Display an error message.
4852              $errstr = sprintf(
4853                  'XML error parsing WSDL from %s on line %d: %s',
4854                  $wsdl,
4855                  xml_get_current_line_number($this->parser),
4856                  xml_error_string(xml_get_error_code($this->parser))
4857                  );
4858              $this->debug($errstr);
4859              $this->debug("XML payload:\n" . $wsdl_string);
4860              $this->setError($errstr);
4861              return false;
4862          } 
4863          // free the parser
4864          xml_parser_free($this->parser);
4865          $this->debug('Parsing WSDL done');
4866          // catch wsdl parse errors
4867          if($this->getError()){
4868              return false;
4869          }
4870          return true;
4871      } 
4872  
4873      /**
4874       * start-element handler
4875       * 
4876       * @param string $parser XML parser object
4877       * @param string $name element name
4878       * @param string $attrs associative array of attributes
4879       * @access private 
4880       */
4881      function start_element($parser, $name, $attrs)
4882      {
4883          if ($this->status == 'schema') {
4884              $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4885              $this->appendDebug($this->currentSchema->getDebug());
4886              $this->currentSchema->clearDebug();
4887          } elseif (preg_match('/schema$/', $name)) {
4888              $this->debug('Parsing WSDL schema');
4889              // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4890              $this->status = 'schema';
4891              $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
4892              $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4893              $this->appendDebug($this->currentSchema->getDebug());
4894              $this->currentSchema->clearDebug();
4895          } else {
4896              // position in the total number of elements, starting from 0
4897              $pos = $this->position++;
4898              $depth = $this->depth++; 
4899              // set self as current value for this depth
4900              $this->depth_array[$depth] = $pos;
4901              $this->message[$pos] = array('cdata' => ''); 
4902              // process attributes
4903              if (count($attrs) > 0) {
4904                  // register namespace declarations
4905                  foreach($attrs as $k => $v) {
4906                      if (preg_match('/^xmlns/',$k)) {
4907                          if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4908                              $this->namespaces[$ns_prefix] = $v;
4909                          } else {
4910                              $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4911                          } 
4912                          if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4913                              $this->XMLSchemaVersion = $v;
4914                              $this->namespaces['xsi'] = $v . '-instance';
4915                          } 
4916                      }
4917                  }
4918                  // expand each attribute prefix to its namespace
4919                  foreach($attrs as $k => $v) {
4920                      $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4921                      if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4922                          $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4923                      } 
4924                      $eAttrs[$k] = $v;
4925                  } 
4926                  $attrs = $eAttrs;
4927              } else {
4928                  $attrs = array();
4929              } 
4930              // get element prefix, namespace and name
4931              if (preg_match('/:/', $name)) {
4932                  // get ns prefix
4933                  $prefix = substr($name, 0, strpos($name, ':')); 
4934                  // get ns
4935                  $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; 
4936                  // get unqualified name
4937                  $name = substr(strstr($name, ':'), 1);
4938              } 
4939              // process attributes, expanding any prefixes to namespaces
4940              // find status, register data
4941              switch ($this->status) {
4942                  case 'message':
4943                      if ($name == 'part') {
4944                          if (isset($attrs['type'])) {
4945                              $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4946                              $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4947                          } 
4948                          if (isset($attrs['element'])) {
4949                              $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4950                              $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4951                          } 
4952                      } 
4953                      break;
4954                  case 'portType':
4955                      switch ($name) {
4956                          case 'operation':
4957                              $this->currentPortOperation = $attrs['name'];
4958                              $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4959                              if (isset($attrs['parameterOrder'])) {
4960                                  $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4961                              } 
4962                              break;
4963                          case 'documentation':
4964                              $this->documentation = true;
4965                              break; 
4966                          // merge input/output data
4967                          default:
4968                              $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4969                              $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4970                              break;
4971                      } 
4972                      break;
4973                  case 'binding':
4974                      switch ($name) {
4975                          case 'binding': 
4976                              // get ns prefix
4977                              if (isset($attrs['style'])) {
4978                              $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4979                              } 
4980                              $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4981                              break;
4982                          case 'header':
4983                              $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4984                              break;
4985                          case 'operation':
4986                              if (isset($attrs['soapAction'])) {
4987                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4988                              } 
4989                              if (isset($attrs['style'])) {
4990                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4991                              } 
4992                              if (isset($attrs['name'])) {
4993                                  $this->currentOperation = $attrs['name'];
4994                                  $this->debug("current binding operation: $this->currentOperation");
4995                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4996                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4997                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4998                              } 
4999                              break;
5000                          case 'input':
5001                              $this->opStatus = 'input';
5002                              break;
5003                          case 'output':
5004                              $this->opStatus = 'output';
5005                              break;
5006                          case 'body':
5007                              if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5008                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5009                              } else {
5010                                  $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5011                              } 
5012                              break;
5013                      } 
5014                      break;
5015                  case 'service':
5016                      switch ($name) {
5017                          case 'port':
5018                              $this->currentPort = $attrs['name'];
5019                              $this->debug('current port: ' . $this->currentPort);
5020                              $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5021                      
5022                              break;
5023                          case 'address':
5024                              $this->ports[$this->currentPort]['location'] = $attrs['location'];
5025                              $this->ports[$this->currentPort]['bindingType'] = $namespace;
5026                              $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
5027                              $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
5028                              break;
5029                      } 
5030                      break;
5031              } 
5032          // set status
5033          switch ($name) {
5034              case 'import':
5035                  if (isset($attrs['location'])) {
5036                      $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
5037                      $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
5038                  } else {
5039                      $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
5040                      if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
5041                          $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
5042                      }
5043                      $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
5044                  }
5045                  break;
5046              //wait for schema
5047              //case 'types':
5048              //    $this->status = 'schema';
5049              //    break;
5050              case 'message':
5051                  $this->status = 'message';
5052                  $this->messages[$attrs['name']] = array();
5053                  $this->currentMessage = $attrs['name'];
5054                  break;
5055              case 'portType':
5056                  $this->status = 'portType';
5057                  $this->portTypes[$attrs['name']] = array();
5058                  $this->currentPortType = $attrs['name'];
5059                  break;
5060              case "binding":
5061                  if (isset($attrs['name'])) {
5062                  // get binding name
5063                      if (strpos($attrs['name'], ':')) {
5064                          $this->currentBinding = $this->getLocalPart($attrs['name']);
5065                      } else {
5066                          $this->currentBinding = $attrs['name'];
5067                      } 
5068                      $this->status = 'binding';
5069                      $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5070                      $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5071                  } 
5072                  break;
5073              case 'service':
5074                  $this->serviceName = $attrs['name'];
5075                  $this->status = 'service';
5076                  $this->debug('current service: ' . $this->serviceName);
5077                  break;
5078              case 'definitions':
5079                  foreach ($attrs as $name => $value) {
5080                      $this->wsdl_info[$name] = $value;
5081                  } 
5082                  break;
5083              } 
5084          } 
5085      } 
5086  
5087      /**
5088      * end-element handler
5089      * 
5090      * @param string $parser XML parser object
5091      * @param string $name element name
5092      * @access private 
5093      */
5094  	function end_element($parser, $name){ 
5095          // unset schema status
5096          if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
5097              $this->status = "";
5098              $this->appendDebug($this->currentSchema->getDebug());
5099              $this->currentSchema->clearDebug();
5100              $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5101              $this->debug('Parsing WSDL schema done');
5102          } 
5103          if ($this->status == 'schema') {
5104              $this->currentSchema->schemaEndElement($parser, $name);
5105          } else {
5106              // bring depth down a notch
5107              $this->depth--;
5108          } 
5109          // end documentation
5110          if ($this->documentation) {
5111              //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5112              //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5113              $this->documentation = false;
5114          } 
5115      } 
5116  
5117      /**
5118       * element content handler
5119       * 
5120       * @param string $parser XML parser object
5121       * @param string $data element content
5122       * @access private 
5123       */
5124  	function character_data($parser, $data)
5125      {
5126          $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5127          if (isset($this->message[$pos]['cdata'])) {
5128              $this->message[$pos]['cdata'] .= $data;
5129          } 
5130          if ($this->documentation) {
5131              $this->documentation .= $data;
5132          } 
5133      } 
5134  
5135      /**
5136      * if authenticating, set user credentials here
5137      *
5138      * @param    string $username
5139      * @param    string $password
5140      * @param    string $authtype (basic|digest|certificate|ntlm)
5141      * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5142      * @access   public
5143      */
5144  	function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
5145          $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5146          $this->appendDebug($this->varDump($certRequest));
5147          $this->username = $username;
5148          $this->password = $password;
5149          $this->authtype = $authtype;
5150          $this->certRequest = $certRequest;
5151      }
5152      
5153  	function getBindingData($binding)
5154      {
5155          if (is_array($this->bindings[$binding])) {
5156              return $this->bindings[$binding];
5157          } 
5158      }
5159      
5160      /**
5161       * returns an assoc array of operation names => operation data
5162       * 
5163       * @param string $portName WSDL port name
5164       * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5165       * @return array 
5166       * @access public 
5167       */
5168  	function getOperations($portName = '', $bindingType = 'soap') {
5169          $ops = array();
5170          if ($bindingType == 'soap') {
5171              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5172          } elseif ($bindingType == 'soap12') {
5173              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5174          } else {
5175              $this->debug("getOperations bindingType $bindingType may not be supported");
5176          }
5177          $this->debug("getOperations for port '$portName' bindingType $bindingType");
5178          // loop thru ports
5179          foreach($this->ports as $port => $portData) {
5180              $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5181              if ($portName == '' || $port == $portName) {
5182                  // binding type of port matches parameter
5183                  if ($portData['bindingType'] == $bindingType) {
5184                      $this->debug("getOperations found port $port bindingType $bindingType");
5185                      //$this->debug("port data: " . $this->varDump($portData));
5186                      //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5187                      // merge bindings
5188                      if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
5189                          $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
5190                      }
5191                  }
5192              }
5193          }
5194          if (count($ops) == 0) {
5195              $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5196          }
5197          return $ops;
5198      } 
5199      
5200      /**
5201       * returns an associative array of data necessary for calling an operation
5202       * 
5203       * @param string $operation name of operation
5204       * @param string $bindingType type of binding eg: soap, soap12
5205       * @return array 
5206       * @access public 
5207       */
5208  	function getOperationData($operation, $bindingType = 'soap')
5209      {
5210          if ($bindingType == 'soap') {
5211              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5212          } elseif ($bindingType == 'soap12') {
5213              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5214          }
5215          // loop thru ports
5216          foreach($this->ports as $port => $portData) {
5217              // binding type of port matches parameter
5218              if ($portData['bindingType'] == $bindingType) {
5219                  // get binding
5220                  //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5221                  foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
5222                      // note that we could/should also check the namespace here
5223                      if ($operation == $bOperation) {
5224                          $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
5225                          return $opData;
5226                      } 
5227                  } 
5228              }
5229          } 
5230      }
5231      
5232      /**
5233       * returns an associative array of data necessary for calling an operation
5234       * 
5235       * @param string $soapAction soapAction for operation
5236       * @param string $bindingType type of binding eg: soap, soap12
5237       * @return array 
5238       * @access public 
5239       */
5240  	function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
5241          if ($bindingType == 'soap') {
5242              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5243          } elseif ($bindingType == 'soap12') {
5244              $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5245          }
5246          // loop thru ports
5247          foreach($this->ports as $port => $portData) {
5248              // binding type of port matches parameter
5249              if ($portData['bindingType'] == $bindingType) {
5250                  // loop through operations for the binding
5251                  foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5252                      if ($opData['soapAction'] == $soapAction) {
5253                          return $opData;
5254                      } 
5255                  } 
5256              }
5257          } 
5258      }
5259      
5260      /**
5261      * returns an array of information about a given type
5262      * returns false if no type exists by the given name
5263      *
5264      *     typeDef = array(
5265      *     'elements' => array(), // refs to elements array
5266      *    'restrictionBase' => '',
5267      *    'phpType' => '',
5268      *    'order' => '(sequence|all)',
5269      *    'attrs' => array() // refs to attributes array
5270      *    )
5271      *
5272      * @param string $type the type
5273      * @param string $ns namespace (not prefix) of the type
5274      * @return mixed
5275      * @access public
5276      * @see nusoap_xmlschema
5277      */
5278  	function getTypeDef($type, $ns) {
5279          $this->debug("in getTypeDef: type=$type, ns=$ns");
5280          if ((! $ns) && isset($this->namespaces['tns'])) {
5281              $ns = $this->namespaces['tns'];
5282              $this->debug("in getTypeDef: type namespace forced to $ns");
5283          }
5284          if (!isset($this->schemas[$ns])) {
5285              foreach ($this->schemas as $ns0 => $schema0) {
5286                  if (strcasecmp($ns, $ns0) == 0) {
5287                      $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5288                      $ns = $ns0;
5289                      break;
5290                  }
5291              }
5292          }
5293          if (isset($this->schemas[$ns])) {
5294              $this->debug("in getTypeDef: have schema for namespace $ns");
5295              for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
5296                  $xs = &$this->schemas[$ns][$i];
5297                  $t = $xs->getTypeDef($type);
5298                  $this->appendDebug($xs->getDebug());
5299                  $xs->clearDebug();
5300                  if ($t) {
5301                      $this->debug("in getTypeDef: found type $type");
5302                      if (!isset($t['phpType'])) {
5303                          // get info for type to tack onto the element
5304                          $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5305                          $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5306                          $etype = $this->getTypeDef($uqType, $ns);
5307                          if ($etype) {
5308                              $this->debug("found type for [element] $type:");
5309                              $this->debug($this->varDump($etype));
5310                              if (isset($etype['phpType'])) {
5311                                  $t['phpType'] = $etype['phpType'];
5312                              }
5313                              if (isset($etype['elements'])) {
5314                                  $t['elements'] = $etype['elements'];
5315                              }
5316                              if (isset($etype['attrs'])) {
5317                                  $t['attrs'] = $etype['attrs'];
5318                              }
5319                          } else {
5320                              $this->debug("did not find type for [element] $type");
5321                          }
5322                      }
5323                      return $t;
5324                  }
5325              }
5326              $this->debug("in getTypeDef: did not find type $type");
5327          } else {
5328              $this->debug("in getTypeDef: do not have schema for namespace $ns");
5329          }
5330          return false;
5331      }
5332  
5333      /**
5334      * prints html description of services
5335      *
5336      * @access private
5337      */
5338      function webDescription(){
5339          global $HTTP_SERVER_VARS;
5340  
5341          if (isset($_SERVER)) {
5342              $PHP_SELF = $_SERVER['PHP_SELF'];
5343          } elseif (isset($HTTP_SERVER_VARS)) {
5344              $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5345          } else {
5346              $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
5347          }
5348  
5349          $b = '
5350          <html><head><title>NuSOAP: '.$this->serviceName.'</title>
5351          <style type="text/css">
5352              body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5353              p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5354              pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5355              ul      { margin-top: 10px; margin-left: 20px; }
5356              li      { list-style-type: none; margin-top: 10px; color: #000000; }
5357              .content{
5358              margin-left: 0px; padding-bottom: 2em; }
5359              .nav {
5360              padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5361              margin-top: 10px; margin-left: 0px; color: #000000;
5362              background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5363              .title {
5364              font-family: arial; font-size: 26px; color: #ffffff;
5365              background-color: #999999; width: 100%;
5366              margin-left: 0px; margin-right: 0px;
5367              padding-top: 10px; padding-bottom: 10px;}
5368              .hidden {
5369              position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5370              font-family: arial; overflow: hidden; width: 600;
5371              padding: 20px; font-size: 10px; background-color: #999999;
5372              layer-background-color:#FFFFFF; }
5373              a,a:active  { color: charcoal; font-weight: bold; }
5374              a:visited   { color: #666666; font-weight: bold; }
5375              a:hover     { color: cc3300; font-weight: bold; }
5376          </style>
5377          <script language="JavaScript" type="text/javascript">
5378          <!--
5379          // POP-UP CAPTIONS...
5380  		function lib_bwcheck(){ //Browsercheck (needed)
5381              this.ver=navigator.appVersion
5382              this.agent=navigator.userAgent
5383              this.dom=document.getElementById?1:0
5384              this.opera5=this.agent.indexOf("Opera 5")>-1
5385              this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5386              this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5387              this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5388              this.ie=this.ie4||this.ie5||this.ie6
5389              this.mac=this.agent.indexOf("Mac")>-1
5390              this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5391              this.ns4=(document.layers && !this.dom)?1:0;
5392              this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5393              return this
5394          }
5395          var bw = new lib_bwcheck()
5396          //Makes crossbrowser object.
5397  		function makeObj(obj){
5398              this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5399              if(!this.evnt) return false
5400              this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5401              this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5402              this.writeIt=b_writeIt;
5403              return this
5404          }
5405          // A unit of measure that will be added when setting the position of a layer.
5406          //var px = bw.ns4||window.opera?"":"px";
5407  		function b_writeIt(text){
5408              if (bw.ns4){this.wref.write(text);this.wref.close()}
5409              else this.wref.innerHTML = text
5410          }
5411          //Shows the messages
5412          var oDesc;
5413  		function popup(divid){
5414              if(oDesc = new makeObj(divid)){
5415              oDesc.css.visibility = "visible"
5416              }
5417          }
5418  		function popout(){ // Hides message
5419              if(oDesc) oDesc.css.visibility = "hidden"
5420          }
5421          //-->
5422          </script>
5423          </head>
5424          <body>
5425          <div class=content>
5426              <br><br>
5427              <div class=title>'.htmlentities($this->serviceName).'</div>
5428              <div class=nav>
5429                  <p>View the <a href="'.htmlentities($PHP_SELF).'?wsdl">WSDL</a> for the service.
5430                  Click on an operation name to view it&apos;s details.</p>
5431                  <ul>';
5432                  foreach($this->getOperations() as $op => $data){
5433                      $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5434                      // create hidden div
5435                      $b .= "<div id='$op' class='hidden'>
5436                      <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5437                      foreach($data as $donnie => $marie){ // loop through opdata
5438                          if($donnie == 'input' || $donnie == 'output'){ // show input/output data
5439                              $b .= "<font color='white'>".htmlentities(ucfirst($donnie)).':</font><br>';
5440                              foreach($marie as $captain => $tenille){ // loop through data
5441                                  if($captain == 'parts'){ // loop thru parts
5442                                      $b .= "&nbsp;&nbsp;".htmlentities($captain).":<br>";
5443                                      //if(is_array($tenille)){
5444                                          foreach($tenille as $joanie => $chachi){
5445                                              $b .= "&nbsp;&nbsp;&nbsp;&nbsp;".htmlentities($joanie).": ".htmlentities($chachi)."<br>";
5446                                          }
5447                                      //}
5448                                  } else {
5449                                      $b .= "&nbsp;&nbsp;".htmlentities($captain).": ".htmlentities($tenille)."<br>";
5450                                  }
5451                              }
5452                          } else {
5453                              $b .= "<font color='white'>".htmlentities(ucfirst($donnie)).":</font> ".htmlentities($marie)."<br>";
5454                          }
5455                      }
5456                      $b .= '</div>';
5457                  }
5458                  $b .= '
5459                  <ul>
5460              </div>
5461          </div></body></html>';
5462          return $b;
5463      }
5464  
5465      /**
5466      * serialize the parsed wsdl
5467      *
5468      * @param mixed $debug whether to put debug=1 in endpoint URL
5469      * @return string serialization of WSDL
5470      * @access public 
5471      */
5472  	function serialize($debug = 0)
5473      {
5474          $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5475          $xml .= "\n<definitions";
5476          foreach($this->namespaces as $k => $v) {
5477              $xml .= " xmlns:$k=\"$v\"";
5478          } 
5479          // 10.9.02 - add poulter fix for wsdl and tns declarations
5480          if (isset($this->namespaces['wsdl'])) {
5481              $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
5482          } 
5483          if (isset($this->namespaces['tns'])) {
5484              $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
5485          } 
5486          $xml .= '>'; 
5487          // imports
5488          if (sizeof($this->import) > 0) {
5489              foreach($this->import as $ns => $list) {
5490                  foreach ($list as $ii) {
5491                      if ($ii['location'] != '') {
5492                          $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5493                      } else {
5494                          $xml .= '<import namespace="' . $ns . '" />';
5495                      }
5496                  }
5497              } 
5498          } 
5499          // types
5500          if (count($this->schemas)>=1) {
5501              $xml .= "\n<types>\n";
5502              foreach ($this->schemas as $ns => $list) {
5503                  foreach ($list as $xs) {
5504                      $xml .= $xs->serializeSchema();
5505                  }
5506              }
5507              $xml .= '</types>';
5508          } 
5509          // messages
5510          if (count($this->messages) >= 1) {
5511              foreach($this->messages as $msgName => $msgParts) {
5512                  $xml .= "\n<message name=\"" . $msgName . '">';
5513                  if(is_array($msgParts)){
5514                      foreach($msgParts as $partName => $partType) {
5515                          // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5516                          if (strpos($partType, ':')) {
5517                              $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
5518                          } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5519                              // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5520                              $typePrefix = 'xsd';
5521                          } else {
5522                              foreach($this->typemap as $ns => $types) {
5523                                  if (isset($types[$partType])) {
5524                                      $typePrefix = $this->getPrefixFromNamespace($ns);
5525                                  } 
5526                              } 
5527                              if (!isset($typePrefix)) {
5528                                  die("$partType has no namespace!");
5529                              } 
5530                          }
5531                          $ns = $this->getNamespaceFromPrefix($typePrefix);
5532                          $localPart = $this->getLocalPart($partType);
5533                          $typeDef = $this->getTypeDef($localPart, $ns);
5534                          if ($typeDef['typeClass'] == 'element') {
5535                              $elementortype = 'element';
5536                              if (substr($localPart, -1) == '^') {
5537                                  $localPart = substr($localPart, 0, -1);
5538                              }
5539                          } else {
5540                              $elementortype = 'type';
5541                          }
5542                          $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5543                      }
5544                  }
5545                  $xml .= '</message>';
5546              } 
5547          } 
5548          // bindings & porttypes
5549          if (count($this->bindings) >= 1) {
5550              $binding_xml = '';
5551              $portType_xml = '';
5552              foreach($this->bindings as $bindingName => $attrs) {
5553                  $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5554                  $binding_xml .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5555                  $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5556                  foreach($attrs['operations'] as $opName => $opParts) {
5557                      $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
5558                      $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
5559                      if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
5560                          $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5561                      } else {
5562                          $enc_style = '';
5563                      }
5564                      $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5565                      if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
5566                          $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5567                      } else {
5568                          $enc_style = '';
5569                      }
5570                      $binding_xml .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5571                      $binding_xml .= "\n" . '  </operation>';
5572                      $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
5573                      if (isset($opParts['parameterOrder'])) {
5574                          $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5575                      } 
5576                      $portType_xml .= '>';
5577                      if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
5578                          $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5579                      }
5580                      $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '"/>';
5581                      $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '"/>';
5582                      $portType_xml .= "\n" . '  </operation>';
5583                  } 
5584                  $portType_xml .= "\n" . '</portType>';
5585                  $binding_xml .= "\n" . '</binding>';
5586              } 
5587              $xml .= $portType_xml . $binding_xml;
5588          } 
5589          // services
5590          $xml .= "\n<service name=\"" . $this->serviceName . '">';
5591          if (count($this->ports) >= 1) {
5592              foreach($this->ports as $pName => $attrs) {
5593                  $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5594                  $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5595                  $xml .= "\n" . '  </port>';
5596              } 
5597          } 
5598          $xml .= "\n" . '</service>';
5599          return $xml . "\n</definitions>";
5600      } 
5601  
5602      /**
5603       * determine whether a set of parameters are unwrapped
5604       * when they are expect to be wrapped, Microsoft-style.
5605       *
5606       * @param string $type the type (element name) of the wrapper
5607       * @param array $parameters the parameter values for the SOAP call
5608       * @return boolean whether they parameters are unwrapped (and should be wrapped)
5609       * @access private
5610       */
5611  	function parametersMatchWrapped($type, &$parameters) {
5612          $this->debug("in parametersMatchWrapped type=$type, parameters=");
5613          $this->appendDebug($this->varDump($parameters));
5614  
5615          // split type into namespace:unqualified-type
5616          if (strpos($type, ':')) {
5617              $uqType = substr($type, strrpos($type, ':') + 1);
5618              $ns = substr($type, 0, strrpos($type, ':'));
5619              $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5620              if ($this->getNamespaceFromPrefix($ns)) {
5621                  $ns = $this->getNamespaceFromPrefix($ns);
5622                  $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5623              }
5624          } else {
5625              // TODO: should the type be compared to types in XSD, and the namespace
5626              // set to XSD if the type matches?
5627              $this->debug("in parametersMatchWrapped: No namespace for type $type");
5628              $ns = '';
5629              $uqType = $type;
5630          }
5631  
5632          // get the type information
5633          if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5634              $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5635              return false;
5636          }
5637          $this->debug("in parametersMatchWrapped: found typeDef=");
5638          $this->appendDebug($this->varDump($typeDef));
5639          if (substr($uqType, -1) == '^') {
5640              $uqType = substr($uqType, 0, -1);
5641          }
5642          $phpType = $typeDef['phpType'];
5643          $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5644          $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5645          
5646          // we expect a complexType or element of complexType
5647          if ($phpType != 'struct') {
5648              $this->debug("in parametersMatchWrapped: not a struct");
5649              return false;
5650          }
5651  
5652          // see whether the parameter names match the elements
5653          if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5654              $elements = 0;
5655              $matches = 0;
5656              foreach ($typeDef['elements'] as $name => $attrs) {
5657                  if (isset($parameters[$name])) {
5658                      $this->debug("in parametersMatchWrapped: have parameter named $name");
5659                      $matches++;
5660                  } else {
5661                      $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5662                  }
5663                  $elements++;
5664              }
5665  
5666              $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5667              if ($matches == 0) {
5668                  return false;
5669              }
5670              return true;
5671          }
5672  
5673          // since there are no elements for the type, if the user passed no
5674          // parameters, the parameters match wrapped.
5675          $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5676          return count($parameters) == 0;
5677      }
5678  
5679      /**
5680       * serialize PHP values according to a WSDL message definition
5681       * contrary to the method name, this is not limited to RPC
5682       *
5683       * TODO
5684       * - multi-ref serialization
5685       * - validate PHP values against type definitions, return errors if invalid
5686       * 
5687       * @param string $operation operation name
5688       * @param string $direction (input|output)
5689       * @param mixed $parameters parameter value(s)
5690       * @param string $bindingType (soap|soap12)
5691       * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5692       * @access public
5693       */
5694  	function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
5695          $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5696          $this->appendDebug('parameters=' . $this->varDump($parameters));
5697          
5698          if ($direction != 'input' && $direction != 'output') {
5699              $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5700              $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5701              return false;
5702          } 
5703          if (!$opData = $this->getOperationData($operation, $bindingType)) {
5704              $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5705              $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5706              return false;
5707          }
5708          $this->debug('in serializeRPCParameters: opData:');
5709          $this->appendDebug($this->varDump($opData));
5710  
5711          // Get encoding style for output and set to current
5712          $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5713          if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5714              $encodingStyle = $opData['output']['encodingStyle'];
5715              $enc_style = $encodingStyle;
5716          }
5717  
5718          // set input params
5719          $xml = '';
5720          if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5721              $parts = &$opData[$direction]['parts'];
5722              $part_count = sizeof($parts);
5723              $style = $opData['style'];
5724              $use = $opData[$direction]['use'];
5725              $this->debug("have $part_count part(s) to serialize using $style/$use");
5726              if (is_array($parameters)) {
5727                  $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5728                  $parameter_count = count($parameters);
5729                  $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5730                  // check for Microsoft-style wrapped parameters
5731                  if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
5732                      $this->debug('check whether the caller has wrapped the parameters');
5733                      if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
5734                          // TODO: consider checking here for double-wrapping, when
5735                          // service function wraps, then NuSOAP wraps again
5736                          $this->debug("change simple array to associative with 'parameters' element");
5737                          $parameters['parameters'] = $parameters[0];
5738                          unset($parameters[0]);
5739                      }
5740                      if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
5741                          $this->debug('check whether caller\'s parameters match the wrapped ones');
5742                          if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5743                              $this->debug('wrap the parameters for the caller');
5744                              $parameters = array('parameters' => $parameters);
5745                              $parameter_count = 1;
5746                          }
5747                      }
5748                  }
5749                  foreach ($parts as $name => $type) {
5750                      $this->debug("serializing part $name of type $type");
5751                      // Track encoding style
5752                      if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5753                          $encodingStyle = $opData[$direction]['encodingStyle'];            
5754                          $enc_style = $encodingStyle;
5755                      } else {
5756                          $enc_style = false;
5757                      }
5758                      // NOTE: add error handling here
5759                      // if serializeType returns false, then catch global error and fault
5760                      if ($parametersArrayType == 'arraySimple') {
5761                          $p = array_shift($parameters);
5762                          $this->debug('calling serializeType w/indexed param');
5763                          $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5764                      } elseif (isset($parameters[$name])) {
5765                          $this->debug('calling serializeType w/named param');
5766                          $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5767                      } else {
5768                          // TODO: only send nillable
5769                          $this->debug('calling serializeType w/null param');
5770                          $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5771                      }
5772                  }
5773              } else {
5774                  $this->debug('no parameters passed.');
5775              }
5776          }
5777          $this->debug("serializeRPCParameters returning: $xml");
5778          return $xml;
5779      } 
5780      
5781      /**
5782       * serialize a PHP value according to a WSDL message definition
5783       * 
5784       * TODO
5785       * - multi-ref serialization
5786       * - validate PHP values against type definitions, return errors if invalid
5787       * 
5788       * @param string $operation operation name
5789       * @param string $direction (input|output)
5790       * @param mixed $parameters parameter value(s)
5791       * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5792       * @access public
5793       * @deprecated
5794       */
5795  	function serializeParameters($operation, $direction, $parameters)
5796      {
5797          $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion"); 
5798          $this->appendDebug('parameters=' . $this->varDump($parameters));
5799          
5800          if ($direction != 'input' && $direction != 'output') {
5801              $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5802              $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5803              return false;
5804          } 
5805          if (!$opData = $this->getOperationData($operation)) {
5806              $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5807              $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5808              return false;
5809          }
5810          $this->debug('opData:');
5811          $this->appendDebug($this->varDump($opData));
5812          
5813          // Get encoding style for output and set to current
5814          $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5815          if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5816              $encodingStyle = $opData['output']['encodingStyle'];
5817              $enc_style = $encodingStyle;
5818          }
5819          
5820          // set input params
5821          $xml = '';
5822          if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5823              
5824              $use = $opData[$direction]['use'];
5825              $this->debug("use=$use");
5826              $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5827              if (is_array($parameters)) {
5828                  $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5829                  $this->debug('have ' . $parametersArrayType . ' parameters');
5830                  foreach($opData[$direction]['parts'] as $name => $type) {
5831                      $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5832                      // Track encoding style
5833                      if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5834                          $encodingStyle = $opData[$direction]['encodingStyle'];            
5835                          $enc_style = $encodingStyle;
5836                      } else {
5837                          $enc_style = false;
5838                      }
5839                      // NOTE: add error handling here
5840                      // if serializeType returns false, then catch global error and fault
5841                      if ($parametersArrayType == 'arraySimple') {
5842                          $p = array_shift($parameters);
5843                          $this->debug('calling serializeType w/indexed param');
5844                          $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5845                      } elseif (isset($parameters[$name])) {
5846                          $this->debug('calling serializeType w/named param');
5847                          $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5848                      } else {
5849                          // TODO: only send nillable
5850                          $this->debug('calling serializeType w/null param');
5851                          $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5852                      }
5853                  }
5854              } else {
5855                  $this->debug('no parameters passed.');
5856              }
5857          }
5858          $this->debug("serializeParameters returning: $xml");
5859          return $xml;
5860      } 
5861      
5862      /**
5863       * serializes a PHP value according a given type definition
5864       * 
5865       * @param string $name name of value (part or element)
5866       * @param string $type XML schema type of value (type or element)
5867       * @param mixed $value a native PHP value (parameter value)
5868       * @param string $use use for part (encoded|literal)
5869       * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5870       * @param boolean $unqualified a kludge for what should be XML namespace form handling
5871       * @return string value serialized as an XML string
5872       * @access private
5873       */
5874  	function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5875      {
5876          $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5877          $this->appendDebug("value=" . $this->varDump($value));
5878          if($use == 'encoded' && $encodingStyle) {
5879              $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5880          }
5881  
5882          // if a soapval has been supplied, let its type override the WSDL
5883          if (is_object($value) && get_class($value) == 'soapval') {
5884              if ($value->type_ns) {
5885                  $type = $value->type_ns . ':' . $value->type;
5886                  $forceType = true;
5887                  $this->debug("in serializeType: soapval overrides type to $type");
5888              } elseif ($value->type) {
5889                  $type = $value->type;
5890                  $forceType = true;
5891                  $this->debug("in serializeType: soapval overrides type to $type");
5892              } else {
5893                  $forceType = false;
5894                  $this->debug("in serializeType: soapval does not override type");
5895              }
5896              $attrs = $value->attributes;
5897              $value = $value->value;
5898              $this->debug("in serializeType: soapval overrides value to $value");
5899              if ($attrs) {
5900                  if (!is_array($value)) {
5901                      $value['!'] = $value;
5902                  }
5903                  foreach ($attrs as $n => $v) {
5904                      $value['!' . $n] = $v;
5905                  }
5906                  $this->debug("in serializeType: soapval provides attributes");
5907              }
5908          } else {
5909              $forceType = false;
5910          }
5911  
5912          $xml = '';
5913          if (strpos($type, ':')) {
5914              $uqType = substr($type, strrpos($type, ':') + 1);
5915              $ns = substr($type, 0, strrpos($type, ':'));
5916              $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5917              if ($this->getNamespaceFromPrefix($ns)) {
5918                  $ns = $this->getNamespaceFromPrefix($ns);
5919                  $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5920              }
5921  
5922              if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5923                  $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5924                  if ($unqualified && $use == 'literal') {
5925                      $elementNS = " xmlns=\"\"";
5926                  } else {
5927                      $elementNS = '';
5928                  }
5929                  if (is_null($value)) {
5930                      if ($use == 'literal') {
5931                          // TODO: depends on minOccurs
5932                          $xml = "<$name$elementNS/>";
5933                      } else {
5934                          // TODO: depends on nillable, which should be checked before calling this method
5935                          $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5936                      }
5937                      $this->debug("in serializeType: returning: $xml");
5938                      return $xml;
5939                  }
5940                  if ($uqType == 'Array') {
5941                      // JBoss/Axis does this sometimes
5942                      return $this->serialize_val($value, $name, false, false, false, false, $use);
5943                  }
5944                  if ($uqType == 'boolean') {
5945                      if ((is_string($value) && $value == 'false') || (! $value)) {
5946                          $value = 'false';
5947                      } else {
5948                          $value = 'true';
5949                      }
5950                  } 
5951                  if ($uqType == 'string' && gettype($value) == 'string') {
5952                      $value = $this->expandEntities($value);
5953                  }
5954                  if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5955                      $value = sprintf("%.0lf", $value);
5956                  }
5957                  // it's a scalar
5958                  // TODO: what about null/nil values?
5959                  // check type isn't a custom type extending xmlschema namespace
5960                  if (!$this->getTypeDef($uqType, $ns)) {
5961                      if ($use == 'literal') {
5962                          if ($forceType) {
5963                              $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5964                          } else {
5965                              $xml = "<$name$elementNS>$value</$name>";
5966                          }
5967                      } else {
5968                          $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5969                      }
5970                      $this->debug("in serializeType: returning: $xml");
5971                      return $xml;
5972                  }
5973                  $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5974              } else if ($ns == 'http://xml.apache.org/xml-soap') {
5975                  $this->debug('in serializeType: appears to be Apache SOAP type');
5976                  if ($uqType == 'Map') {
5977                      $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5978                      if (! $tt_prefix) {
5979                          $this->debug('in serializeType: Add namespace for Apache SOAP type');
5980                          $tt_prefix = 'ns' . rand(1000, 9999);
5981                          $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5982                          // force this to be added to usedNamespaces
5983                          $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5984                      }
5985                      $contents = '';
5986                      foreach($value as $k => $v) {
5987                          $this->debug("serializing map element: key $k, value $v");
5988                          $contents .= '<item>';
5989                          $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5990                          $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5991                          $contents .= '</item>';
5992                      }
5993                      if ($use == 'literal') {
5994                          if ($forceType) {
5995                              $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5996                          } else {
5997                              $xml = "<$name>$contents</$name>";
5998                          }
5999                      } else {
6000                          $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6001                      }
6002                      $this->debug("in serializeType: returning: $xml");
6003                      return $xml;
6004                  }
6005                  $this->debug('in serializeType: Apache SOAP type, but only support Map');
6006              }
6007          } else {
6008              // TODO: should the type be compared to types in XSD, and the namespace
6009              // set to XSD if the type matches?
6010              $this->debug("in serializeType: No namespace for type $type");
6011              $ns = '';
6012              $uqType = $type;
6013          }
6014          if(!$typeDef = $this->getTypeDef($uqType, $ns)){
6015              $this->setError("$type ($uqType) is not a supported type.");
6016              $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6017              return false;
6018          } else {
6019              $this->debug("in serializeType: found typeDef");
6020              $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6021              if (substr($uqType, -1) == '^') {
6022                  $uqType = substr($uqType, 0, -1);
6023              }
6024          }
6025          if (!isset($typeDef['phpType'])) {
6026              $this->setError("$type ($uqType) has no phpType.");
6027              $this->debug("in serializeType: $type ($uqType) has no phpType.");
6028              return false;
6029          }
6030          $phpType = $typeDef['phpType'];
6031          $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); 
6032          // if php type == struct, map value to the <all> element names
6033          if ($phpType == 'struct') {
6034              if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
6035                  $elementName = $uqType;
6036                  if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6037                      $elementNS = " xmlns=\"$ns\"";
6038                  } else {
6039                      $elementNS = " xmlns=\"\"";
6040                  }
6041              } else {
6042                  $elementName = $name;
6043                  if ($unqualified) {
6044                      $elementNS = " xmlns=\"\"";
6045                  } else {
6046                      $elementNS = '';
6047                  }
6048              }
6049              if (is_null($value)) {
6050                  if ($use == 'literal') {
6051                      // TODO: depends on minOccurs and nillable
6052                      $xml = "<$elementName$elementNS/>";
6053                  } else {
6054                      $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6055                  }
6056                  $this->debug("in serializeType: returning: $xml");
6057                  return $xml;
6058              }
6059              if (is_object($value)) {
6060                  $value = get_object_vars($value);
6061              }
6062              if (is_array($value)) {
6063                  $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6064                  if ($use == 'literal') {
6065                      if ($forceType) {
6066                          $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6067                      } else {
6068                          $xml = "<$elementName$elementNS$elementAttrs>";
6069                      }
6070                  } else {
6071                      $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6072                  }
6073  
6074                  if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
6075                      if (isset($value['!'])) {
6076                          $xml .= $value['!'];
6077                          $this->debug("in serializeType: serialized simpleContent for type $type");
6078                      } else {
6079                          $this->debug("in serializeType: no simpleContent to serialize for type $type");
6080                      }
6081                  } else {
6082                      // complexContent
6083                      $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6084                  }
6085                  $xml .= "</$elementName>";
6086              } else {
6087                  $this->debug("in serializeType: phpType is struct, but value is not an array");
6088                  $this->setError("phpType is struct, but value is not an array: see debug output for details");
6089                  $xml = '';
6090              }
6091          } elseif ($phpType == 'array') {
6092              if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6093                  $elementNS = " xmlns=\"$ns\"";
6094              } else {
6095                  if ($unqualified) {
6096                      $elementNS = " xmlns=\"\"";
6097                  } else {
6098                      $elementNS = '';
6099                  }
6100              }
6101              if (is_null($value)) {
6102                  if ($use == 'literal') {
6103                      // TODO: depends on minOccurs
6104                      $xml = "<$name$elementNS/>";
6105                  } else {
6106                      $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
6107                          $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6108                          ":Array\" " .
6109                          $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6110                          ':arrayType="' .
6111                          $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
6112                          ':' .
6113                          $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
6114                  }
6115                  $this->debug("in serializeType: returning: $xml");
6116                  return $xml;
6117              }
6118              if (isset($typeDef['multidimensional'])) {
6119                  $nv = array();
6120                  foreach($value as $v) {
6121                      $cols = ',' . sizeof($v);
6122                      $nv = array_merge($nv, $v);
6123                  } 
6124                  $value = $nv;
6125              } else {
6126                  $cols = '';
6127              } 
6128              if (is_array($value) && sizeof($value) >= 1) {
6129                  $rows = sizeof($value);
6130                  $contents = '';
6131                  foreach($value as $k => $v) {
6132                      $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
6133                      //if (strpos($typeDef['arrayType'], ':') ) {
6134                      if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6135                          $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6136                      } else {
6137                          $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6138                      } 
6139                  }
6140              } else {
6141                  $rows = 0;
6142                  $contents = null;
6143              }
6144              // TODO: for now, an empty value will be serialized as a zero element
6145              // array.  Revisit this when coding the handling of null/nil values.
6146              if ($use == 'literal') {
6147                  $xml = "<$name$elementNS>"
6148                      .$contents
6149                      ."</$name>";
6150              } else {
6151                  $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
6152                      $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6153                      .':arrayType="'
6154                      .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6155                      .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
6156                      .$contents
6157                      ."</$name>";
6158              }
6159          } elseif ($phpType == 'scalar') {
6160              if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6161                  $elementNS = " xmlns=\"$ns\"";
6162              } else {
6163                  if ($unqualified) {
6164                      $elementNS = " xmlns=\"\"";
6165                  } else {
6166                      $elementNS = '';
6167                  }
6168              }
6169              if ($use == 'literal') {
6170                  if ($forceType) {
6171                      $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6172                  } else {
6173                      $xml = "<$name$elementNS>$value</$name>";
6174                  }
6175              } else {
6176                  $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6177              }
6178          }
6179          $this->debug("in serializeType: returning: $xml");
6180          return $xml;
6181      }
6182      
6183      /**
6184       * serializes the attributes for a complexType
6185       *
6186       * @param array $typeDef our internal representation of an XML schema type (or element)
6187       * @param mixed $value a native PHP value (parameter value)
6188       * @param string $ns the namespace of the type
6189       * @param string $uqType the local part of the type
6190       * @return string value serialized as an XML string
6191       * @access private
6192       */
6193  	function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
6194          $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6195          $xml = '';
6196          if (isset($typeDef['extensionBase'])) {
6197              $nsx = $this->getPrefix($typeDef['extensionBase']);
6198              $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6199              if ($this->getNamespaceFromPrefix($nsx)) {
6200                  $nsx = $this->getNamespaceFromPrefix($nsx);
6201              }
6202              if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6203                  $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6204                  $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6205              } else {
6206                  $this->debug("extension base $nsx:$uqTypex is not a supported type");
6207              }
6208          }
6209          if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6210              $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6211              if (is_array($value)) {
6212                  $xvalue = $value;
6213              } elseif (is_object($value)) {
6214                  $xvalue = get_object_vars($value);
6215              } else {
6216                  $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6217                  $xvalue = array();
6218              }
6219              foreach ($typeDef['attrs'] as $aName => $attrs) {
6220                  if (isset($xvalue['!' . $aName])) {
6221                      $xname = '!' . $aName;
6222                      $this->debug("value provided for attribute $aName with key $xname");
6223                  } elseif (isset($xvalue[$aName])) {
6224                      $xname = $aName;
6225                      $this->debug("value provided for attribute $aName with key $xname");
6226                  } elseif (isset($attrs['default'])) {
6227                      $xname = '!' . $aName;
6228                      $xvalue[$xname] = $attrs['default'];
6229                      $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6230                  } else {
6231                      $xname = '';
6232                      $this->debug("no value provided for attribute $aName");
6233                  }
6234                  if ($xname) {
6235                      $xml .=  " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
6236                  }
6237              } 
6238          } else {
6239              $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6240          }
6241          return $xml;
6242      }
6243  
6244      /**
6245       * serializes the elements for a complexType
6246       *
6247       * @param array $typeDef our internal representation of an XML schema type (or element)
6248       * @param mixed $value a native PHP value (parameter value)
6249       * @param string $ns the namespace of the type
6250       * @param string $uqType the local part of the type
6251       * @param string $use use for part (encoded|literal)
6252       * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6253       * @return string value serialized as an XML string
6254       * @access private
6255       */
6256  	function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
6257          $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6258          $xml = '';
6259          if (isset($typeDef['extensionBase'])) {
6260              $nsx = $this->getPrefix($typeDef['extensionBase']);
6261              $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6262              if ($this->getNamespaceFromPrefix($nsx)) {
6263                  $nsx = $this->getNamespaceFromPrefix($nsx);
6264              }
6265              if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6266                  $this->debug("serialize elements for extension base $nsx:$uqTypex");
6267                  $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6268              } else {
6269                  $this->debug("extension base $nsx:$uqTypex is not a supported type");
6270              }
6271          }
6272          if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6273              $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6274              if (is_array($value)) {
6275                  $xvalue = $value;
6276              } elseif (is_object($value)) {
6277                  $xvalue = get_object_vars($value);
6278              } else {
6279                  $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6280                  $xvalue = array();
6281              }
6282              // toggle whether all elements are present - ideally should validate against schema
6283              if (count($typeDef['elements']) != count($xvalue)){
6284                  $optionals = true;
6285              }
6286              foreach ($typeDef['elements'] as $eName => $attrs) {
6287                  if (!isset($xvalue[$eName])) {
6288                      if (isset($attrs['default'])) {
6289                          $xvalue[$eName] = $attrs['default'];
6290                          $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6291                      }
6292                  }
6293                  // if user took advantage of a minOccurs=0, then only serialize named parameters
6294                  if (isset($optionals)
6295                      && (!isset($xvalue[$eName])) 
6296                      && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
6297                      ){
6298                      if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
6299                          $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6300                      }
6301                      // do nothing
6302                      $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6303                  } else {
6304                      // get value
6305                      if (isset($xvalue[$eName])) {
6306                          $v = $xvalue[$eName];
6307                      } else {
6308                          $v = null;
6309                      }
6310                      if (isset($attrs['form'])) {
6311                          $unqualified = ($attrs['form'] == 'unqualified');
6312                      } else {
6313                          $unqualified = false;
6314                      }
6315                      if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
6316                          $vv = $v;
6317                          foreach ($vv as $k => $v) {
6318                              if (isset($attrs['type']) || isset($attrs['ref'])) {
6319                                  // serialize schema-defined type
6320                                  $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6321                              } else {
6322                                  // serialize generic type (can this ever really happen?)
6323                                  $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6324                                  $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6325                              }
6326                          }
6327                      } else {
6328                          if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
6329                              // do nothing
6330                          } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
6331                              // TODO: serialize a nil correctly, but for now serialize schema-defined type
6332                              $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6333                          } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6334                              // serialize schema-defined type
6335                              $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6336                          } else {
6337                              // serialize generic type (can this ever really happen?)
6338                              $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6339                              $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6340                          }
6341                      }
6342                  }
6343              } 
6344          } else {
6345              $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6346          }
6347          return $xml;
6348      }
6349  
6350      /**
6351      * adds an XML Schema complex type to the WSDL types
6352      *
6353      * @param string    $name
6354      * @param string $typeClass (complexType|simpleType|attribute)
6355      * @param string $phpType currently supported are array and struct (php assoc array)
6356      * @param string $compositor (all|sequence|choice)
6357      * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6358      * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6359      * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6360      * @param string $arrayType as namespace:name (xsd:string)
6361      * @see nusoap_xmlschema
6362      * @access public
6363      */
6364  	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
6365          if (count($elements) > 0) {
6366              $eElements = array();
6367              foreach($elements as $n => $e){
6368                  // expand each element
6369                  $ee = array();
6370                  foreach ($e as $k => $v) {
6371                      $k = strpos($k,':') ? $this->expandQname($k) : $k;
6372                      $v = strpos($v,':') ? $this->expandQname($v) : $v;
6373                      $ee[$k] = $v;
6374                  }
6375                  $eElements[$n] = $ee;
6376              }
6377              $elements = $eElements;
6378          }
6379          
6380          if (count($attrs) > 0) {
6381              foreach($attrs as $n => $a){
6382                  // expand each attribute
6383                  foreach ($a as $k => $v) {
6384                      $k = strpos($k,':') ? $this->expandQname($k) : $k;
6385                      $v = strpos($v,':') ? $this->expandQname($v) : $v;
6386                      $aa[$k] = $v;
6387                  }
6388                  $eAttrs[$n] = $aa;
6389              }
6390              $attrs = $eAttrs;
6391          }
6392  
6393          $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6394          $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
6395  
6396          $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6397          $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
6398      }
6399  
6400      /**
6401      * adds an XML Schema simple type to the WSDL types
6402      *
6403      * @param string $name
6404      * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6405      * @param string $typeClass (should always be simpleType)
6406      * @param string $phpType (should always be scalar)
6407      * @param array $enumeration array of values
6408      * @see nusoap_xmlschema
6409      * @access public
6410      */
6411  	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
6412          $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6413  
6414          $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6415          $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6416      }
6417  
6418      /**
6419      * adds an element to the WSDL types
6420      *
6421      * @param array $attrs attributes that must include name and type
6422      * @see nusoap_xmlschema
6423      * @access public
6424      */
6425  	function addElement($attrs) {
6426          $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6427          $this->schemas[$typens][0]->addElement($attrs);
6428      }
6429  
6430      /**
6431      * register an operation with the server
6432      * 
6433      * @param string $name operation (method) name
6434      * @param array $in assoc array of input values: key = param name, value = param type
6435      * @param array $out assoc array of output values: key = param name, value = param type
6436      * @param string $namespace optional The namespace for the operation
6437      * @param string $soapaction optional The soapaction for the operation
6438      * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
6439      * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
6440      * @param string $documentation optional The description to include in the WSDL
6441      * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6442      * @access public 
6443      */
6444  	function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
6445          if ($use == 'encoded' && $encodingStyle == '') {
6446              $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6447          }
6448  
6449          if ($style == 'document') {
6450              $elements = array();
6451              foreach ($in as $n => $t) {
6452                  $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6453              }
6454              $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6455              $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
6456              $in = array('parameters' => 'tns:' . $name . '^');
6457  
6458              $elements = array();
6459              foreach ($out as $n => $t) {
6460                  $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6461              }
6462              $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6463              $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
6464              $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
6465          }
6466  
6467          // get binding
6468          $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
6469          array(
6470          'name' => $name,
6471          'binding' => $this->serviceName . 'Binding',
6472          'endpoint' => $this->endpoint,
6473          'soapAction' => $soapaction,
6474          'style' => $style,
6475          'input' => array(
6476              'use' => $use,
6477              'namespace' => $namespace,
6478              'encodingStyle' => $encodingStyle,
6479              'message' => $name . 'Request',
6480              'parts' => $in),
6481          'output' => array(
6482              'use' => $use,
6483              'namespace' => $namespace,
6484              'encodingStyle' => $encodingStyle,
6485              'message' => $name . 'Response',
6486              'parts' => $out),
6487          'namespace' => $namespace,
6488          'transport' => 'http://schemas.xmlsoap.org/soap/http',
6489          'documentation' => $documentation); 
6490          // add portTypes
6491          // add messages
6492          if($in)
6493          {
6494              foreach($in as $pName => $pType)
6495              {
6496                  if(strpos($pType,':')) {
6497                      $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6498                  }
6499                  $this->messages[$name.'Request'][$pName] = $pType;
6500              }
6501          } else {
6502              $this->messages[$name.'Request']= '0';
6503          }
6504          if($out)
6505          {
6506              foreach($out as $pName => $pType)
6507              {
6508                  if(strpos($pType,':')) {
6509                      $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6510                  }
6511                  $this->messages[$name.'Response'][$pName] = $pType;
6512              }
6513          } else {
6514              $this->messages[$name.'Response']= '0';
6515          }
6516          return true;
6517      } 
6518  }
6519  ?><?php
6520  
6521  
6522  
6523  /**
6524  *
6525  * nusoap_parser class parses SOAP XML messages into native PHP values
6526  *
6527  * @author   Dietrich Ayala <dietrich@ganx4.com>
6528  * @author   Scott Nichol <snichol@users.sourceforge.net>
6529  * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6530  * @access   public
6531  */
6532  class nusoap_parser extends nusoap_base {
6533  
6534      var $xml = '';
6535      var $xml_encoding = '';
6536      var $method = '';
6537      var $root_struct = '';
6538      var $root_struct_name = '';
6539      var $root_struct_namespace = '';
6540      var $root_header = '';
6541      var $document = '';            // incoming SOAP body (text)
6542      // determines where in the message we are (envelope,header,body,method)
6543      var $status = '';
6544      var $position = 0;
6545      var $depth = 0;
6546      var $default_namespace = '';
6547      var $namespaces = array();
6548      var $message = array();
6549      var $parent = '';
6550      var $fault = false;
6551      var $fault_code = '';
6552      var $fault_str = '';
6553      var $fault_detail = '';
6554      var $depth_array = array();
6555      var $debug_flag = true;
6556      var $soapresponse = NULL;    // parsed SOAP Body
6557      var $soapheader = NULL;        // parsed SOAP Header
6558      var $responseHeaders = '';    // incoming SOAP headers (text)
6559      var $body_position = 0;
6560      // for multiref parsing:
6561      // array of id => pos
6562      var $ids = array();
6563      // array of id => hrefs => pos
6564      var $multirefs = array();
6565      // toggle for auto-decoding element content
6566      var $decode_utf8 = true;
6567  
6568      /**
6569      * constructor that actually does the parsing
6570      *
6571      * @param    string $xml SOAP message
6572      * @param    string $encoding character encoding scheme of message
6573      * @param    string $method method for which XML is parsed (unused?)
6574      * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6575      * @access   public
6576      */
6577  	function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
6578          parent::nusoap_base();
6579          $this->xml = $xml;
6580          $this->xml_encoding = $encoding;
6581          $this->method = $method;
6582          $this->decode_utf8 = $decode_utf8;
6583  
6584          // Check whether content has been read.
6585          if(!empty($xml)){
6586              // Check XML encoding
6587              $pos_xml = strpos($xml, '<?xml');
6588              if ($pos_xml !== FALSE) {
6589                  $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6590                  if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6591                      $xml_encoding = $res[1];
6592                      if (strtoupper($xml_encoding) != $encoding) {
6593                          $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6594                          $this->debug($err);
6595                          if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
6596                              $this->setError($err);
6597                              return;
6598                          }
6599                          // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6600                      } else {
6601                          $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6602                      }
6603                  } else {
6604                      $this->debug('No encoding specified in XML declaration');
6605                  }
6606              } else {
6607                  $this->debug('No XML declaration');
6608              }
6609              $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
6610              // Create an XML parser - why not xml_parser_create_ns?
6611              $this->parser = xml_parser_create($this->xml_encoding);
6612              // Set the options for parsing the XML data.
6613              //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6614              xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6615              xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6616              // Set the object for the parser.
6617              xml_set_object($this->parser, $this);
6618              // Set the element handlers for the parser.
6619              xml_set_element_handler($this->parser, 'start_element','end_element');
6620              xml_set_character_data_handler($this->parser,'character_data');
6621  
6622              // Parse the XML file.
6623              if(!xml_parse($this->parser,$xml,true)){
6624                  // Display an error message.
6625                  $err = sprintf('XML error parsing SOAP payload on line %d: %s',
6626                  xml_get_current_line_number($this->parser),
6627                  xml_error_string(xml_get_error_code($this->parser)));
6628                  $this->debug($err);
6629                  $this->debug("XML payload:\n" . $xml);
6630                  $this->setError($err);
6631              } else {
6632                  $this->debug('in nusoap_parser ctor, message:');
6633                  $this->appendDebug($this->varDump($this->message));
6634                  $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
6635                  // get final value
6636                  $this->soapresponse = $this->message[$this->root_struct]['result'];
6637                  // get header value
6638                  if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
6639                      $this->soapheader = $this->message[$this->root_header]['result'];
6640                  }
6641                  // resolve hrefs/ids
6642                  if(sizeof($this->multirefs) > 0){
6643                      foreach($this->multirefs as $id => $hrefs){
6644                          $this->debug('resolving multirefs for id: '.$id);
6645                          $idVal = $this->buildVal($this->ids[$id]);
6646                          if (is_array($idVal) && isset($idVal['!id'])) {
6647                              unset($idVal['!id']);
6648                          }
6649                          foreach($hrefs as $refPos => $ref){
6650                              $this->debug('resolving href at pos '.$refPos);
6651                              $this->multirefs[$id][$refPos] = $idVal;
6652                          }
6653                      }
6654                  }
6655              }
6656              xml_parser_free($this->parser);
6657          } else {
6658              $this->debug('xml was empty, didn\'t parse!');
6659              $this->setError('xml was empty, didn\'t parse!');
6660          }
6661      }
6662  
6663      /**
6664      * start-element handler
6665      *
6666      * @param    resource $parser XML parser object
6667      * @param    string $name element name
6668      * @param    array $attrs associative array of attributes
6669      * @access   private
6670      */
6671  	function start_element($parser, $name, $attrs) {
6672          // position in a total number of elements, starting from 0
6673          // update class level pos
6674          $pos = $this->position++;
6675          // and set mine
6676          $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
6677          // depth = how many levels removed from root?
6678          // set mine as current global depth and increment global depth value
6679          $this->message[$pos]['depth'] = $this->depth++;
6680  
6681          // else add self as child to whoever the current parent is
6682          if($pos != 0){
6683              $this->message[$this->parent]['children'] .= '|'.$pos;
6684          }
6685          // set my parent
6686          $this->message[$pos]['parent'] = $this->parent;
6687          // set self as current parent
6688          $this->parent = $pos;
6689          // set self as current value for this depth
6690          $this->depth_array[$this->depth] = $pos;
6691          // get element prefix
6692          if(strpos($name,':')){
6693              // get ns prefix
6694              $prefix = substr($name,0,strpos($name,':'));
6695              // get unqualified name
6696              $name = substr(strstr($name,':'),1);
6697          }
6698          // set status
6699          if ($name == 'Envelope' && $this->status == '') {
6700              $this->status = 'envelope';
6701          } elseif ($name == 'Header' && $this->status == 'envelope') {
6702              $this->root_header = $pos;
6703              $this->status = 'header';
6704          } elseif ($name == 'Body' && $this->status == 'envelope'){
6705              $this->status = 'body';
6706              $this->body_position = $pos;
6707          // set method
6708          } elseif($this->status == 'body' && $pos == ($this->body_position+1)) {
6709              $this->status = 'method';
6710              $this->root_struct_name = $name;
6711              $this->root_struct = $pos;
6712              $this->message[$pos]['type'] = 'struct';
6713              $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6714          }
6715          // set my status
6716          $this->message[$pos]['status'] = $this->status;
6717          // set name
6718          $this->message[$pos]['name'] = htmlspecialchars($name);
6719          // set attrs
6720          $this->message[$pos]['attrs'] = $attrs;
6721  
6722          // loop through atts, logging ns and type declarations
6723          $attstr = '';
6724          foreach($attrs as $key => $value){
6725              $key_prefix = $this->getPrefix($key);
6726              $key_localpart = $this->getLocalPart($key);
6727              // if ns declarations, add to class level array of valid namespaces
6728              if($key_prefix == 'xmlns'){
6729                  if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){
6730                      $this->XMLSchemaVersion = $value;
6731                      $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6732                      $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
6733                  }
6734                  $this->namespaces[$key_localpart] = $value;
6735                  // set method namespace
6736                  if($name == $this->root_struct_name){
6737                      $this->methodNamespace = $value;
6738                  }
6739              // if it's a type declaration, set type
6740          } elseif($key_localpart == 'type'){
6741                  if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
6742                      // do nothing: already processed arrayType
6743                  } else {
6744                      $value_prefix = $this->getPrefix($value);
6745                      $value_localpart = $this->getLocalPart($value);
6746                      $this->message[$pos]['type'] = $value_localpart;
6747                      $this->message[$pos]['typePrefix'] = $value_prefix;
6748                      if(isset($this->namespaces[$value_prefix])){
6749                          $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6750                      } else if(isset($attrs['xmlns:'.$value_prefix])) {
6751                          $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6752                      }
6753                      // should do something here with the namespace of specified type?
6754                  }
6755              } elseif($key_localpart == 'arrayType'){
6756                  $this->message[$pos]['type'] = 'array';
6757                  /* do arrayType ereg here
6758                  [1]    arrayTypeValue    ::=    atype asize
6759                  [2]    atype    ::=    QName rank*
6760                  [3]    rank    ::=    '[' (',')* ']'
6761                  [4]    asize    ::=    '[' length~ ']'
6762                  [5]    length    ::=    nextDimension* Digit+
6763                  [6]    nextDimension    ::=    Digit+ ','
6764                  */
6765                  $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
6766                  if(preg_match($expr,$value,$regs)){
6767                      $this->message[$pos]['typePrefix'] = $regs[1];
6768                      $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6769                      if (isset($this->namespaces[$regs[1]])) {
6770                          $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6771                      } else if (isset($attrs['xmlns:'.$regs[1]])) {
6772                          $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6773                      }
6774                      $this->message[$pos]['arrayType'] = $regs[2];
6775                      $this->message[$pos]['arraySize'] = $regs[3];
6776                      $this->message[$pos]['arrayCols'] = $regs[4];
6777                  }
6778              // specifies nil value (or not)
6779              } elseif ($key_localpart == 'nil'){
6780                  $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6781              // some other attribute
6782              } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6783                  $this->message[$pos]['xattrs']['!' . $key] = $value;
6784              }
6785  
6786              if ($key == 'xmlns') {
6787                  $this->default_namespace = $value;
6788              }
6789              // log id
6790              if($key == 'id'){
6791                  $this->ids[$value] = $pos;
6792              }
6793              // root
6794              if($key_localpart == 'root' && $value == 1){
6795                  $this->status = 'method';
6796                  $this->root_struct_name = $name;
6797                  $this->root_struct = $pos;
6798                  $this->debug("found root struct $this->root_struct_name, pos $pos");
6799              }
6800              // for doclit
6801              $attstr .= " $key=\"$value\"";
6802          }
6803          // get namespace - must be done after namespace atts are processed
6804          if(isset($prefix)){
6805              $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6806              $this->default_namespace = $this->namespaces[$prefix];
6807          } else {
6808              $this->message[$pos]['namespace'] = $this->default_namespace;
6809          }
6810          if($this->status == 'header'){
6811              if ($this->root_header != $pos) {
6812                  $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6813              }
6814          } elseif($this->root_struct_name != ''){
6815              $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6816          }
6817      }
6818  
6819      /**
6820      * end-element handler
6821      *
6822      * @param    resource $parser XML parser object
6823      * @param    string $name element name
6824      * @access   private
6825      */
6826  	function end_element($parser, $name) {
6827          // position of current element is equal to the last value left in depth_array for my depth
6828          $pos = $this->depth_array[$this->depth--];
6829  
6830          // get element prefix
6831          if(strpos($name,':')){
6832              // get ns prefix
6833              $prefix = substr($name,0,strpos($name,':'));
6834              // get unqualified name
6835              $name = substr(strstr($name,':'),1);
6836          }
6837          
6838          // build to native type
6839          if(isset($this->body_position) && $pos > $this->body_position){
6840              // deal w/ multirefs
6841              if(isset($this->message[$pos]['attrs']['href'])){
6842                  // get id
6843                  $id = substr($this->message[$pos]['attrs']['href'],1);
6844                  // add placeholder to href array
6845                  $this->multirefs[$id][$pos] = 'placeholder';
6846                  // add set a reference to it as the result value
6847                  $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6848              // build complexType values
6849              } elseif($this->message[$pos]['children'] != ''){
6850                  // if result has already been generated (struct/array)
6851                  if(!isset($this->message[$pos]['result'])){
6852                      $this->message[$pos]['result'] = $this->buildVal($pos);
6853                  }
6854              // build complexType values of attributes and possibly simpleContent
6855              } elseif (isset($this->message[$pos]['xattrs'])) {
6856                  if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6857                      $this->message[$pos]['xattrs']['!'] = null;
6858                  } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6859                      if (isset($this->message[$pos]['type'])) {
6860                          $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6861                      } else {
6862                          $parent = $this->message[$pos]['parent'];
6863                          if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6864                              $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6865                          } else {
6866                              $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6867                          }
6868                      }
6869                  }
6870                  $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6871              // set value of simpleType (or nil complexType)
6872              } else {
6873                  //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6874                  if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6875                      $this->message[$pos]['xattrs']['!'] = null;
6876                  } elseif (isset($this->message[$pos]['type'])) {
6877                      $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6878                  } else {
6879                      $parent = $this->message[$pos]['parent'];
6880                      if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6881                          $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6882                      } else {
6883                          $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6884                      }
6885                  }
6886  
6887                  /* add value to parent's result, if parent is struct/array
6888                  $parent = $this->message[$pos]['parent'];
6889                  if($this->message[$parent]['type'] != 'map'){
6890                      if(strtolower($this->message[$parent]['type']) == 'array'){
6891                          $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6892                      } else {
6893                          $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6894                      }
6895                  }
6896                  */
6897              }
6898          }
6899          
6900          // for doclit
6901          if($this->status == 'header'){
6902              if ($this->root_header != $pos) {
6903                  $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6904              }
6905          } elseif($pos >= $this->root_struct){
6906              $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6907          }
6908          // switch status
6909          if ($pos == $this->root_struct){
6910              $this->status = 'body';
6911              $this->root_struct_namespace = $this->message[$pos]['namespace'];
6912          } elseif ($pos == $this->root_header) {
6913              $this->status = 'envelope';
6914          } elseif ($name == 'Body' && $this->status == 'body') {
6915              $this->status = 'envelope';
6916          } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
6917              $this->status = 'envelope';
6918          } elseif ($name == 'Envelope' && $this->status == 'envelope') {
6919              $this->status = '';
6920          }
6921          // set parent back to my parent
6922          $this->parent = $this->message[$pos]['parent'];
6923      }
6924  
6925      /**
6926      * element content handler
6927      *
6928      * @param    resource $parser XML parser object
6929      * @param    string $data element content
6930      * @access   private
6931      */
6932  	function character_data($parser, $data){
6933          $pos = $this->depth_array[$this->depth];
6934          if ($this->xml_encoding=='UTF-8'){
6935              // TODO: add an option to disable this for folks who want
6936              // raw UTF-8 that, e.g., might not map to iso-8859-1
6937              // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6938              if($this->decode_utf8){
6939                  $data = utf8_decode($data);
6940              }
6941          }
6942          $this->message[$pos]['cdata'] .= $data;
6943          // for doclit
6944          if($this->status == 'header'){
6945              $this->responseHeaders .= $data;
6946          } else {
6947              $this->document .= $data;
6948          }
6949      }
6950  
6951      /**
6952      * get the parsed message (SOAP Body)
6953      *
6954      * @return    mixed
6955      * @access   public
6956      * @deprecated    use get_soapbody instead
6957      */
6958  	function get_response(){
6959          return $this->soapresponse;
6960      }
6961  
6962      /**
6963      * get the parsed SOAP Body (NULL if there was none)
6964      *
6965      * @return    mixed
6966      * @access   public
6967      */
6968  	function get_soapbody(){
6969          return $this->soapresponse;
6970      }
6971  
6972      /**
6973      * get the parsed SOAP Header (NULL if there was none)
6974      *
6975      * @return    mixed
6976      * @access   public
6977      */
6978  	function get_soapheader(){
6979          return $this->soapheader;
6980      }
6981  
6982      /**
6983      * get the unparsed SOAP Header
6984      *
6985      * @return    string XML or empty if no Header
6986      * @access   public
6987      */
6988  	function getHeaders(){
6989          return $this->responseHeaders;
6990      }
6991  
6992      /**
6993      * decodes simple types into PHP variables
6994      *
6995      * @param    string $value value to decode
6996      * @param    string $type XML type to decode
6997      * @param    string $typens XML type namespace to decode
6998      * @return    mixed PHP value
6999      * @access   private
7000      */
7001  	function decodeSimple($value, $type, $typens) {
7002          // TODO: use the namespace!
7003          if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
7004              return (string) $value;
7005          }
7006          if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
7007              return (int) $value;
7008          }
7009          if ($type == 'float' || $type == 'double' || $type == 'decimal') {
7010              return (double) $value;
7011          }
7012          if ($type == 'boolean') {
7013              if (strtolower($value) == 'false' || strtolower($value) == 'f') {
7014                  return false;
7015              }
7016              return (boolean) $value;
7017          }
7018          if ($type == 'base64' ||