shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

nusoap.php (362593B)


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