Dokeos PHP Cross Reference Learning Management Systems

Source: /main/inc/lib/nusoap/nusoap.php - 7996 lines - 282070 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /*
   4  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 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 mailing list:
  28  nusoap-general@lists.sourceforge.net
  29  
  30  If you have any questions or comments, please email:
  31  
  32  Dietrich Ayala
  33  dietrich@ganx4.com
  34  http://dietrich.ganx4.com/nusoap
  35  
  36  NuSphere Corporation
  37  http://www.nusphere.com
  38  
  39  */
  40  
  41  /*
  42   *    Some of the standards implmented in whole or part by NuSOAP:
  43   *
  44   *    SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
  45   *    WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
  46   *    SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
  47   *    XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
  48   *    Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
  49   *    XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
  50   *    RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
  51   *    RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
  52   *    RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
  53   */
  54  
  55  /* load classes
  56  
  57  // necessary classes
  58  require_once('class.soapclient.php');
  59  require_once('class.soap_val.php');
  60  require_once('class.soap_parser.php');
  61  require_once('class.soap_fault.php');
  62  
  63  // transport classes
  64  require_once('class.soap_transport_http.php');
  65  
  66  // optional add-on classes
  67  require_once('class.xmlschema.php');
  68  require_once('class.wsdl.php');
  69  
  70  // server class
  71  require_once('class.soap_server.php');*/
  72  
  73  // class variable emulation
  74  // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
  75  $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
  76  
  77  /**
  78  *
  79  * nusoap_base
  80  *
  81  * @author   Dietrich Ayala <dietrich@ganx4.com>
  82  * @author   Scott Nichol <snichol@users.sourceforge.net>
  83  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
  84  * @access   public
  85  */
  86  class nusoap_base {
  87      /**
  88       * Identification for HTTP headers.
  89       *
  90       * @var string
  91       * @access private
  92       */
  93      public $title = 'NuSOAP';
  94      /**
  95       * Version for HTTP headers.
  96       *
  97       * @var string
  98       * @access private
  99       */
 100      public $version = '0.7.3';
 101      /**
 102       * CVS revision for HTTP headers.
 103       *
 104       * @var string
 105       * @access private
 106       */
 107      public $revision = '$Revision: 1.114 $';
 108      /**
 109       * Current error string (manipulated by getError/setError)
 110       *
 111       * @var string
 112       * @access private
 113       */
 114      public $error_str = '';
 115      /**
 116       * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
 117       *
 118       * @var string
 119       * @access private
 120       */
 121      public $debug_str = '';
 122      /**
 123       * toggles automatic encoding of special characters as entities
 124       * (should always be true, I think)
 125       *
 126       * @var boolean
 127       * @access private
 128       */
 129      public $charencoding = true;
 130      /**
 131       * the debug level for this instance
 132       *
 133       * @var    integer
 134       * @access private
 135       */
 136      public $debugLevel;
 137  
 138      /**
 139      * set schema version
 140      *
 141      * @var      string
 142      * @access   public
 143      */
 144      public $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
 145  
 146      /**
 147      * charset encoding for outgoing messages
 148      *
 149      * @var      string
 150      * @access   public
 151      */
 152      public $soap_defencoding = 'ISO-8859-1';
 153      //var $soap_defencoding = 'UTF-8';
 154  
 155      /**
 156      * namespaces in an array of prefix => uri
 157      *
 158      * this is "seeded" by a set of constants, but it may be altered by code
 159      *
 160      * @var      array
 161      * @access   public
 162      */
 163      public $namespaces = array(
 164          'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
 165          'xsd' => 'http://www.w3.org/2001/XMLSchema',
 166          'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
 167          'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
 168          );
 169  
 170      /**
 171      * namespaces used in the current context, e.g. during serialization
 172      *
 173      * @var      array
 174      * @access   private
 175      */
 176      public $usedNamespaces = array();
 177  
 178      /**
 179      * XML Schema types in an array of uri => (array of xml type => php type)
 180      * is this legacy yet?
 181      * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
 182      * @var      array
 183      * @access   public
 184      */
 185      public $typemap = array(
 186      'http://www.w3.org/2001/XMLSchema' => array(
 187          'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
 188          'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
 189          'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
 190          // abstract "any" types
 191          'anyType'=>'string','anySimpleType'=>'string',
 192          // derived datatypes
 193          'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
 194          'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
 195          'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
 196          'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
 197      'http://www.w3.org/2000/10/XMLSchema' => array(
 198          'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
 199          'float'=>'double','dateTime'=>'string',
 200          'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
 201      'http://www.w3.org/1999/XMLSchema' => array(
 202          'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
 203          'float'=>'double','dateTime'=>'string',
 204          'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
 205      'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
 206      'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
 207      'http://xml.apache.org/xml-soap' => array('Map')
 208      );
 209  
 210      /**
 211      * XML entities to convert
 212      *
 213      * @var      array
 214      * @access   public
 215      * @deprecated
 216      * @see    expandEntities
 217      */
 218      public $xmlEntities = array('quot' => '"','amp' => '&',
 219          'lt' => '<','gt' => '>','apos' => "'");
 220  
 221      /**
 222      * constructor
 223      *
 224      * @access    public
 225      */
 226  	function nusoap_base() {
 227          $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
 228      }
 229  
 230      /**
 231      * gets the global debug level, which applies to future instances
 232      *
 233      * @return    integer    Debug level 0-9, where 0 turns off
 234      * @access    public
 235      */
 236  	function getGlobalDebugLevel() {
 237          return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
 238      }
 239  
 240      /**
 241      * sets the global debug level, which applies to future instances
 242      *
 243      * @param    int    $level    Debug level 0-9, where 0 turns off
 244      * @access    public
 245      */
 246  	function setGlobalDebugLevel($level) {
 247          $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
 248      }
 249  
 250      /**
 251      * gets the debug level for this instance
 252      *
 253      * @return    int    Debug level 0-9, where 0 turns off
 254      * @access    public
 255      */
 256  	function getDebugLevel() {
 257          return $this->debugLevel;
 258      }
 259  
 260      /**
 261      * sets the debug level for this instance
 262      *
 263      * @param    int    $level    Debug level 0-9, where 0 turns off
 264      * @access    public
 265      */
 266  	function setDebugLevel($level) {
 267          $this->debugLevel = $level;
 268      }
 269  
 270      /**
 271      * adds debug data to the instance debug string with formatting
 272      *
 273      * @param    string $string debug data
 274      * @access   private
 275      */
 276  	function debug($string){
 277          if ($this->debugLevel > 0) {
 278              $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
 279          }
 280      }
 281  
 282      /**
 283      * adds debug data to the instance debug string without formatting
 284      *
 285      * @param    string $string debug data
 286      * @access   public
 287      */
 288  	function appendDebug($string){
 289          if ($this->debugLevel > 0) {
 290              // it would be nice to use a memory stream here to use
 291              // memory more efficiently
 292              $this->debug_str .= $string;
 293          }
 294      }
 295  
 296      /**
 297      * clears the current debug data for this instance
 298      *
 299      * @access   public
 300      */
 301  	function clearDebug() {
 302          // it would be nice to use a memory stream here to use
 303          // memory more efficiently
 304          $this->debug_str = '';
 305      }
 306  
 307      /**
 308      * gets the current debug data for this instance
 309      *
 310      * @return   debug data
 311      * @access   public
 312      */
 313      function &getDebug() {
 314          // it would be nice to use a memory stream here to use
 315          // memory more efficiently
 316          return $this->debug_str;
 317      }
 318  
 319      /**
 320      * gets the current debug data for this instance as an XML comment
 321      * this may change the contents of the debug data
 322      *
 323      * @return   debug data as an XML comment
 324      * @access   public
 325      */
 326      function &getDebugAsXMLComment() {
 327          // it would be nice to use a memory stream here to use
 328          // memory more efficiently
 329          while (strpos($this->debug_str, '--')) {
 330              $this->debug_str = str_replace('--', '- -', $this->debug_str);
 331          }
 332          $ret = "<!--\n" . $this->debug_str . "\n-->";
 333          return $ret;
 334      }
 335  
 336      /**
 337      * expands entities, e.g. changes '<' to '&lt;'.
 338      *
 339      * @param    string    $val    The string in which to expand entities.
 340      * @access    private
 341      */
 342  	function expandEntities($val) {
 343          if ($this->charencoding) {
 344              $val = str_replace('&', '&amp;', $val);
 345              $val = str_replace("'", '&apos;', $val);
 346              $val = str_replace('"', '&quot;', $val);
 347              $val = str_replace('<', '&lt;', $val);
 348              $val = str_replace('>', '&gt;', $val);
 349          }
 350          return $val;
 351      }
 352  
 353      /**
 354      * returns error string if present
 355      *
 356      * @return   mixed error string or false
 357      * @access   public
 358      */
 359  	function getError(){
 360          if($this->error_str != ''){
 361              return $this->error_str;
 362          }
 363          return false;
 364      }
 365  
 366      /**
 367      * sets error string
 368      *
 369      * @return   boolean $string error string
 370      * @access   private
 371      */
 372  	function setError($str){
 373          $this->error_str = $str;
 374      }
 375  
 376      /**
 377      * detect if array is a simple array or a struct (associative array)
 378      *
 379      * @param    mixed    $val    The PHP array
 380      * @return    string    (arraySimple|arrayStruct)
 381      * @access    private
 382      */
 383  	function isArraySimpleOrStruct($val) {
 384          $keyList = array_keys($val);
 385          foreach ($keyList as $keyListValue) {
 386              if (!is_int($keyListValue)) {
 387                  return 'arrayStruct';
 388              }
 389          }
 390          return 'arraySimple';
 391      }
 392  
 393      /**
 394      * serializes PHP values in accordance w/ section 5. Type information is
 395      * not serialized if $use == 'literal'.
 396      *
 397      * @param    mixed    $val    The value to serialize
 398      * @param    string    $name    The name (local part) of the XML element
 399      * @param    string    $type    The XML schema type (local part) for the element
 400      * @param    string    $name_ns    The namespace for the name of the XML element
 401      * @param    string    $type_ns    The namespace for the type of the element
 402      * @param    array    $attributes    The attributes to serialize as name=>value pairs
 403      * @param    string    $use    The WSDL "use" (encoded|literal)
 404      * @param    boolean    $soapval    Whether this is called from soapval.
 405      * @return    string    The serialized element, possibly with child elements
 406      * @access    public
 407      */
 408  	function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
 409          $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
 410          $this->appendDebug('value=' . $this->varDump($val));
 411          $this->appendDebug('attributes=' . $this->varDump($attributes));
 412  
 413          if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
 414              $this->debug("serialize_val: serialize soapval");
 415              $xml = $val->serialize($use);
 416              $this->appendDebug($val->getDebug());
 417              $val->clearDebug();
 418              $this->debug("serialize_val of soapval returning $xml");
 419              return $xml;
 420          }
 421          // force valid name if necessary
 422          if (is_numeric($name)) {
 423              $name = '__numeric_' . $name;
 424          } elseif (! $name) {
 425              $name = 'noname';
 426          }
 427          // if name has ns, add ns prefix to name
 428          $xmlns = '';
 429          if($name_ns){
 430              $prefix = 'nu'.rand(1000,9999);
 431              $name = $prefix.':'.$name;
 432              $xmlns .= " xmlns:$prefix=\"$name_ns\"";
 433          }
 434          // if type is prefixed, create type prefix
 435          if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
 436              // need to fix this. shouldn't default to xsd if no ns specified
 437              // w/o checking against typemap
 438              $type_prefix = 'xsd';
 439          } elseif($type_ns){
 440              $type_prefix = 'ns'.rand(1000,9999);
 441              $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
 442          }
 443          // serialize attributes if present
 444          $atts = '';
 445          if($attributes){
 446              foreach($attributes as $k => $v){
 447                  $atts .= " $k=\"".$this->expandEntities($v).'"';
 448              }
 449          }
 450          // serialize null value
 451          if (is_null($val)) {
 452              $this->debug("serialize_val: serialize null");
 453              if ($use == 'literal') {
 454                  // TODO: depends on minOccurs
 455                  $xml = "<$name$xmlns$atts/>";
 456                  $this->debug("serialize_val returning $xml");
 457                  return $xml;
 458              } else {
 459                  if (isset($type) && isset($type_prefix)) {
 460                      $type_str = " xsi:type=\"$type_prefix:$type\"";
 461                  } else {
 462                      $type_str = '';
 463                  }
 464                  $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
 465                  $this->debug("serialize_val returning $xml");
 466                  return $xml;
 467              }
 468          }
 469          // serialize if an xsd built-in primitive type
 470          if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
 471              $this->debug("serialize_val: serialize xsd built-in primitive type");
 472              if (is_bool($val)) {
 473                  if ($type == 'boolean') {
 474                      $val = $val ? 'true' : 'false';
 475                  } elseif (! $val) {
 476                      $val = 0;
 477                  }
 478              } else if (is_string($val)) {
 479                  $val = $this->expandEntities($val);
 480              }
 481              if ($use == 'literal') {
 482                  $xml = "<$name$xmlns$atts>$val</$name>";
 483                  $this->debug("serialize_val returning $xml");
 484                  return $xml;
 485              } else {
 486                  $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
 487                  $this->debug("serialize_val returning $xml");
 488                  return $xml;
 489              }
 490          }
 491          // detect type and serialize
 492          $xml = '';
 493          switch(true) {
 494              case (is_bool($val) || $type == 'boolean'):
 495                     $this->debug("serialize_val: serialize boolean");
 496                  if ($type == 'boolean') {
 497                      $val = $val ? 'true' : 'false';
 498                  } elseif (! $val) {
 499                      $val = 0;
 500                  }
 501                  if ($use == 'literal') {
 502                      $xml .= "<$name$xmlns$atts>$val</$name>";
 503                  } else {
 504                      $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
 505                  }
 506                  break;
 507              case (is_int($val) || is_long($val) || $type == 'int'):
 508                     $this->debug("serialize_val: serialize int");
 509                  if ($use == 'literal') {
 510                      $xml .= "<$name$xmlns$atts>$val</$name>";
 511                  } else {
 512                      $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
 513                  }
 514                  break;
 515              case (is_float($val)|| is_double($val) || $type == 'float'):
 516                     $this->debug("serialize_val: serialize float");
 517                  if ($use == 'literal') {
 518                      $xml .= "<$name$xmlns$atts>$val</$name>";
 519                  } else {
 520                      $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
 521                  }
 522                  break;
 523              case (is_string($val) || $type == 'string'):
 524                     $this->debug("serialize_val: serialize string");
 525                  $val = $this->expandEntities($val);
 526                  if ($use == 'literal') {
 527                      $xml .= "<$name$xmlns$atts>$val</$name>";
 528                  } else {
 529                      $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
 530                  }
 531                  break;
 532              case is_object($val):
 533                     $this->debug("serialize_val: serialize object");
 534                  if (get_class($val) == 'soapval') {
 535                      $this->debug("serialize_val: serialize soapval object");
 536                      $pXml = $val->serialize($use);
 537                      $this->appendDebug($val->getDebug());
 538                      $val->clearDebug();
 539                  } else {
 540                      if (! $name) {
 541                          $name = get_class($val);
 542                          $this->debug("In serialize_val, used class name $name as element name");
 543                      } else {
 544                          $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
 545                      }
 546                      foreach(get_object_vars($val) as $k => $v){
 547                          $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
 548                      }
 549                  }
 550                  if(isset($type) && isset($type_prefix)){
 551                      $type_str = " xsi:type=\"$type_prefix:$type\"";
 552                  } else {
 553                      $type_str = '';
 554                  }
 555                  if ($use == 'literal') {
 556                      $xml .= "<$name$xmlns$atts>$pXml</$name>";
 557                  } else {
 558                      $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
 559                  }
 560                  break;
 561              break;
 562              case (is_array($val) || $type):
 563                  // detect if struct or array
 564                  $valueType = $this->isArraySimpleOrStruct($val);
 565                  if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
 566                         $this->debug("serialize_val: serialize array");
 567                      $i = 0;
 568                      if(is_array($val) && count($val)> 0){
 569                          foreach($val as $v){
 570                              if(is_object($v) && get_class($v) ==  'soapval'){
 571                                  $tt_ns = $v->type_ns;
 572                                  $tt = $v->type;
 573                              } elseif (is_array($v)) {
 574                                  $tt = $this->isArraySimpleOrStruct($v);
 575                              } else {
 576                                  $tt = gettype($v);
 577                              }
 578                              $array_types[$tt] = 1;
 579                              // TODO: for literal, the name should be $name
 580                              $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
 581                              ++$i;
 582                          }
 583                          if(count($array_types) > 1){
 584                              $array_typename = 'xsd:anyType';
 585                          } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
 586                              if ($tt == 'integer') {
 587                                  $tt = 'int';
 588                              }
 589                              $array_typename = 'xsd:'.$tt;
 590                          } elseif(isset($tt) && $tt == 'arraySimple'){
 591                              $array_typename = 'SOAP-ENC:Array';
 592                          } elseif(isset($tt) && $tt == 'arrayStruct'){
 593                              $array_typename = 'unnamed_struct_use_soapval';
 594                          } else {
 595                              // if type is prefixed, create type prefix
 596                              if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
 597                                   $array_typename = 'xsd:' . $tt;
 598                              } elseif ($tt_ns) {
 599                                  $tt_prefix = 'ns' . rand(1000, 9999);
 600                                  $array_typename = "$tt_prefix:$tt";
 601                                  $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
 602                              } else {
 603                                  $array_typename = $tt;
 604                              }
 605                          }
 606                          $array_type = $i;
 607                          if ($use == 'literal') {
 608                              $type_str = '';
 609                          } else if (isset($type) && isset($type_prefix)) {
 610                              $type_str = " xsi:type=\"$type_prefix:$type\"";
 611                          } else {
 612                              $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
 613                          }
 614                      // empty array
 615                      } else {
 616                          if ($use == 'literal') {
 617                              $type_str = '';
 618                          } else if (isset($type) && isset($type_prefix)) {
 619                              $type_str = " xsi:type=\"$type_prefix:$type\"";
 620                          } else {
 621                              $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
 622                          }
 623                      }
 624                      // TODO: for array in literal, there is no wrapper here
 625                      $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
 626                  } else {
 627                      // got a struct
 628                         $this->debug("serialize_val: serialize struct");
 629                      if(isset($type) && isset($type_prefix)){
 630                          $type_str = " xsi:type=\"$type_prefix:$type\"";
 631                      } else {
 632                          $type_str = '';
 633                      }
 634                      if ($use == 'literal') {
 635                          $xml .= "<$name$xmlns$atts>";
 636                      } else {
 637                          $xml .= "<$name$xmlns$type_str$atts>";
 638                      }
 639                      foreach($val as $k => $v){
 640                          // Apache Map
 641                          if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
 642                              $xml .= '<item>';
 643                              $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
 644                              $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
 645                              $xml .= '</item>';
 646                          } else {
 647                              $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
 648                          }
 649                      }
 650                      $xml .= "</$name>";
 651                  }
 652                  break;
 653              default:
 654                     $this->debug("serialize_val: serialize unknown");
 655                  $xml .= 'not detected, got '.gettype($val).' for '.$val;
 656                  break;
 657          }
 658          $this->debug("serialize_val returning $xml");
 659          return $xml;
 660      }
 661  
 662      /**
 663      * serializes a message
 664      *
 665      * @param string $body the XML of the SOAP body
 666      * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
 667      * @param array $namespaces optional the namespaces used in generating the body and headers
 668      * @param string $style optional (rpc|document)
 669      * @param string $use optional (encoded|literal)
 670      * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
 671      * @return string the message
 672      * @access public
 673      */
 674      function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
 675      // TODO: add an option to automatically run utf8_encode on $body and $headers
 676      // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
 677      // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
 678  
 679      $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
 680      $this->debug("headers:");
 681      $this->appendDebug($this->varDump($headers));
 682      $this->debug("namespaces:");
 683      $this->appendDebug($this->varDump($namespaces));
 684  
 685      // serialize namespaces
 686      $ns_string = '';
 687      foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
 688          $ns_string .= " xmlns:$k=\"$v\"";
 689      }
 690      if($encodingStyle) {
 691          $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
 692      }
 693  
 694      // serialize headers
 695      if($headers){
 696          if (is_array($headers)) {
 697              $xml = '';
 698              foreach ($headers as $k => $v) {
 699                  if (is_object($v) && get_class($v) == 'soapval') {
 700                      $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
 701                  } else {
 702                      $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
 703                  }
 704              }
 705              $headers = $xml;
 706              $this->debug("In serializeEnvelope, serialized array of headers to $headers");
 707          }
 708          $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
 709      }
 710      // serialize envelope
 711      return
 712      '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
 713      '<SOAP-ENV:Envelope'.$ns_string.">".
 714      $headers.
 715      "<SOAP-ENV:Body>".
 716          $body.
 717      "</SOAP-ENV:Body>".
 718      "</SOAP-ENV:Envelope>";
 719      }
 720  
 721      /**
 722       * formats a string to be inserted into an HTML stream
 723       *
 724       * @param string $str The string to format
 725       * @return string The formatted string
 726       * @access public
 727       * @deprecated
 728       */
 729      function formatDump($str){
 730          $str = htmlspecialchars($str);
 731          return nl2br($str);
 732      }
 733  
 734      /**
 735      * contracts (changes namespace to prefix) a qualified name
 736      *
 737      * @param    string $qname qname
 738      * @return    string contracted qname
 739      * @access   private
 740      */
 741  	function contractQname($qname){
 742          // get element namespace
 743          //$this->xdebug("Contract $qname");
 744          if (strrpos($qname, ':')) {
 745              // get unqualified name
 746              $name = substr($qname, strrpos($qname, ':') + 1);
 747              // get ns
 748              $ns = substr($qname, 0, strrpos($qname, ':'));
 749              $p = $this->getPrefixFromNamespace($ns);
 750              if ($p) {
 751                  return $p . ':' . $name;
 752              }
 753              return $qname;
 754          } else {
 755              return $qname;
 756          }
 757      }
 758  
 759      /**
 760      * expands (changes prefix to namespace) a qualified name
 761      *
 762      * @param    string $qname qname
 763      * @return    string expanded qname
 764      * @access   private
 765      */
 766  	function expandQname($qname){
 767          // get element prefix
 768          if(strpos($qname,':') && !ereg('^http://',$qname)){
 769              // get unqualified name
 770              $name = substr(strstr($qname,':'),1);
 771              // get ns prefix
 772              $prefix = substr($qname,0,strpos($qname,':'));
 773              if(isset($this->namespaces[$prefix])){
 774                  return $this->namespaces[$prefix].':'.$name;
 775              } else {
 776                  return $qname;
 777              }
 778          } else {
 779              return $qname;
 780          }
 781      }
 782  
 783      /**
 784      * returns the local part of a prefixed string
 785      * returns the original string, if not prefixed
 786      *
 787      * @param string $str The prefixed string
 788      * @return string The local part
 789      * @access public
 790      */
 791  	function getLocalPart($str){
 792          if($sstr = strrchr($str,':')){
 793              // get unqualified name
 794              return substr( $sstr, 1 );
 795          } else {
 796              return $str;
 797          }
 798      }
 799  
 800      /**
 801      * returns the prefix part of a prefixed string
 802      * returns false, if not prefixed
 803      *
 804      * @param string $str The prefixed string
 805      * @return mixed The prefix or false if there is no prefix
 806      * @access public
 807      */
 808  	function getPrefix($str){
 809          if($pos = strrpos($str,':')){
 810              // get prefix
 811              return substr($str,0,$pos);
 812          }
 813          return false;
 814      }
 815  
 816      /**
 817      * pass it a prefix, it returns a namespace
 818      *
 819      * @param string $prefix The prefix
 820      * @return mixed The namespace, false if no namespace has the specified prefix
 821      * @access public
 822      */
 823  	function getNamespaceFromPrefix($prefix){
 824          if (isset($this->namespaces[$prefix])) {
 825              return $this->namespaces[$prefix];
 826          }
 827          //$this->setError("No namespace registered for prefix '$prefix'");
 828          return false;
 829      }
 830  
 831      /**
 832      * returns the prefix for a given namespace (or prefix)
 833      * or false if no prefixes registered for the given namespace
 834      *
 835      * @param string $ns The namespace
 836      * @return mixed The prefix, false if the namespace has no prefixes
 837      * @access public
 838      */
 839  	function getPrefixFromNamespace($ns) {
 840          foreach ($this->namespaces as $p => $n) {
 841              if ($ns == $n || $ns == $p) {
 842                  $this->usedNamespaces[$p] = $n;
 843                  return $p;
 844              }
 845          }
 846          return false;
 847      }
 848  
 849      /**
 850      * returns the time in ODBC canonical form with microseconds
 851      *
 852      * @return string The time in ODBC canonical form with microseconds
 853      * @access public
 854      */
 855  	function getmicrotime() {
 856          if (function_exists('gettimeofday')) {
 857              $tod = gettimeofday();
 858              $sec = $tod['sec'];
 859              $usec = $tod['usec'];
 860          } else {
 861              $sec = time();
 862              $usec = 0;
 863          }
 864          return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
 865      }
 866  
 867      /**
 868       * Returns a string with the output of var_dump
 869       *
 870       * @param mixed $data The variable to var_dump
 871       * @return string The output of var_dump
 872       * @access public
 873       */
 874      function varDump($data) {
 875          ob_start();
 876          var_dump($data);
 877          $ret_val = ob_get_contents();
 878          ob_end_clean();
 879          return $ret_val;
 880      }
 881  
 882      /**
 883      * represents the object as a string
 884      *
 885      * @return    string
 886      * @access   public
 887      */
 888  	function __toString() {
 889          return $this->varDump($this);
 890      }
 891  }
 892  
 893  // XML Schema Datatype Helper Functions
 894  
 895  //xsd:dateTime helpers
 896  
 897  /**
 898  * convert unix timestamp to ISO 8601 compliant date string
 899  *
 900  * @param    string $timestamp Unix time stamp
 901  * @param    boolean $utc Whether the time stamp is UTC or local
 902  * @access   public
 903  */
 904  function timestamp_to_iso8601($timestamp,$utc=true){
 905      $datestr = date('Y-m-d\TH:i:sO',$timestamp);
 906      if($utc){
 907          $eregStr =
 908          '([0-9]{4})-'.    // centuries & years CCYY-
 909          '([0-9]{2})-'.    // months MM-
 910          '([0-9]{2})'.    // days DD
 911          'T'.            // separator T
 912          '([0-9]{2}):'.    // hours hh:
 913          '([0-9]{2}):'.    // minutes mm:
 914          '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
 915          '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
 916  
 917          if(ereg($eregStr,$datestr,$regs)){
 918              return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
 919          }
 920          return false;
 921      } else {
 922          return $datestr;
 923      }
 924  }
 925  
 926  /**
 927  * convert ISO 8601 compliant date string to unix timestamp
 928  *
 929  * @param    string $datestr ISO 8601 compliant date string
 930  * @access   public
 931  */
 932  function iso8601_to_timestamp($datestr){
 933      $eregStr =
 934      '([0-9]{4})-'.    // centuries & years CCYY-
 935      '([0-9]{2})-'.    // months MM-
 936      '([0-9]{2})'.    // days DD
 937      'T'.            // separator T
 938      '([0-9]{2}):'.    // hours hh:
 939      '([0-9]{2}):'.    // minutes mm:
 940      '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
 941      '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
 942      if(ereg($eregStr,$datestr,$regs)){
 943          // not utc
 944          if($regs[8] != 'Z'){
 945              $op = substr($regs[8],0,1);
 946              $h = substr($regs[8],1,2);
 947              $m = substr($regs[8],strlen($regs[8])-2,2);
 948              if($op == '-'){
 949                  $regs[4] = $regs[4] + $h;
 950                  $regs[5] = $regs[5] + $m;
 951              } elseif($op == '+'){
 952                  $regs[4] = $regs[4] - $h;
 953                  $regs[5] = $regs[5] - $m;
 954              }
 955          }
 956          return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 957  //        return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
 958      } else {
 959          return false;
 960      }
 961  }
 962  
 963  /**
 964  * sleeps some number of microseconds
 965  *
 966  * @param    string $usec the number of microseconds to sleep
 967  * @access   public
 968  * @deprecated
 969  */
 970  function usleepWindows($usec)
 971  {
 972      $start = gettimeofday();
 973  
 974      do
 975      {
 976          $stop = gettimeofday();
 977          $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
 978          + $stop['usec'] - $start['usec'];
 979      }
 980      while ($timePassed < $usec);
 981  }
 982  
 983  ?><?php
 984  
 985  
 986  
 987  /**
 988  * Contains information for a SOAP fault.
 989  * Mainly used for returning faults from deployed functions
 990  * in a server instance.
 991  * @author   Dietrich Ayala <dietrich@ganx4.com>
 992  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
 993  * @access public
 994  */
 995  class nusoap_fault extends nusoap_base {
 996      /**
 997       * The fault code (client|server)
 998       * @var string
 999       * @access private
1000       */
1001      public $faultcode;
1002      /**
1003       * The fault actor
1004       * @var string
1005       * @access private
1006       */
1007      public $faultactor;
1008      /**
1009       * The fault string, a description of the fault
1010       * @var string
1011       * @access private
1012       */
1013      public $faultstring;
1014      /**
1015       * The fault detail, typically a string or array of string
1016       * @var mixed
1017       * @access private
1018       */
1019      public $faultdetail;
1020  
1021      /**
1022      * constructor
1023      *
1024      * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1025      * @param string $faultactor only used when msg routed between multiple actors
1026      * @param string $faultstring human readable error message
1027      * @param mixed $faultdetail detail, typically a string or array of string
1028      */
1029  	function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1030          parent::nusoap_base();
1031          $this->faultcode = $faultcode;
1032          $this->faultactor = $faultactor;
1033          $this->faultstring = $faultstring;
1034          $this->faultdetail = $faultdetail;
1035      }
1036  
1037      /**
1038      * serialize a fault
1039      *
1040      * @return    string    The serialization of the fault instance.
1041      * @access   public
1042      */
1043  	function serialize(){
1044          $ns_string = '';
1045          foreach($this->namespaces as $k => $v){
1046              $ns_string .= "\n  xmlns:$k=\"$v\"";
1047          }
1048          $return_msg =
1049              '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1050              '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1051                  '<SOAP-ENV:Body>'.
1052                  '<SOAP-ENV:Fault>'.
1053                      $this->serialize_val($this->faultcode, 'faultcode').
1054                      $this->serialize_val($this->faultactor, 'faultactor').
1055                      $this->serialize_val($this->faultstring, 'faultstring').
1056                      $this->serialize_val($this->faultdetail, 'detail').
1057                  '</SOAP-ENV:Fault>'.
1058                  '</SOAP-ENV:Body>'.
1059              '</SOAP-ENV:Envelope>';
1060          return $return_msg;
1061      }
1062  }
1063  
1064  /**
1065   * Backward compatibility
1066   */
1067  class soap_fault extends nusoap_fault {
1068  }
1069  
1070  ?><?php
1071  
1072  
1073  
1074  /**
1075  * parses an XML Schema, allows access to it's data, other utility methods.
1076  * imperfect, no validation... yet, but quite functional.
1077  *
1078  * @author   Dietrich Ayala <dietrich@ganx4.com>
1079  * @author   Scott Nichol <snichol@users.sourceforge.net>
1080  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
1081  * @access   public
1082  */
1083  class nusoap_xmlschema extends nusoap_base  {
1084  
1085      // files
1086      public $schema = '';
1087      public $xml = '';
1088      // namespaces
1089      public $enclosingNamespaces;
1090      // schema info
1091      public $schemaInfo = array();
1092      public $schemaTargetNamespace = '';
1093      // types, elements, attributes defined by the schema
1094      public $attributes = array();
1095      public $complexTypes = array();
1096      public $complexTypeStack = array();
1097      public $currentComplexType = null;
1098      public $elements = array();
1099      public $elementStack = array();
1100      public $currentElement = null;
1101      public $simpleTypes = array();
1102      public $simpleTypeStack = array();
1103      public $currentSimpleType = null;
1104      // imports
1105      public $imports = array();
1106      // parser vars
1107      public $parser;
1108      public $position = 0;
1109      public $depth = 0;
1110      public $depth_array = array();
1111      public $message = array();
1112      public $defaultNamespace = array();
1113  
1114      /**
1115      * constructor
1116      *
1117      * @param    string $schema schema document URI
1118      * @param    string $xml xml document URI
1119      * @param    string $namespaces namespaces defined in enclosing XML
1120      * @access   public
1121      */
1122  	function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1123          parent::nusoap_base();
1124          $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1125          // files
1126          $this->schema = $schema;
1127          $this->xml = $xml;
1128  
1129          // namespaces
1130          $this->enclosingNamespaces = $namespaces;
1131          $this->namespaces = array_merge($this->namespaces, $namespaces);
1132  
1133          // parse schema file
1134          if($schema != ''){
1135              $this->debug('initial schema file: '.$schema);
1136              $this->parseFile($schema, 'schema');
1137          }
1138  
1139          // parse xml file
1140          if($xml != ''){
1141              $this->debug('initial xml file: '.$xml);
1142              $this->parseFile($xml, 'xml');
1143          }
1144  
1145      }
1146  
1147      /**
1148      * parse an XML file
1149      *
1150      * @param string $xml path/URL to XML file
1151      * @param string $type (schema | xml)
1152      * @return boolean
1153      * @access public
1154      */
1155  	function parseFile($xml,$type){
1156          // parse xml file
1157          if($xml != ""){
1158              $xmlStr = @join("",@file($xml));
1159              if($xmlStr == ""){
1160                  $msg = 'Error reading XML from '.$xml;
1161                  $this->setError($msg);
1162                  $this->debug($msg);
1163              return false;
1164              } else {
1165                  $this->debug("parsing $xml");
1166                  $this->parseString($xmlStr,$type);
1167                  $this->debug("done parsing $xml");
1168              return true;
1169              }
1170          }
1171          return false;
1172      }
1173  
1174      /**
1175      * parse an XML string
1176      *
1177      * @param    string $xml path or URL
1178      * @param    string $type (schema|xml)
1179      * @access   private
1180      */
1181  	function parseString($xml,$type){
1182          // parse xml string
1183          if($xml != ""){
1184  
1185              // Create an XML parser.
1186              $this->parser = xml_parser_create();
1187              // Set the options for parsing the XML data.
1188              xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1189  
1190              // Set the object for the parser.
1191              xml_set_object($this->parser, $this);
1192  
1193              // Set the element handlers for the parser.
1194              if($type == "schema"){
1195                  xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1196                  xml_set_character_data_handler($this->parser,'schemaCharacterData');
1197              } elseif($type == "xml"){
1198                  xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1199                  xml_set_character_data_handler($this->parser,'xmlCharacterData');
1200              }
1201  
1202              // Parse the XML file.
1203              if(!xml_parse($this->parser,$xml,true)){
1204              // Display an error message.
1205                  $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1206                  xml_get_current_line_number($this->parser),
1207                  xml_error_string(xml_get_error_code($this->parser))
1208                  );
1209                  $this->debug($errstr);
1210                  $this->debug("XML payload:\n" . $xml);
1211                  $this->setError($errstr);
1212              }
1213  
1214              xml_parser_free($this->parser);
1215          } else{
1216              $this->debug('no xml passed to parseString()!!');
1217              $this->setError('no xml passed to parseString()!!');
1218          }
1219      }
1220  
1221      /**
1222       * gets a type name for an unnamed type
1223       *
1224       * @param    string    Element name
1225       * @return    string    A type name for an unnamed type
1226       * @access    private
1227       */
1228  	function CreateTypeName($ename) {
1229          $scope = '';
1230          for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1231              $scope .= $this->complexTypeStack[$i] . '_';
1232          }
1233          return $scope . $ename . '_ContainedType';
1234      }
1235  
1236      /**
1237      * start-element handler
1238      *
1239      * @param    string $parser XML parser object
1240      * @param    string $name element name
1241      * @param    string $attrs associative array of attributes
1242      * @access   private
1243      */
1244  	function schemaStartElement($parser, $name, $attrs) {
1245  
1246          // position in the total number of elements, starting from 0
1247          $pos = $this->position++;
1248          $depth = $this->depth++;
1249          // set self as current value for this depth
1250          $this->depth_array[$depth] = $pos;
1251          $this->message[$pos] = array('cdata' => '');
1252          if ($depth > 0) {
1253              $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1254          } else {
1255              $this->defaultNamespace[$pos] = false;
1256          }
1257  
1258          // get element prefix
1259          if($prefix = $this->getPrefix($name)){
1260              // get unqualified name
1261              $name = $this->getLocalPart($name);
1262          } else {
1263              $prefix = '';
1264          }
1265  
1266          // loop thru attributes, expanding, and registering namespace declarations
1267          if(count($attrs) > 0){
1268              foreach($attrs as $k => $v){
1269                  // if ns declarations, add to class level array of valid namespaces
1270                  if(ereg("^xmlns",$k)){
1271                      //$this->xdebug("$k: $v");
1272                      //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1273                      if($ns_prefix = substr(strrchr($k,':'),1)){
1274                          //$this->xdebug("Add namespace[$ns_prefix] = $v");
1275                          $this->namespaces[$ns_prefix] = $v;
1276                      } else {
1277                          $this->defaultNamespace[$pos] = $v;
1278                          if (! $this->getPrefixFromNamespace($v)) {
1279                              $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1280                          }
1281                      }
1282                      if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1283                          $this->XMLSchemaVersion = $v;
1284                          $this->namespaces['xsi'] = $v.'-instance';
1285                      }
1286                  }
1287              }
1288              foreach($attrs as $k => $v){
1289                  // expand each attribute
1290                  $k = strpos($k,':') ? $this->expandQname($k) : $k;
1291                  $v = strpos($v,':') ? $this->expandQname($v) : $v;
1292                  $eAttrs[$k] = $v;
1293              }
1294              $attrs = $eAttrs;
1295          } else {
1296              $attrs = array();
1297          }
1298          // find status, register data
1299          switch($name){
1300              case 'all':            // (optional) compositor content for a complexType
1301              case 'choice':
1302              case 'group':
1303              case 'sequence':
1304                  //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1305                  $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1306                  //if($name == 'all' || $name == 'sequence'){
1307                  //    $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1308                  //}
1309              break;
1310              case 'attribute':    // complexType attribute
1311                  //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1312                  $this->xdebug("parsing attribute:");
1313                  $this->appendDebug($this->varDump($attrs));
1314                  if (!isset($attrs['form'])) {
1315                      $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1316                  }
1317                  if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1318                      $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1319                      if (!strpos($v, ':')) {
1320                          // no namespace in arrayType attribute value...
1321                          if ($this->defaultNamespace[$pos]) {
1322                              // ...so use the default
1323                              $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1324                          }
1325                      }
1326                  }
1327                  if(isset($attrs['name'])){
1328                      $this->attributes[$attrs['name']] = $attrs;
1329                      $aname = $attrs['name'];
1330                  } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1331                      if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1332                          $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1333                      } else {
1334                          $aname = '';
1335                      }
1336                  } elseif(isset($attrs['ref'])){
1337                      $aname = $attrs['ref'];
1338                      $this->attributes[$attrs['ref']] = $attrs;
1339                  }
1340  
1341                  if($this->currentComplexType){    // This should *always* be
1342                      $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1343                  }
1344                  // arrayType attribute
1345                  if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1346                      $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1347                      $prefix = $this->getPrefix($aname);
1348                      if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1349                          $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1350                      } else {
1351                          $v = '';
1352                      }
1353                      if(strpos($v,'[,]')){
1354                          $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1355                      }
1356                      $v = substr($v,0,strpos($v,'[')); // clip the []
1357                      if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1358                          $v = $this->XMLSchemaVersion.':'.$v;
1359                      }
1360                      $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1361                  }
1362              break;
1363              case 'complexContent':    // (optional) content for a complexType
1364              break;
1365              case 'complexType':
1366                  array_push($this->complexTypeStack, $this->currentComplexType);
1367                  if(isset($attrs['name'])){
1368                      // TODO: what is the scope of named complexTypes that appear
1369                      //       nested within other c complexTypes?
1370                      $this->xdebug('processing named complexType '.$attrs['name']);
1371                      //$this->currentElement = false;
1372                      $this->currentComplexType = $attrs['name'];
1373                      $this->complexTypes[$this->currentComplexType] = $attrs;
1374                      $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1375                      // This is for constructs like
1376                      //           <complexType name="ListOfString" base="soap:Array">
1377                      //                <sequence>
1378                      //                    <element name="string" type="xsd:string"
1379                      //                        minOccurs="0" maxOccurs="unbounded" />
1380                      //                </sequence>
1381                      //            </complexType>
1382                      if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1383                          $this->xdebug('complexType is unusual array');
1384                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1385                      } else {
1386                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1387                      }
1388                  } else {
1389                      $name = $this->CreateTypeName($this->currentElement);
1390                      $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1391                      $this->currentComplexType = $name;
1392                      //$this->currentElement = false;
1393                      $this->complexTypes[$this->currentComplexType] = $attrs;
1394                      $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1395                      // This is for constructs like
1396                      //           <complexType name="ListOfString" base="soap:Array">
1397                      //                <sequence>
1398                      //                    <element name="string" type="xsd:string"
1399                      //                        minOccurs="0" maxOccurs="unbounded" />
1400                      //                </sequence>
1401                      //            </complexType>
1402                      if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1403                          $this->xdebug('complexType is unusual array');
1404                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1405                      } else {
1406                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1407                      }
1408                  }
1409              break;
1410              case 'element':
1411                  array_push($this->elementStack, $this->currentElement);
1412                  if (!isset($attrs['form'])) {
1413                      $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1414                  }
1415                  if(isset($attrs['type'])){
1416                      $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1417                      if (! $this->getPrefix($attrs['type'])) {
1418                          if ($this->defaultNamespace[$pos]) {
1419                              $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1420                              $this->xdebug('used default namespace to make type ' . $attrs['type']);
1421                          }
1422                      }
1423                      // This is for constructs like
1424                      //           <complexType name="ListOfString" base="soap:Array">
1425                      //                <sequence>
1426                      //                    <element name="string" type="xsd:string"
1427                      //                        minOccurs="0" maxOccurs="unbounded" />
1428                      //                </sequence>
1429                      //            </complexType>
1430                      if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1431                          $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1432                          $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1433                      }
1434                      $this->currentElement = $attrs['name'];
1435                      $ename = $attrs['name'];
1436                  } elseif(isset($attrs['ref'])){
1437                      $this->xdebug("processing element as ref to ".$attrs['ref']);
1438                      $this->currentElement = "ref to ".$attrs['ref'];
1439                      $ename = $this->getLocalPart($attrs['ref']);
1440                  } else {
1441                      $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1442                      $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1443                      $this->currentElement = $attrs['name'];
1444                      $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1445                      $ename = $attrs['name'];
1446                  }
1447                  if (isset($ename) && $this->currentComplexType) {
1448                      $this->xdebug("add element $ename to complexType $this->currentComplexType");
1449                      $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1450                  } elseif (!isset($attrs['ref'])) {
1451                      $this->xdebug("add element $ename to elements array");
1452                      $this->elements[ $attrs['name'] ] = $attrs;
1453                      $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1454                  }
1455              break;
1456              case 'enumeration':    //    restriction value list member
1457                  $this->xdebug('enumeration ' . $attrs['value']);
1458                  if ($this->currentSimpleType) {
1459                      $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1460                  } elseif ($this->currentComplexType) {
1461                      $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1462                  }
1463              break;
1464              case 'extension':    // simpleContent or complexContent type extension
1465                  $this->xdebug('extension ' . $attrs['base']);
1466                  if ($this->currentComplexType) {
1467                      $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1468                  }
1469              break;
1470              case 'import':
1471                  if (isset($attrs['schemaLocation'])) {
1472                      //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1473                      $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1474                  } else {
1475                      //$this->xdebug('import namespace ' . $attrs['namespace']);
1476                      $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1477                      if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1478                          $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1479                      }
1480                  }
1481              break;
1482              case 'list':    // simpleType value list
1483              break;
1484              case 'restriction':    // simpleType, simpleContent or complexContent value restriction
1485                  $this->xdebug('restriction ' . $attrs['base']);
1486                  if($this->currentSimpleType){
1487                      $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1488                  } elseif($this->currentComplexType){
1489                      $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1490                      if(strstr($attrs['base'],':') == ':Array'){
1491                          $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1492                      }
1493                  }
1494              break;
1495              case 'schema':
1496                  $this->schemaInfo = $attrs;
1497                  $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1498                  if (isset($attrs['targetNamespace'])) {
1499                      $this->schemaTargetNamespace = $attrs['targetNamespace'];
1500                  }
1501                  if (!isset($attrs['elementFormDefault'])) {
1502                      $this->schemaInfo['elementFormDefault'] = 'unqualified';
1503                  }
1504                  if (!isset($attrs['attributeFormDefault'])) {
1505                      $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1506                  }
1507              break;
1508              case 'simpleContent':    // (optional) content for a complexType
1509              break;
1510              case 'simpleType':
1511                  array_push($this->simpleTypeStack, $this->currentSimpleType);
1512                  if(isset($attrs['name'])){
1513                      $this->xdebug("processing simpleType for name " . $attrs['name']);
1514                      $this->currentSimpleType = $attrs['name'];
1515                      $this->simpleTypes[ $attrs['name'] ] = $attrs;
1516                      $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1517                      $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1518                  } else {
1519                      $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1520                      $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1521                      $this->currentSimpleType = $name;
1522                      //$this->currentElement = false;
1523                      $this->simpleTypes[$this->currentSimpleType] = $attrs;
1524                      $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1525                  }
1526              break;
1527              case 'union':    // simpleType type list
1528              break;
1529              default:
1530                  //$this->xdebug("do not have anything to do for element $name");
1531          }
1532      }
1533  
1534      /**
1535      * end-element handler
1536      *
1537      * @param    string $parser XML parser object
1538      * @param    string $name element name
1539      * @access   private
1540      */
1541  	function schemaEndElement($parser, $name) {
1542          // bring depth down a notch
1543          $this->depth--;
1544          // position of current element is equal to the last value left in depth_array for my depth
1545          if(isset($this->depth_array[$this->depth])){
1546              $pos = $this->depth_array[$this->depth];
1547          }
1548          // get element prefix
1549          if ($prefix = $this->getPrefix($name)){
1550              // get unqualified name
1551              $name = $this->getLocalPart($name);
1552          } else {
1553              $prefix = '';
1554          }
1555          // move on...
1556          if($name == 'complexType'){
1557              $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1558              $this->currentComplexType = array_pop($this->complexTypeStack);
1559              //$this->currentElement = false;
1560          }
1561          if($name == 'element'){
1562              $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1563              $this->currentElement = array_pop($this->elementStack);
1564          }
1565          if($name == 'simpleType'){
1566              $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1567              $this->currentSimpleType = array_pop($this->simpleTypeStack);
1568          }
1569      }
1570  
1571      /**
1572      * element content handler
1573      *
1574      * @param    string $parser XML parser object
1575      * @param    string $data element content
1576      * @access   private
1577      */
1578  	function schemaCharacterData($parser, $data){
1579          $pos = $this->depth_array[$this->depth - 1];
1580          $this->message[$pos]['cdata'] .= $data;
1581      }
1582  
1583      /**
1584      * serialize the schema
1585      *
1586      * @access   public
1587      */
1588  	function serializeSchema(){
1589  
1590          $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1591          $xml = '';
1592          // imports
1593          if (sizeof($this->imports) > 0) {
1594              foreach($this->imports as $ns => $list) {
1595                  foreach ($list as $ii) {
1596                      if ($ii['location'] != '') {
1597                          $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1598                      } else {
1599                          $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1600                      }
1601                  }
1602              }
1603          }
1604          // complex types
1605          foreach($this->complexTypes as $typeName => $attrs){
1606              $contentStr = '';
1607              // serialize child elements
1608              if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1609                  foreach($attrs['elements'] as $element => $eParts){
1610                      if(isset($eParts['ref'])){
1611                          $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1612                      } else {
1613                          $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1614                          foreach ($eParts as $aName => $aValue) {
1615                              // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1616                              if ($aName != 'name' && $aName != 'type') {
1617                                  $contentStr .= " $aName=\"$aValue\"";
1618                              }
1619                          }
1620                          $contentStr .= "/>\n";
1621                      }
1622                  }
1623                  // compositor wraps elements
1624                  if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1625                      $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1626                  }
1627              }
1628              // attributes
1629              if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1630                  foreach($attrs['attrs'] as $attr => $aParts){
1631                      $contentStr .= "    <$schemaPrefix:attribute";
1632                      foreach ($aParts as $a => $v) {
1633                          if ($a == 'ref' || $a == 'type') {
1634                              $contentStr .= " $a=\"".$this->contractQName($v).'"';
1635                          } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1636                              $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1637                              $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1638                          } else {
1639                              $contentStr .= " $a=\"$v\"";
1640                          }
1641                      }
1642                      $contentStr .= "/>\n";
1643                  }
1644              }
1645              // if restriction
1646              if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1647                  $contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1648                  // complex or simple content
1649                  if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1650                      $contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1651                  }
1652              }
1653              // finalize complex type
1654              if($contentStr != ''){
1655                  $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1656              } else {
1657                  $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1658              }
1659              $xml .= $contentStr;
1660          }
1661          // simple types
1662          if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1663              foreach($this->simpleTypes as $typeName => $eParts){
1664                  $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1665                  if (isset($eParts['enumeration'])) {
1666                      foreach ($eParts['enumeration'] as $e) {
1667                          $xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1668                      }
1669                  }
1670                  $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1671              }
1672          }
1673          // elements
1674          if(isset($this->elements) && count($this->elements) > 0){
1675              foreach($this->elements as $element => $eParts){
1676                  $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1677              }
1678          }
1679          // attributes
1680          if(isset($this->attributes) && count($this->attributes) > 0){
1681              foreach($this->attributes as $attr => $aParts){
1682                  $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1683              }
1684          }
1685          // finish 'er up
1686          $attr = '';
1687          foreach ($this->schemaInfo as $k => $v) {
1688              if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1689                  $attr .= " $k=\"$v\"";
1690              }
1691          }
1692          $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1693          foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1694              $el .= " xmlns:$nsp=\"$ns\"";
1695          }
1696          $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1697          return $xml;
1698      }
1699  
1700      /**
1701      * adds debug data to the clas level debug string
1702      *
1703      * @param    string $string debug data
1704      * @access   private
1705      */
1706  	function xdebug($string){
1707          $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1708      }
1709  
1710      /**
1711      * get the PHP type of a user defined type in the schema
1712      * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1713      * returns false if no type exists, or not w/ the given namespace
1714      * else returns a string that is either a native php type, or 'struct'
1715      *
1716      * @param string $type name of defined type
1717      * @param string $ns namespace of type
1718      * @return mixed
1719      * @access public
1720      * @deprecated
1721      */
1722  	function getPHPType($type,$ns){
1723          if(isset($this->typemap[$ns][$type])){
1724              //print "found type '$type' and ns $ns in typemap<br>";
1725              return $this->typemap[$ns][$type];
1726          } elseif(isset($this->complexTypes[$type])){
1727              //print "getting type '$type' and ns $ns from complexTypes array<br>";
1728              return $this->complexTypes[$type]['phpType'];
1729          }
1730          return false;
1731      }
1732  
1733      /**
1734      * returns an associative array of information about a given type
1735      * returns false if no type exists by the given name
1736      *
1737      *    For a complexType typeDef = array(
1738      *    'restrictionBase' => '',
1739      *    'phpType' => '',
1740      *    'compositor' => '(sequence|all)',
1741      *    'elements' => array(), // refs to elements array
1742      *    'attrs' => array() // refs to attributes array
1743      *    ... and so on (see addComplexType)
1744      *    )
1745      *
1746      *   For simpleType or element, the array has different keys.
1747      *
1748      * @param string $type
1749      * @return mixed
1750      * @access public
1751      * @see addComplexType
1752      * @see addSimpleType
1753      * @see addElement
1754      */
1755  	function getTypeDef($type){
1756          //$this->debug("in getTypeDef for type $type");
1757          if (substr($type, -1) == '^') {
1758              $is_element = 1;
1759              $type = substr($type, 0, -1);
1760          } else {
1761              $is_element = 0;
1762          }
1763  
1764          if((! $is_element) && isset($this->complexTypes[$type])){
1765              $this->xdebug("in getTypeDef, found complexType $type");
1766              return $this->complexTypes[$type];
1767          } elseif((! $is_element) && isset($this->simpleTypes[$type])){
1768              $this->xdebug("in getTypeDef, found simpleType $type");
1769              if (!isset($this->simpleTypes[$type]['phpType'])) {
1770                  // get info for type to tack onto the simple type
1771                  // TODO: can this ever really apply (i.e. what is a simpleType really?)
1772                  $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1773                  $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1774                  $etype = $this->getTypeDef($uqType);
1775                  if ($etype) {
1776                      $this->xdebug("in getTypeDef, found type for simpleType $type:");
1777                      $this->xdebug($this->varDump($etype));
1778                      if (isset($etype['phpType'])) {
1779                          $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1780                      }
1781                      if (isset($etype['elements'])) {
1782                          $this->simpleTypes[$type]['elements'] = $etype['elements'];
1783                      }
1784                  }
1785              }
1786              return $this->simpleTypes[$type];
1787          } elseif(isset($this->elements[$type])){
1788              $this->xdebug("in getTypeDef, found element $type");
1789              if (!isset($this->elements[$type]['phpType'])) {
1790                  // get info for type to tack onto the element
1791                  $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1792                  $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1793                  $etype = $this->getTypeDef($uqType);
1794                  if ($etype) {
1795                      $this->xdebug("in getTypeDef, found type for element $type:");
1796                      $this->xdebug($this->varDump($etype));
1797                      if (isset($etype['phpType'])) {
1798                          $this->elements[$type]['phpType'] = $etype['phpType'];
1799                      }
1800                      if (isset($etype['elements'])) {
1801                          $this->elements[$type]['elements'] = $etype['elements'];
1802                      }
1803                  } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1804                      $this->xdebug("in getTypeDef, element $type is an XSD type");
1805                      $this->elements[$type]['phpType'] = 'scalar';
1806                  }
1807              }
1808              return $this->elements[$type];
1809          } elseif(isset($this->attributes[$type])){
1810              $this->xdebug("in getTypeDef, found attribute $type");
1811              return $this->attributes[$type];
1812          } elseif (ereg('_ContainedType$', $type)) {
1813              $this->xdebug("in getTypeDef, have an untyped element $type");
1814              $typeDef['typeClass'] = 'simpleType';
1815              $typeDef['phpType'] = 'scalar';
1816              $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1817              return $typeDef;
1818          }
1819          $this->xdebug("in getTypeDef, did not find $type");
1820          return false;
1821      }
1822  
1823      /**
1824      * returns a sample serialization of a given type, or false if no type by the given name
1825      *
1826      * @param string $type name of type
1827      * @return mixed
1828      * @access public
1829      * @deprecated
1830      */
1831      function serializeTypeDef($type){
1832          //print "in sTD() for type $type<br>";
1833      if($typeDef = $this->getTypeDef($type)){
1834          $str .= '<'.$type;
1835          if(is_array($typeDef['attrs'])){
1836          foreach($typeDef['attrs'] as $attName => $data){
1837              $str .= " $attName=\"{type = ".$data['type']."}\"";
1838          }
1839          }
1840          $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1841          if(count($typeDef['elements']) > 0){
1842          $str .= ">";
1843          foreach($typeDef['elements'] as $element => $eData){
1844              $str .= $this->serializeTypeDef($element);
1845          }
1846          $str .= "</$type>";
1847          } elseif($typeDef['typeClass'] == 'element') {
1848          $str .= "></$type>";
1849          } else {
1850          $str .= "/>";
1851          }
1852              return $str;
1853      }
1854          return false;
1855      }
1856  
1857      /**
1858      * returns HTML form elements that allow a user
1859      * to enter values for creating an instance of the given type.
1860      *
1861      * @param string $name name for type instance
1862      * @param string $type name of type
1863      * @return string
1864      * @access public
1865      * @deprecated
1866      */
1867  	function typeToForm($name,$type){
1868          // get typedef
1869          if($typeDef = $this->getTypeDef($type)){
1870              // if struct
1871              if($typeDef['phpType'] == 'struct'){
1872                  $buffer .= '<table>';
1873                  foreach($typeDef['elements'] as $child => $childDef){
1874                      $buffer .= "
1875                      <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1876                      <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1877                  }
1878                  $buffer .= '</table>';
1879              // if array
1880              } elseif($typeDef['phpType'] == 'array'){
1881                  $buffer .= '<table>';
1882                  for($i=0;$i < 3; $i++){
1883                      $buffer .= "
1884                      <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1885                      <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1886                  }
1887                  $buffer .= '</table>';
1888              // if scalar
1889              } else {
1890                  $buffer .= "<input type='text' name='parameters[$name]'>";
1891              }
1892          } else {
1893              $buffer .= "<input type='text' name='parameters[$name]'>";
1894          }
1895          return $buffer;
1896      }
1897  
1898      /**
1899      * adds a complex type to the schema
1900      *
1901      * example: array
1902      *
1903      * addType(
1904      *     'ArrayOfstring',
1905      *     'complexType',
1906      *     'array',
1907      *     '',
1908      *     'SOAP-ENC:Array',
1909      *     array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1910      *     'xsd:string'
1911      * );
1912      *
1913      * example: PHP associative array ( SOAP Struct )
1914      *
1915      * addType(
1916      *     'SOAPStruct',
1917      *     'complexType',
1918      *     'struct',
1919      *     'all',
1920      *     array('myVar'=> array('name'=>'myVar','type'=>'string')
1921      * );
1922      *
1923      * @param name
1924      * @param typeClass (complexType|simpleType|attribute)
1925      * @param phpType: currently supported are array and struct (php assoc array)
1926      * @param compositor (all|sequence|choice)
1927      * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1928      * @param elements = array ( name = array(name=>'',type=>'') )
1929      * @param attrs = array(
1930      *     array(
1931      *        'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1932      *        "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1933      *     )
1934      * )
1935      * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1936      * @access public
1937      * @see getTypeDef
1938      */
1939  	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1940          $this->complexTypes[$name] = array(
1941          'name'        => $name,
1942          'typeClass'    => $typeClass,
1943          'phpType'    => $phpType,
1944          'compositor'=> $compositor,
1945          'restrictionBase' => $restrictionBase,
1946          'elements'    => $elements,
1947          'attrs'        => $attrs,
1948          'arrayType'    => $arrayType
1949          );
1950  
1951          $this->xdebug("addComplexType $name:");
1952          $this->appendDebug($this->varDump($this->complexTypes[$name]));
1953      }
1954  
1955      /**
1956      * adds a simple type to the schema
1957      *
1958      * @param string $name
1959      * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1960      * @param string $typeClass (should always be simpleType)
1961      * @param string $phpType (should always be scalar)
1962      * @param array $enumeration array of values
1963      * @access public
1964      * @see nusoap_xmlschema
1965      * @see getTypeDef
1966      */
1967  	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1968          $this->simpleTypes[$name] = array(
1969          'name'            => $name,
1970          'typeClass'        => $typeClass,
1971          'phpType'        => $phpType,
1972          'type'            => $restrictionBase,
1973          'enumeration'    => $enumeration
1974          );
1975  
1976          $this->xdebug("addSimpleType $name:");
1977          $this->appendDebug($this->varDump($this->simpleTypes[$name]));
1978      }
1979  
1980      /**
1981      * adds an element to the schema
1982      *
1983      * @param array $attrs attributes that must include name and type
1984      * @see nusoap_xmlschema
1985      * @access public
1986      */
1987  	function addElement($attrs) {
1988          if (! $this->getPrefix($attrs['type'])) {
1989              $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1990          }
1991          $this->elements[ $attrs['name'] ] = $attrs;
1992          $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1993  
1994          $this->xdebug("addElement " . $attrs['name']);
1995          $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1996      }
1997  }
1998  
1999  /**
2000   * Backward compatibility
2001   */
2002  class XMLSchema extends nusoap_xmlschema {
2003  }
2004  
2005  ?><?php
2006  
2007  
2008  
2009  /**
2010  * For creating serializable abstractions of native PHP types.  This class
2011  * allows element name/namespace, XSD type, and XML attributes to be
2012  * associated with a value.  This is extremely useful when WSDL is not
2013  * used, but is also useful when WSDL is used with polymorphic types, including
2014  * xsd:anyType and user-defined types.
2015  *
2016  * @author   Dietrich Ayala <dietrich@ganx4.com>
2017  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2018  * @access   public
2019  */
2020  class soapval extends nusoap_base {
2021      /**
2022       * The XML element name
2023       *
2024       * @var string
2025       * @access private
2026       */
2027      public $name;
2028      /**
2029       * The XML type name (string or false)
2030       *
2031       * @var mixed
2032       * @access private
2033       */
2034      public $type;
2035      /**
2036       * The PHP value
2037       *
2038       * @var mixed
2039       * @access private
2040       */
2041      public $value;
2042      /**
2043       * The XML element namespace (string or false)
2044       *
2045       * @var mixed
2046       * @access private
2047       */
2048      public $element_ns;
2049      /**
2050       * The XML type namespace (string or false)
2051       *
2052       * @var mixed
2053       * @access private
2054       */
2055      public $type_ns;
2056      /**
2057       * The XML element attributes (array or false)
2058       *
2059       * @var mixed
2060       * @access private
2061       */
2062      public $attributes;
2063  
2064      /**
2065      * constructor
2066      *
2067      * @param    string $name optional name
2068      * @param    mixed $type optional type name
2069      * @param    mixed $value optional value
2070      * @param    mixed $element_ns optional namespace of value
2071      * @param    mixed $type_ns optional namespace of type
2072      * @param    mixed $attributes associative array of attributes to add to element serialization
2073      * @access   public
2074      */
2075    	function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2076          parent::nusoap_base();
2077          $this->name = $name;
2078          $this->type = $type;
2079          $this->value = $value;
2080          $this->element_ns = $element_ns;
2081          $this->type_ns = $type_ns;
2082          $this->attributes = $attributes;
2083      }
2084  
2085      /**
2086      * return serialized value
2087      *
2088      * @param    string $use The WSDL use value (encoded|literal)
2089      * @return    string XML data
2090      * @access   public
2091      */
2092  	function serialize($use='encoded') {
2093          return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2094      }
2095  
2096      /**
2097      * decodes a soapval object into a PHP native type
2098      *
2099      * @return    mixed
2100      * @access   public
2101      */
2102  	function decode(){
2103          return $this->value;
2104      }
2105  }
2106  
2107  
2108  
2109  ?><?php
2110  
2111  
2112  
2113  /**
2114  * transport class for sending/receiving data via HTTP and HTTPS
2115  * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2116  *
2117  * @author   Dietrich Ayala <dietrich@ganx4.com>
2118  * @author   Scott Nichol <snichol@users.sourceforge.net>
2119  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
2120  * @access public
2121  */
2122  class soap_transport_http extends nusoap_base {
2123  
2124      public $url = '';
2125      public $uri = '';
2126      public $digest_uri = '';
2127      public $scheme = '';
2128      public $host = '';
2129      public $port = '';
2130      public $path = '';
2131      public $request_method = 'POST';
2132      public $protocol_version = '1.0';
2133      public $encoding = '';
2134      public $outgoing_headers = array();
2135      public $incoming_headers = array();
2136      public $incoming_cookies = array();
2137      public $outgoing_payload = '';
2138      public $incoming_payload = '';
2139      public $response_status_line;    // HTTP response status line
2140      public $useSOAPAction = true;
2141      public $persistentConnection = false;
2142      public $ch = false;    // cURL handle
2143      public $ch_options = array();    // cURL custom options
2144      public $use_curl = false;        // force cURL use
2145      public $proxy = null;            // proxy information (associative array)
2146      public $username = '';
2147      public $password = '';
2148      public $authtype = '';
2149      public $digestRequest = array();
2150      public $certRequest = array();    // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2151                                  // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2152                                  // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2153                                  // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2154                                  // passphrase: SSL key password/passphrase
2155                                  // certpassword: SSL certificate password
2156                                  // verifypeer: default is 1
2157                                  // verifyhost: default is 1
2158  
2159      /**
2160      * constructor
2161      *
2162      * @param string $url The URL to which to connect
2163      * @param array $curl_options User-specified cURL options
2164      * @param boolean $use_curl Whether to try to force cURL use
2165      * @access public
2166      */
2167  	function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2168          parent::nusoap_base();
2169          $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2170          $this->appendDebug($this->varDump($curl_options));
2171          $this->setURL($url);
2172          if (is_array($curl_options)) {
2173              $this->ch_options = $curl_options;
2174          }
2175          $this->use_curl = $use_curl;
2176          ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2177          $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2178      }
2179  
2180      /**
2181      * sets a cURL option
2182      *
2183      * @param    mixed $option The cURL option (always integer?)
2184      * @param    mixed $value The cURL option value
2185      * @access   private
2186      */
2187  	function setCurlOption($option, $value) {
2188          $this->debug("setCurlOption option=$option, value=");
2189          $this->appendDebug($this->varDump($value));
2190          curl_setopt($this->ch, $option, $value);
2191      }
2192  
2193      /**
2194      * sets an HTTP header
2195      *
2196      * @param string $name The name of the header
2197      * @param string $value The value of the header
2198      * @access private
2199      */
2200  	function setHeader($name, $value) {
2201          $this->outgoing_headers[$name] = $value;
2202          $this->debug("set header $name: $value");
2203      }
2204  
2205      /**
2206      * unsets an HTTP header
2207      *
2208      * @param string $name The name of the header
2209      * @access private
2210      */
2211  	function unsetHeader($name) {
2212          if (isset($this->outgoing_headers[$name])) {
2213              $this->debug("unset header $name");
2214              unset($this->outgoing_headers[$name]);
2215          }
2216      }
2217  
2218      /**
2219      * sets the URL to which to connect
2220      *
2221      * @param string $url The URL to which to connect
2222      * @access private
2223      */
2224  	function setURL($url) {
2225          $this->url = $url;
2226  
2227          $u = parse_url($url);
2228          foreach($u as $k => $v){
2229              $this->debug("parsed URL $k = $v");
2230              $this->$k = $v;
2231          }
2232  
2233          // add any GET params to path
2234          if(isset($u['query']) && $u['query'] != ''){
2235              $this->path .= '?' . $u['query'];
2236          }
2237  
2238          // set default port
2239          if(!isset($u['port'])){
2240              if($u['scheme'] == 'https'){
2241                  $this->port = 443;
2242              } else {
2243                  $this->port = 80;
2244              }
2245          }
2246  
2247          $this->uri = $this->path;
2248          $this->digest_uri = $this->uri;
2249  
2250          // build headers
2251          if (!isset($u['port'])) {
2252              $this->setHeader('Host', $this->host);
2253          } else {
2254              $this->setHeader('Host', $this->host.':'.$this->port);
2255          }
2256  
2257          if (isset($u['user']) && $u['user'] != '') {
2258              $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2259          }
2260      }
2261  
2262      /**
2263      * gets the I/O method to use
2264      *
2265      * @return    string    I/O method to use (socket|curl|unknown)
2266      * @access    private
2267      */
2268  	function io_method() {
2269          if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2270              return 'curl';
2271          if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2272              return 'socket';
2273          return 'unknown';
2274      }
2275  
2276      /**
2277      * establish an HTTP connection
2278      *
2279      * @param    integer $timeout set connection timeout in seconds
2280      * @param    integer $response_timeout set response timeout in seconds
2281      * @return    boolean true if connected, false if not
2282      * @access   private
2283      */
2284  	function connect($connection_timeout=0,$response_timeout=30){
2285            // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2286            // "regular" socket.
2287            // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2288            //       loaded), and until PHP5 stream_get_wrappers is not available.
2289  //          if ($this->scheme == 'https') {
2290  //              if (version_compare(phpversion(), '4.3.0') >= 0) {
2291  //                  if (extension_loaded('openssl')) {
2292  //                      $this->scheme = 'ssl';
2293  //                      $this->debug('Using SSL over OpenSSL');
2294  //                  }
2295  //              }
2296  //        }
2297          $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2298        if ($this->io_method() == 'socket') {
2299          if (!is_array($this->proxy)) {
2300              $host = $this->host;
2301              $port = $this->port;
2302          } else {
2303              $host = $this->proxy['host'];
2304              $port = $this->proxy['port'];
2305          }
2306  
2307          // use persistent connection
2308          if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2309              if (!feof($this->fp)) {
2310                  $this->debug('Re-use persistent connection');
2311                  return true;
2312              }
2313              fclose($this->fp);
2314              $this->debug('Closed persistent connection at EOF');
2315          }
2316  
2317          // munge host if using OpenSSL
2318          if ($this->scheme == 'ssl') {
2319              $host = 'ssl://' . $host;
2320          }
2321          $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2322  
2323          // open socket
2324          if($connection_timeout > 0){
2325              $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2326          } else {
2327              $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2328          }
2329  
2330          // test pointer
2331          if(!$this->fp) {
2332              $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2333              if ($this->errno) {
2334                  $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2335              } else {
2336                  $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2337              }
2338              $this->debug($msg);
2339              $this->setError($msg);
2340              return false;
2341          }
2342  
2343          // set response timeout
2344          $this->debug('set response timeout to ' . $response_timeout);
2345          socket_set_timeout( $this->fp, $response_timeout);
2346  
2347          $this->debug('socket connected');
2348          return true;
2349        } else if ($this->io_method() == 'curl') {
2350          if (!extension_loaded('curl')) {
2351  //            $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2352              $this->setError('The PHP cURL Extension is required for HTTPS or NLTM.  You will need to re-build or update your PHP to included cURL.');
2353              return false;
2354          }
2355          // Avoid warnings when PHP does not have these options
2356          if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2357              $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2358          else
2359              $CURLOPT_CONNECTIONTIMEOUT = 78;
2360          if (defined('CURLOPT_HTTPAUTH'))
2361              $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2362          else
2363              $CURLOPT_HTTPAUTH = 107;
2364          if (defined('CURLOPT_PROXYAUTH'))
2365              $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2366          else
2367              $CURLOPT_PROXYAUTH = 111;
2368          if (defined('CURLAUTH_BASIC'))
2369              $CURLAUTH_BASIC = CURLAUTH_BASIC;
2370          else
2371              $CURLAUTH_BASIC = 1;
2372          if (defined('CURLAUTH_DIGEST'))
2373              $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2374          else
2375              $CURLAUTH_DIGEST = 2;
2376          if (defined('CURLAUTH_NTLM'))
2377              $CURLAUTH_NTLM = CURLAUTH_NTLM;
2378          else
2379              $CURLAUTH_NTLM = 8;
2380  
2381          $this->debug('connect using cURL');
2382          // init CURL
2383          $this->ch = curl_init();
2384          // set url
2385          $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2386          // add path
2387          $hostURL .= $this->path;
2388          $this->setCurlOption(CURLOPT_URL, $hostURL);
2389          // follow location headers (re-directs)
2390          if (ini_get('safe_mode') || ini_get('open_basedir')) {
2391              $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2392              $this->debug('safe_mode = ');
2393              $this->appendDebug($this->varDump(ini_get('safe_mode')));
2394              $this->debug('open_basedir = ');
2395              $this->appendDebug($this->varDump(ini_get('open_basedir')));
2396          } else {
2397              $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2398          }
2399          // ask for headers in the response output
2400          $this->setCurlOption(CURLOPT_HEADER, 1);
2401          // ask for the response output as the return value
2402          $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2403          // encode
2404          // We manage this ourselves through headers and encoding
2405  //        if(function_exists('gzuncompress')){
2406  //            $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2407  //        }
2408          // persistent connection
2409          if ($this->persistentConnection) {
2410              // I believe the following comment is now bogus, having applied to
2411              // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2412              // The way we send data, we cannot use persistent connections, since
2413              // there will be some "junk" at the end of our request.
2414              //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2415              $this->persistentConnection = false;
2416              $this->setHeader('Connection', 'close');
2417          }
2418          // set timeouts
2419          if ($connection_timeout != 0) {
2420              $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2421          }
2422          if ($response_timeout != 0) {
2423              $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2424          }
2425  
2426          if ($this->scheme == 'https') {
2427              $this->debug('set cURL SSL verify options');
2428              // recent versions of cURL turn on peer/host checking by default,
2429              // while PHP binaries are not compiled with a default location for the
2430              // CA cert bundle, so disable peer/host checking.
2431              //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2432              $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2433              $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2434  
2435              // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2436              if ($this->authtype == 'certificate') {
2437                  $this->debug('set cURL certificate options');
2438                  if (isset($this->certRequest['cainfofile'])) {
2439                      $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2440                  }
2441                  if (isset($this->certRequest['verifypeer'])) {
2442                      $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2443                  } else {
2444                      $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2445                  }
2446                  if (isset($this->certRequest['verifyhost'])) {
2447                      $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2448                  } else {
2449                      $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2450                  }
2451                  if (isset($this->certRequest['sslcertfile'])) {
2452                      $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2453                  }
2454                  if (isset($this->certRequest['sslkeyfile'])) {
2455                      $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2456                  }
2457                  if (isset($this->certRequest['passphrase'])) {
2458                      $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2459                  }
2460                  if (isset($this->certRequest['certpassword'])) {
2461                      $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2462                  }
2463              }
2464          }
2465          if ($this->authtype && ($this->authtype != 'certificate')) {
2466              if ($this->username) {
2467                  $this->debug('set cURL username/password');
2468                  $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2469              }
2470              if ($this->authtype == 'basic') {
2471                  $this->debug('set cURL for Basic authentication');
2472                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2473              }
2474              if ($this->authtype == 'digest') {
2475                  $this->debug('set cURL for digest authentication');
2476                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2477              }
2478              if ($this->authtype == 'ntlm') {
2479                  $this->debug('set cURL for NTLM authentication');
2480                  $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2481              }
2482          }
2483          if (is_array($this->proxy)) {
2484              $this->debug('set cURL proxy options');
2485              if ($this->proxy['port'] != '') {
2486                  $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2487              } else {
2488                  $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2489              }
2490              if ($this->proxy['username'] || $this->proxy['password']) {
2491                  $this->debug('set cURL proxy authentication options');
2492                  $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2493                  if ($this->proxy['authtype'] == 'basic') {
2494                      $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2495                  }
2496                  if ($this->proxy['authtype'] == 'ntlm') {
2497                      $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2498                  }
2499              }
2500          }
2501          $this->debug('cURL connection set up');
2502          return true;
2503        } else {
2504          $this->setError('Unknown scheme ' . $this->scheme);
2505          $this->debug('Unknown scheme ' . $this->scheme);
2506          return false;
2507        }
2508      }
2509  
2510      /**
2511      * sends the SOAP request and gets the SOAP response via HTTP[S]
2512      *
2513      * @param    string $data message data
2514      * @param    integer $timeout set connection timeout in seconds
2515      * @param    integer $response_timeout set response timeout in seconds
2516      * @param    array $cookies cookies to send
2517      * @return    string data
2518      * @access   public
2519      */
2520  	function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2521  
2522          $this->debug('entered send() with data of length: '.strlen($data));
2523  
2524          $this->tryagain = true;
2525          $tries = 0;
2526          while ($this->tryagain) {
2527              $this->tryagain = false;
2528              if ($tries++ < 2) {
2529                  // make connnection
2530                  if (!$this->connect($timeout, $response_timeout)){
2531                      return false;
2532                  }
2533  
2534                  // send request
2535                  if (!$this->sendRequest($data, $cookies)){
2536                      return false;
2537                  }
2538  
2539                  // get response
2540                  $respdata = $this->getResponse();
2541              } else {
2542                  $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2543              }
2544          }
2545          $this->debug('end of send()');
2546          return $respdata;
2547      }
2548  
2549  
2550      /**
2551      * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2552      *
2553      * @param    string $data message data
2554      * @param    integer $timeout set connection timeout in seconds
2555      * @param    integer $response_timeout set response timeout in seconds
2556      * @param    array $cookies cookies to send
2557      * @return    string data
2558      * @access   public
2559      * @deprecated
2560      */
2561  	function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2562          return $this->send($data, $timeout, $response_timeout, $cookies);
2563      }
2564  
2565      /**
2566      * if authenticating, set user credentials here
2567      *
2568      * @param    string $username
2569      * @param    string $password
2570      * @param    string $authtype (basic|digest|certificate|ntlm)
2571      * @param    array $digestRequest (keys must be nonce, nc, realm, qop)
2572      * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2573      * @access   public
2574      */
2575  	function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2576          $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2577          $this->appendDebug($this->varDump($digestRequest));
2578          $this->debug("certRequest=");
2579          $this->appendDebug($this->varDump($certRequest));
2580          // cf. RFC 2617
2581          if ($authtype == 'basic') {
2582              $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2583          } elseif ($authtype == 'digest') {
2584              if (isset($digestRequest['nonce'])) {
2585                  $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2586  
2587                  // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2588  
2589                  // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2590                  $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2591  
2592                  // H(A1) = MD5(A1)
2593                  $HA1 = md5($A1);
2594  
2595                  // A2 = Method ":" digest-uri-value
2596                  $A2 = $this->request_method . ':' . $this->digest_uri;
2597  
2598                  // H(A2)
2599                  $HA2 =  md5($A2);
2600  
2601                  // KD(secret, data) = H(concat(secret, ":", data))
2602                  // if qop == auth:
2603                  // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2604                  //                              ":" nc-value
2605                  //                              ":" unq(cnonce-value)
2606                  //                              ":" unq(qop-value)
2607                  //                              ":" H(A2)
2608                  //                            ) <">
2609                  // if qop is missing,
2610                  // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2611  
2612                  $unhashedDigest = '';
2613                  $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2614                  $cnonce = $nonce;
2615                  if ($digestRequest['qop'] != '') {
2616                      $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2617                  } else {
2618                      $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2619                  }
2620  
2621                  $hashedDigest = md5($unhashedDigest);
2622  
2623                  $opaque = '';
2624                  if (isset($digestRequest['opaque'])) {
2625                      $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2626                  }
2627  
2628                  $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 . '"');
2629              }
2630          } elseif ($authtype == 'certificate') {
2631              $this->certRequest = $certRequest;
2632              $this->debug('Authorization header not set for certificate');
2633          } elseif ($authtype == 'ntlm') {
2634              // do nothing
2635              $this->debug('Authorization header not set for ntlm');
2636          }
2637          $this->username = $username;
2638          $this->password = $password;
2639          $this->authtype = $authtype;
2640          $this->digestRequest = $digestRequest;
2641      }
2642  
2643      /**
2644      * set the soapaction value
2645      *
2646      * @param    string $soapaction
2647      * @access   public
2648      */
2649  	function setSOAPAction($soapaction) {
2650          $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2651      }
2652  
2653      /**
2654      * use http encoding
2655      *
2656      * @param    string $enc encoding style. supported values: gzip, deflate, or both
2657      * @access   public
2658      */
2659  	function setEncoding($enc='gzip, deflate') {
2660          if (function_exists('gzdeflate')) {
2661              $this->protocol_version = '1.1';
2662              $this->setHeader('Accept-Encoding', $enc);
2663              if (!isset($this->outgoing_headers['Connection'])) {
2664                  $this->setHeader('Connection', 'close');
2665                  $this->persistentConnection = false;
2666              }
2667              set_magic_quotes_runtime(0);
2668              // deprecated
2669              $this->encoding = $enc;
2670          }
2671      }
2672  
2673      /**
2674      * set proxy info here
2675      *
2676      * @param    string $proxyhost use an empty string to remove proxy
2677      * @param    string $proxyport
2678      * @param    string $proxyusername
2679      * @param    string $proxypassword
2680      * @param    string $proxyauthtype (basic|ntlm)
2681      * @access   public
2682      */
2683  	function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2684          if ($proxyhost) {
2685              $this->proxy = array(
2686                  'host' => $proxyhost,
2687                  'port' => $proxyport,
2688                  'username' => $proxyusername,
2689                  'password' => $proxypassword,
2690                  'authtype' => $proxyauthtype
2691              );
2692              if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2693                  $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2694              }
2695          } else {
2696              $this->debug('remove proxy');
2697              $proxy = null;
2698              unsetHeader('Proxy-Authorization');
2699          }
2700      }
2701  
2702  
2703      /**
2704       * Test if the given string starts with a header that is to be skipped.
2705       * Skippable headers result from chunked transfer and proxy requests.
2706       *
2707       * @param    string $data The string to check.
2708       * @returns    boolean    Whether a skippable header was found.
2709       * @access    private
2710       */
2711  	function isSkippableCurlHeader(&$data) {
2712          $skipHeaders = array(    'HTTP/1.1 100',
2713                                  'HTTP/1.0 301',
2714                                  'HTTP/1.1 301',
2715                                  'HTTP/1.0 302',
2716                                  'HTTP/1.1 302',
2717                                  'HTTP/1.0 401',
2718                                  'HTTP/1.1 401',
2719                                  'HTTP/1.0 200 Connection established');
2720          foreach ($skipHeaders as $hd) {
2721              $prefix = substr($data, 0, strlen($hd));
2722              if ($prefix == $hd) return true;
2723          }
2724  
2725          return false;
2726      }
2727  
2728      /**
2729      * decode a string that is encoded w/ "chunked' transfer encoding
2730       * as defined in RFC2068 19.4.6
2731      *
2732      * @param    string $buffer
2733      * @param    string $lb
2734      * @returns    string
2735      * @access   public
2736      * @deprecated
2737      */
2738  	function decodeChunked($buffer, $lb){
2739          // length := 0
2740          $length = 0;
2741          $new = '';
2742  
2743          // read chunk-size, chunk-extension (if any) and CRLF
2744          // get the position of the linebreak
2745          $chunkend = strpos($buffer, $lb);
2746          if ($chunkend == FALSE) {
2747              $this->debug('no linebreak found in decodeChunked');
2748              return $new;
2749          }
2750          $temp = substr($buffer,0,$chunkend);
2751          $chunk_size = hexdec( trim($temp) );
2752          $chunkstart = $chunkend + strlen($lb);
2753          // while (chunk-size > 0) {
2754          while ($chunk_size > 0) {
2755              $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2756              $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2757  
2758              // Just in case we got a broken connection
2759                if ($chunkend == FALSE) {
2760                    $chunk = substr($buffer,$chunkstart);
2761                  // append chunk-data to entity-body
2762                  $new .= $chunk;
2763                    $length += strlen($chunk);
2764                    break;
2765              }
2766  
2767                // read chunk-data and CRLF
2768                $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2769                // append chunk-data to entity-body
2770                $new .= $chunk;
2771                // length := length + chunk-size
2772                $length += strlen($chunk);
2773                // read chunk-size and CRLF
2774                $chunkstart = $chunkend + strlen($lb);
2775  
2776                $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2777              if ($chunkend == FALSE) {
2778                  break; //Just in case we got a broken connection
2779              }
2780              $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2781              $chunk_size = hexdec( trim($temp) );
2782              $chunkstart = $chunkend;
2783          }
2784          return $new;
2785      }
2786  
2787      /**
2788       * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2789       *
2790       * @param    string $data HTTP body
2791       * @param    string $cookie_str data for HTTP Cookie header
2792       * @return    void
2793       * @access    private
2794       */
2795  	function buildPayload($data, $cookie_str = '') {
2796          // Note: for cURL connections, $this->outgoing_payload is ignored,
2797          // as is the Content-Length header, but these are still created as
2798          // debugging guides.
2799  
2800          // add content-length header
2801          $this->setHeader('Content-Length', strlen($data));
2802  
2803          // start building outgoing payload:
2804          if ($this->proxy) {
2805              $uri = $this->url;
2806          } else {
2807              $uri = $this->uri;
2808          }
2809          $req = "$this->request_method $uri HTTP/$this->protocol_version";
2810          $this->debug("HTTP request: $req");
2811          $this->outgoing_payload = "$req\r\n";
2812  
2813          // loop thru headers, serializing
2814          foreach($this->outgoing_headers as $k => $v){
2815              $hdr = $k.': '.$v;
2816              $this->debug("HTTP header: $hdr");
2817              $this->outgoing_payload .= "$hdr\r\n";
2818          }
2819  
2820          // add any cookies
2821          if ($cookie_str != '') {
2822              $hdr = 'Cookie: '.$cookie_str;
2823              $this->debug("HTTP header: $hdr");
2824              $this->outgoing_payload .= "$hdr\r\n";
2825          }
2826  
2827          // header/body separator
2828          $this->outgoing_payload .= "\r\n";
2829  
2830          // add data
2831          $this->outgoing_payload .= $data;
2832      }
2833  
2834      /**
2835      * sends the SOAP request via HTTP[S]
2836      *
2837      * @param    string $data message data
2838      * @param    array $cookies cookies to send
2839      * @return    boolean    true if OK, false if problem
2840      * @access   private
2841      */
2842  	function sendRequest($data, $cookies = NULL) {
2843          // build cookie string
2844          $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2845  
2846          // build payload
2847          $this->buildPayload($data, $cookie_str);
2848  
2849        if ($this->io_method() == 'socket') {
2850          // send payload
2851          if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2852              $this->setError('couldn\'t write message data to socket');
2853              $this->debug('couldn\'t write message data to socket');
2854              return false;
2855          }
2856          $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2857          return true;
2858        } else if ($this->io_method() == 'curl') {
2859          // set payload
2860          // cURL does say this should only be the verb, and in fact it
2861          // turns out that the URI and HTTP version are appended to this, which
2862          // some servers refuse to work with (so we no longer use this method!)
2863          //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2864          $curl_headers = array();
2865          foreach($this->outgoing_headers as $k => $v){
2866              if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2867                  $this->debug("Skip cURL header $k: $v");
2868              } else {
2869                  $curl_headers[] = "$k: $v";
2870              }
2871          }
2872          if ($cookie_str != '') {
2873              $curl_headers[] = 'Cookie: ' . $cookie_str;
2874          }
2875          $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2876          $this->debug('set cURL HTTP headers');
2877          if ($this->request_method == "POST") {
2878                $this->setCurlOption(CURLOPT_POST, 1);
2879                $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2880              $this->debug('set cURL POST data');
2881            } else {
2882            }
2883          // insert custom user-set cURL options
2884          foreach ($this->ch_options as $key => $val) {
2885              $this->setCurlOption($key, $val);
2886          }
2887  
2888          $this->debug('set cURL payload');
2889          return true;
2890        }
2891      }
2892  
2893      /**
2894      * gets the SOAP response via HTTP[S]
2895      *
2896      * @return    string the response (also sets member variables like incoming_payload)
2897      * @access   private
2898      */
2899  	function getResponse(){
2900          $this->incoming_payload = '';
2901  
2902        if ($this->io_method() == 'socket') {
2903          // loop until headers have been retrieved
2904          $data = '';
2905          while (!isset($lb)){
2906  
2907              // We might EOF during header read.
2908              if(feof($this->fp)) {
2909                  $this->incoming_payload = $data;
2910                  $this->debug('found no headers before EOF after length ' . strlen($data));
2911                  $this->debug("received before EOF:\n" . $data);
2912                  $this->setError('server failed to send headers');
2913                  return false;
2914              }
2915  
2916              $tmp = fgets($this->fp, 256);
2917              $tmplen = strlen($tmp);
2918              $this->debug("read line of $tmplen bytes: " . trim($tmp));
2919  
2920              if ($tmplen == 0) {
2921                  $this->incoming_payload = $data;
2922                  $this->debug('socket read of headers timed out after length ' . strlen($data));
2923                  $this->debug("read before timeout: " . $data);
2924                  $this->setError('socket read of headers timed out');
2925                  return false;
2926              }
2927  
2928              $data .= $tmp;
2929              $pos = strpos($data,"\r\n\r\n");
2930              if($pos > 1){
2931                  $lb = "\r\n";
2932              } else {
2933                  $pos = strpos($data,"\n\n");
2934                  if($pos > 1){
2935                      $lb = "\n";
2936                  }
2937              }
2938              // remove 100 headers
2939              if (isset($lb) && ereg('^HTTP/1.1 100',$data)) {
2940                  unset($lb);
2941                  $data = '';
2942              }//
2943          }
2944          // store header data
2945          $this->incoming_payload .= $data;
2946          $this->debug('found end of headers after length ' . strlen($data));
2947          // process headers
2948          $header_data = trim(substr($data,0,$pos));
2949          $header_array = explode($lb,$header_data);
2950          $this->incoming_headers = array();
2951          $this->incoming_cookies = array();
2952          foreach($header_array as $header_line){
2953              $arr = explode(':',$header_line, 2);
2954              if(count($arr) > 1){
2955                  $header_name = strtolower(trim($arr[0]));
2956                  $this->incoming_headers[$header_name] = trim($arr[1]);
2957                  if ($header_name == 'set-cookie') {
2958                      // TODO: allow multiple cookies from parseCookie
2959                      $cookie = $this->parseCookie(trim($arr[1]));
2960                      if ($cookie) {
2961                          $this->incoming_cookies[] = $cookie;
2962                          $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2963                      } else {
2964                          $this->debug('did not find cookie in ' . trim($arr[1]));
2965                      }
2966                  }
2967              } else if (isset($header_name)) {
2968                  // append continuation line to previous header
2969                  $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2970              }
2971          }
2972  
2973          // loop until msg has been received
2974          if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2975              $content_length =  2147483647;    // ignore any content-length header
2976              $chunked = true;
2977              $this->debug("want to read chunked content");
2978          } elseif (isset($this->incoming_headers['content-length'])) {
2979              $content_length = $this->incoming_headers['content-length'];
2980              $chunked = false;
2981              $this->debug("want to read content of length $content_length");
2982          } else {
2983              $content_length =  2147483647;
2984              $chunked = false;
2985              $this->debug("want to read content to EOF");
2986          }
2987          $data = '';
2988          do {
2989              if ($chunked) {
2990                  $tmp = fgets($this->fp, 256);
2991                  $tmplen = strlen($tmp);
2992                  $this->debug("read chunk line of $tmplen bytes");
2993                  if ($tmplen == 0) {
2994                      $this->incoming_payload = $data;
2995                      $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2996                      $this->debug("read before timeout:\n" . $data);
2997                      $this->setError('socket read of chunk length timed out');
2998                      return false;
2999                  }
3000                  $content_length = hexdec(trim($tmp));
3001                  $this->debug("chunk length $content_length");
3002              }
3003              $strlen = 0;
3004              while (($strlen < $content_length) && (!feof($this->fp))) {
3005                  $readlen = min(8192, $content_length - $strlen);
3006                  $tmp = fread($this->fp, $readlen);
3007                  $tmplen = strlen($tmp);
3008                  $this->debug("read buffer of $tmplen bytes");
3009                  if (($tmplen == 0) && (!feof($this->fp))) {
3010                      $this->incoming_payload = $data;
3011                      $this->debug('socket read of body timed out after length ' . strlen($data));
3012                      $this->debug("read before timeout:\n" . $data);
3013                      $this->setError('socket read of body timed out');
3014                      return false;
3015                  }
3016                  $strlen += $tmplen;
3017                  $data .= $tmp;
3018              }
3019              if ($chunked && ($content_length > 0)) {
3020                  $tmp = fgets($this->fp, 256);
3021                  $tmplen = strlen($tmp);
3022                  $this->debug("read chunk terminator of $tmplen bytes");
3023                  if ($tmplen == 0) {
3024                      $this->incoming_payload = $data;
3025                      $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3026                      $this->debug("read before timeout:\n" . $data);
3027                      $this->setError('socket read of chunk terminator timed out');
3028                      return false;
3029                  }
3030              }
3031          } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3032          if (feof($this->fp)) {
3033              $this->debug('read to EOF');
3034          }
3035          $this->debug('read body of length ' . strlen($data));
3036          $this->incoming_payload .= $data;
3037          $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3038  
3039          // close filepointer
3040          if(
3041              (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3042              (! $this->persistentConnection) || feof($this->fp)){
3043              fclose($this->fp);
3044              $this->fp = false;
3045              $this->debug('closed socket');
3046          }
3047  
3048          // connection was closed unexpectedly
3049          if($this->incoming_payload == ''){
3050              $this->setError('no response from server');
3051              return false;
3052          }
3053  
3054          // decode transfer-encoding
3055  //        if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3056  //            if(!$data = $this->decodeChunked($data, $lb)){
3057  //                $this->setError('Decoding of chunked data failed');
3058  //                return false;
3059  //            }
3060              //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3061              // set decoded payload
3062  //            $this->incoming_payload = $header_data.$lb.$lb.$data;
3063  //        }
3064  
3065        } else if ($this->io_method() == 'curl') {
3066          // send and receive
3067          $this->debug('send and receive with cURL');
3068          $this->incoming_payload = curl_exec($this->ch);
3069          $data = $this->incoming_payload;
3070  
3071          $cErr = curl_error($this->ch);
3072          if ($cErr != '') {
3073              $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3074              // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3075              foreach(curl_getinfo($this->ch) as $k => $v){
3076                  $err .= "$k: $v<br>";
3077              }
3078              $this->debug($err);
3079              $this->setError($err);
3080              curl_close($this->ch);
3081              return false;
3082          } else {
3083              //echo '<pre>';
3084              //var_dump(curl_getinfo($this->ch));
3085              //echo '</pre>';
3086          }
3087          // close curl
3088          $this->debug('No cURL error, closing cURL');
3089          curl_close($this->ch);
3090  
3091          // try removing skippable headers
3092          $savedata = $data;
3093          while ($this->isSkippableCurlHeader($data)) {
3094              $this->debug("Found HTTP header to skip");
3095              if ($pos = strpos($data,"\r\n\r\n")) {
3096                  $data = ltrim(substr($data,$pos));
3097              } elseif($pos = strpos($data,"\n\n") ) {
3098                  $data = ltrim(substr($data,$pos));
3099              }
3100          }
3101  
3102          if ($data == '') {
3103              // have nothing left; just remove 100 header(s)
3104              $data = $savedata;
3105              while (ereg('^HTTP/1.1 100',$data)) {
3106                  if ($pos = strpos($data,"\r\n\r\n")) {
3107                      $data = ltrim(substr($data,$pos));
3108                  } elseif($pos = strpos($data,"\n\n") ) {
3109                      $data = ltrim(substr($data,$pos));
3110                  }
3111              }
3112          }
3113  
3114          // separate content from HTTP headers
3115          if ($pos = strpos($data,"\r\n\r\n")) {
3116              $lb = "\r\n";
3117          } elseif( $pos = strpos($data,"\n\n")) {
3118              $lb = "\n";
3119          } else {
3120              $this->debug('no proper separation of headers and document');
3121              $this->setError('no proper separation of headers and document');
3122              return false;
3123          }
3124          $header_data = trim(substr($data,0,$pos));
3125          $header_array = explode($lb,$header_data);
3126          $data = ltrim(substr($data,$pos));
3127          $this->debug('found proper separation of headers and document');
3128          $this->debug('cleaned data, stringlen: '.strlen($data));
3129          // clean headers
3130          foreach ($header_array as $header_line) {
3131              $arr = explode(':',$header_line,2);
3132              if(count($arr) > 1){
3133                  $header_name = strtolower(trim($arr[0]));
3134                  $this->incoming_headers[$header_name] = trim($arr[1]);
3135                  if ($header_name == 'set-cookie') {
3136                      // TODO: allow multiple cookies from parseCookie
3137                      $cookie = $this->parseCookie(trim($arr[1]));
3138                      if ($cookie) {
3139                          $this->incoming_cookies[] = $cookie;
3140                          $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3141                      } else {
3142                          $this->debug('did not find cookie in ' . trim($arr[1]));
3143                      }
3144                  }
3145              } else if (isset($header_name)) {
3146                  // append continuation line to previous header
3147                  $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3148              }
3149          }
3150        }
3151  
3152          $this->response_status_line = $header_array[0];
3153          $arr = explode(' ', $this->response_status_line, 3);
3154          $http_version = $arr[0];
3155          $http_status = intval($arr[1]);
3156          $http_reason = count($arr) > 2 ? $arr[2] : '';
3157  
3158           // see if we need to resend the request with http digest authentication
3159           if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3160               $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3161               $this->setURL($this->incoming_headers['location']);
3162              $this->tryagain = true;
3163              return false;
3164          }
3165  
3166           // see if we need to resend the request with http digest authentication
3167           if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3168               $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3169               if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3170                   $this->debug('Server wants digest authentication');
3171                   // remove "Digest " from our elements
3172                   $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3173  
3174                   // parse elements into array
3175                   $digestElements = explode(',', $digestString);
3176                   foreach ($digestElements as $val) {
3177                       $tempElement = explode('=', trim($val), 2);
3178                       $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3179                   }
3180  
3181                  // should have (at least) qop, realm, nonce
3182                   if (isset($digestRequest['nonce'])) {
3183                       $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3184                       $this->tryagain = true;
3185                       return false;
3186                   }
3187               }
3188              $this->debug('HTTP authentication failed');
3189              $this->setError('HTTP authentication failed');
3190              return false;
3191           }
3192  
3193          if (
3194              ($http_status >= 300 && $http_status <= 307) ||
3195              ($http_status >= 400 && $http_status <= 417) ||
3196              ($http_status >= 501 && $http_status <= 505)
3197             ) {
3198              $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3199              return false;
3200          }
3201  
3202          // decode content-encoding
3203          if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3204              if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3205                  // if decoding works, use it. else assume data wasn't gzencoded
3206                  if(function_exists('gzinflate')){
3207                      //$timer->setMarker('starting decoding of gzip/deflated content');
3208                      // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3209                      // this means there are no Zlib headers, although there should be
3210                      $this->debug('The gzinflate function exists');
3211                      $datalen = strlen($data);
3212                      if ($this->incoming_headers['content-encoding'] == 'deflate') {
3213                          if ($degzdata = @gzinflate($data)) {
3214                              $data = $degzdata;
3215                              $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3216                              if (strlen($data) < $datalen) {
3217                                  // test for the case that the payload has been compressed twice
3218                                  $this->debug('The inflated payload is smaller than the gzipped one; try again');
3219                                  if ($degzdata = @gzinflate($data)) {
3220                                      $data = $degzdata;
3221                                      $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3222                                  }
3223                              }
3224                          } else {
3225                              $this->debug('Error using gzinflate to inflate the payload');
3226                              $this->setError('Error using gzinflate to inflate the payload');
3227                          }
3228                      } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3229                          if ($degzdata = @gzinflate(substr($data, 10))) {    // do our best
3230                              $data = $degzdata;
3231                              $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3232                              if (strlen($data) < $datalen) {
3233                                  // test for the case that the payload has been compressed twice
3234                                  $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3235                                  if ($degzdata = @gzinflate(substr($data, 10))) {
3236                                      $data = $degzdata;
3237                                      $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3238                                  }
3239                              }
3240                          } else {
3241                              $this->debug('Error using gzinflate to un-gzip the payload');
3242                              $this->setError('Error using gzinflate to un-gzip the payload');
3243                          }
3244                      }
3245                      //$timer->setMarker('finished decoding of gzip/deflated content');
3246                      //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3247                      // set decoded payload
3248                      $this->incoming_payload = $header_data.$lb.$lb.$data;
3249                  } else {
3250                      $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3251                      $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3252                  }
3253              } else {
3254                  $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3255                  $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3256              }
3257          } else {
3258              $this->debug('No Content-Encoding header');
3259          }
3260  
3261          if(strlen($data) == 0){
3262              $this->debug('no data after headers!');
3263              $this->setError('no data present after HTTP headers');
3264              return false;
3265          }
3266  
3267          return $data;
3268      }
3269  
3270      /**
3271       * sets the content-type for the SOAP message to be sent
3272       *
3273       * @param    string $type the content type, MIME style
3274       * @param    mixed $charset character set used for encoding (or false)
3275       * @access    public
3276       */
3277  	function setContentType($type, $charset = false) {
3278          $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3279      }
3280  
3281      /**
3282       * specifies that an HTTP persistent connection should be used
3283       *
3284       * @return    boolean whether the request was honored by this method.
3285       * @access    public
3286       */
3287  	function usePersistentConnection(){
3288          if (isset($this->outgoing_headers['Accept-Encoding'])) {
3289              return false;
3290          }
3291          $this->protocol_version = '1.1';
3292          $this->persistentConnection = true;
3293          $this->setHeader('Connection', 'Keep-Alive');
3294          return true;
3295      }
3296  
3297      /**
3298       * parse an incoming Cookie into it's parts
3299       *
3300       * @param    string $cookie_str content of cookie
3301       * @return    array with data of that cookie
3302       * @access    private
3303       */
3304      /*
3305       * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3306       */
3307  	function parseCookie($cookie_str) {
3308          $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3309          $data = split(';', $cookie_str);
3310          $value_str = $data[0];
3311  
3312          $cookie_param = 'domain=';
3313          $start = strpos($cookie_str, $cookie_param);
3314          if ($start > 0) {
3315              $domain = substr($cookie_str, $start + strlen($cookie_param));
3316              $domain = substr($domain, 0, strpos($domain, ';'));
3317          } else {
3318              $domain = '';
3319          }
3320  
3321          $cookie_param = 'expires=';
3322          $start = strpos($cookie_str, $cookie_param);
3323          if ($start > 0) {
3324              $expires = substr($cookie_str, $start + strlen($cookie_param));
3325              $expires = substr($expires, 0, strpos($expires, ';'));
3326          } else {
3327              $expires = '';
3328          }
3329  
3330          $cookie_param = 'path=';
3331          $start = strpos($cookie_str, $cookie_param);
3332          if ( $start > 0 ) {
3333              $path = substr($cookie_str, $start + strlen($cookie_param));
3334              $path = substr($path, 0, strpos($path, ';'));
3335          } else {
3336              $path = '/';
3337          }
3338  
3339          $cookie_param = ';secure;';
3340          if (strpos($cookie_str, $cookie_param) !== FALSE) {
3341              $secure = true;
3342          } else {
3343              $secure = false;
3344          }
3345  
3346          $sep_pos = strpos($value_str, '=');
3347  
3348          if ($sep_pos) {
3349              $name = substr($value_str, 0, $sep_pos);
3350              $value = substr($value_str, $sep_pos + 1);
3351              $cookie= array(    'name' => $name,
3352                              'value' => $value,
3353                              'domain' => $domain,
3354                              'path' => $path,
3355                              'expires' => $expires,
3356                              'secure' => $secure
3357                              );
3358              return $cookie;
3359          }
3360          return false;
3361      }
3362  
3363      /**
3364       * sort out cookies for the current request
3365       *
3366       * @param    array $cookies array with all cookies
3367       * @param    boolean $secure is the send-content secure or not?
3368       * @return    string for Cookie-HTTP-Header
3369       * @access    private
3370       */
3371  	function getCookiesForRequest($cookies, $secure=false) {
3372          $cookie_str = '';
3373          if ((! is_null($cookies)) && (is_array($cookies))) {
3374              foreach ($cookies as $cookie) {
3375                  if (! is_array($cookie)) {
3376                      continue;
3377                  }
3378                  $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3379                  if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3380                      if (strtotime($cookie['expires']) <= time()) {
3381                          $this->debug('cookie has expired');
3382                          continue;
3383                      }
3384                  }
3385                  if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3386                      $domain = preg_quote($cookie['domain']);
3387                      if (! preg_match("'.*$domain$'i", $this->host)) {
3388                          $this->debug('cookie has different domain');
3389                          continue;
3390                      }
3391                  }
3392                  if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3393                      $path = preg_quote($cookie['path']);
3394                      if (! preg_match("'^$path.*'i", $this->path)) {
3395                          $this->debug('cookie is for a different path');
3396                          continue;
3397                      }
3398                  }
3399                  if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3400                      $this->debug('cookie is secure, transport is not');
3401                      continue;
3402                  }
3403                  $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3404                  $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3405              }
3406          }
3407          return $cookie_str;
3408    }
3409  }
3410  
3411  ?><?php
3412  
3413  
3414  
3415  /**
3416  *
3417  * nusoap_server allows the user to create a SOAP server
3418  * that is capable of receiving messages and returning responses
3419  *
3420  * @author   Dietrich Ayala <dietrich@ganx4.com>
3421  * @author   Scott Nichol <snichol@users.sourceforge.net>
3422  * @version  $Id: nusoap.php,v 1.114 2007/11/06 15:17:46 snichol Exp $
3423  * @access   public
3424  */
3425  class nusoap_server extends nusoap_base {
3426      /**
3427       * HTTP headers of request
3428       * @var array
3429       * @access private
3430       */
3431      public $headers = array();
3432      /**
3433       * HTTP request
3434       * @var string
3435       * @access private
3436       */
3437      public $request = '';
3438      /**
3439       * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3440       * @var string
3441       * @access public
3442       */
3443      public $requestHeaders = '';
3444      /**
3445       * SOAP Headers from request (parsed)
3446       * @var mixed
3447       * @access public
3448       */
3449      public $requestHeader = NULL;
3450      /**
3451       * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3452       * @var string
3453       * @access public
3454       */
3455      public $document = '';
3456      /**
3457       * SOAP payload for request (text)
3458       * @var string
3459       * @access public
3460       */
3461      public $requestSOAP = '';
3462      /**
3463       * requested method namespace URI
3464       * @var string
3465       * @access private
3466       */
3467      public $methodURI = '';
3468      /**
3469       * name of method requested
3470       * @var string
3471       * @access private
3472       */
3473      public $methodname = '';
3474      /**
3475       * method parameters from request
3476       * @var array
3477       * @access private
3478       */
3479      public $methodparams = array();
3480      /**
3481       * SOAP Action from request
3482       * @var string
3483       * @access private
3484       */
3485      public $SOAPAction = '';
3486      /**
3487       * character set encoding of incoming (request) messages
3488       * @var string
3489       * @access public
3490       */
3491      public $xml_encoding = '';
3492      /**
3493       * toggles whether the parser decodes element content w/ utf8_decode()
3494       * @var boolean
3495       * @access public
3496       */
3497      public $decode_utf8 = true;
3498  
3499      /**
3500       * HTTP headers of response
3501       * @var array
3502       * @access public
3503       */
3504      public $outgoing_headers = array();
3505      /**
3506       * HTTP response
3507       * @var string
3508       * @access private
3509       */
3510      public $response = '';
3511      /**
3512       * SOAP headers for response (text or array of soapval or associative array)
3513       * @var mixed
3514       * @access public
3515       */
3516      public $responseHeaders = '';
3517      /**
3518       * SOAP payload for response (text)
3519       * @var string
3520       * @access private
3521       */
3522      public $responseSOAP = '';
3523      /**
3524       * method return value to place in response
3525       * @var mixed
3526       * @access private
3527       */
3528      public $methodreturn = false;
3529      /**
3530       * whether $methodreturn is a string of literal XML
3531       * @var boolean
3532       * @access public
3533       */
3534      public $methodreturnisliteralxml = false;
3535      /**
3536       * SOAP fault for response (or false)
3537       * @var mixed
3538       * @access private
3539       */
3540      public $fault = false;
3541      /**
3542       * text indication of result (for debugging)
3543       * @var string
3544       * @access private
3545       */
3546      public $result = 'successful';
3547  
3548      /**
3549       * assoc array of operations => opData; operations are added by the register()
3550       * method or by parsing an external WSDL definition
3551       * @var array
3552       * @access private
3553       */
3554      public $operations = array();
3555      /**
3556       * wsdl instance (if one)
3557       * @var mixed
3558       * @access private
3559       */
3560      public $wsdl = false;
3561      /**
3562       * URL for WSDL (if one)
3563       * @var mixed
3564       * @access private
3565       */
3566      public $externalWSDLURL = false;
3567      /**
3568       * whether to append debug to response as XML comment
3569       * @var boolean
3570       * @access public
3571       */
3572      public $debug_flag = false;
3573  
3574  
3575      /**
3576      * constructor
3577      * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3578      *
3579      * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3580      * @access   public
3581      */
3582  	function nusoap_server($wsdl=false){
3583          parent::nusoap_base();
3584          // turn on debugging?
3585          global $debug;
3586          global $HTTP_SERVER_VARS;
3587  
3588          if (isset($_SERVER)) {
3589              $this->debug("_SERVER is defined:");
3590              $this->appendDebug($this->varDump($_SERVER));
3591          } elseif (isset($HTTP_SERVER_VARS)) {
3592              $this->debug("HTTP_SERVER_VARS is defined:");
3593              $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3594          } else {
3595              $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3596          }
3597  
3598          if (isset($debug)) {
3599              $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3600              $this->debug_flag = $debug;
3601          } elseif (isset($_SERVER['QUERY_STRING'])) {
3602              $qs = explode('&', $_SERVER['QUERY_STRING']);
3603              foreach ($qs as $v) {
3604                  if (substr($v, 0, 6) == 'debug=') {
3605                      $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3606                      $this->debug_flag = substr($v, 6);
3607                  }
3608              }
3609          } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3610              $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3611              foreach ($qs as $v) {
3612                  if (substr($v, 0, 6) == 'debug=') {
3613                      $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3614                      $this->debug_flag = substr($v, 6);
3615                  }
3616              }
3617          }
3618  
3619          // wsdl
3620          if($wsdl){
3621              $this->debug("In nusoap_server, WSDL is specified");
3622              if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3623                  $this->wsdl = $wsdl;
3624                  $this->externalWSDLURL = $this->wsdl->wsdl;
3625                  $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3626              } else {
3627                  $this->debug('Create wsdl from ' . $wsdl);
3628                  $this->wsdl = new wsdl($wsdl);
3629                  $this->externalWSDLURL = $wsdl;
3630              }
3631              $this->appendDebug($this->wsdl->getDebug());
3632              $this->wsdl->clearDebug();
3633              if($err = $this->wsdl->getError()){
3634                  die('WSDL ERROR: '.$err);
3635              }
3636          }
3637      }
3638  
3639      /**
3640      * processes request and returns response
3641      *
3642      * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3643      * @access   public
3644      */
3645  	function service($data){
3646          global $HTTP_SERVER_VARS;
3647  
3648          if (isset($_SERVER['QUERY_STRING'])) {
3649              $qs = $_SERVER['QUERY_STRING'];
3650          } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3651              $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3652          } else {
3653              $qs = '';
3654          }
3655          $this->debug("In service, query string=$qs");
3656  
3657          if (ereg('wsdl', $qs) ){
3658              $this->debug("In service, this is a request for WSDL");
3659              if($this->externalWSDLURL){
3660                if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3661                  header('Location: '.$this->externalWSDLURL);
3662                } else { // assume file
3663                  header("Content-Type: text/xml\r\n");
3664                  $fp = fopen($this->externalWSDLURL, 'r');
3665                  fpassthru($fp);
3666                }
3667              } elseif ($this->wsdl) {
3668                  header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3669                  print $this->wsdl->serialize($this->debug_flag);
3670                  if ($this->debug_flag) {
3671                      $this->debug('wsdl:');
3672                      $this->appendDebug($this->varDump($this->wsdl));
3673                      print $this->getDebugAsXMLComment();
3674                  }
3675              } else {
3676                  header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3677                  print "This service does not provide WSDL";
3678              }
3679          } elseif ($data == '' && $this->wsdl) {
3680              $this->debug("In service, there is no data, so return Web description");
3681              print $this->wsdl->webDescription();
3682          } else {
3683              $this->debug("In service, invoke the request");
3684              $this->parse_request($data);
3685              if (! $this->fault) {
3686                  $this->invoke_method();
3687              }
3688              if (! $this->fault) {
3689                  $this->serialize_return();
3690              }
3691              $this->send_response();
3692          }
3693      }
3694  
3695      /**
3696      * parses HTTP request headers.
3697      *
3698      * The following fields are set by this function (when successful)
3699      *
3700      * headers
3701      * request
3702      * xml_encoding
3703      * SOAPAction
3704      *
3705      * @access   private
3706      */
3707  	function parse_http_headers() {
3708          global $HTTP_SERVER_VARS;
3709  
3710          $this->request = '';
3711          $this->SOAPAction = '';
3712          if(function_exists('getallheaders')){
3713              $this->debug("In parse_http_headers, use getallheaders");
3714              $headers = getallheaders();
3715              foreach($headers as $k=>$v){
3716                  $k =