source: pmb4.2/trunk/fuentes/pmb/classes/nusoap/nusoap.php @ 815

Last change on this file since 815 was 815, checked in by jrpelegrina, 4 years ago

Initial release of pmb 4.2

  • Property svn:executable set to *
File size: 255.8 KB
Line 
1<?php
2
3/*
4$Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
5
6NuSOAP - Web Services Toolkit for PHP
7
8Copyright (c) 2002 NuSphere Corporation
9
10This library is free software; you can redistribute it and/or
11modify it under the terms of the GNU Lesser General Public
12License as published by the Free Software Foundation; either
13version 2.1 of the License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18Lesser General Public License for more details.
19
20You should have received a copy of the GNU Lesser General Public
21License along with this library; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24If you have any questions or comments, please email:
25
26Dietrich Ayala
27dietrich@ganx4.com
28http://dietrich.ganx4.com/nusoap
29
30NuSphere Corporation
31http://www.nusphere.com
32
33*/
34
35/* load classes
36
37// necessary classes
38require_once('class.soapclient.php');
39require_once('class.soap_val.php');
40require_once('class.soap_parser.php');
41require_once('class.soap_fault.php');
42
43// transport classes
44require_once('class.soap_transport_http.php');
45
46// optional add-on classes
47require_once('class.xmlschema.php');
48require_once('class.wsdl.php');
49
50// server class
51require_once('class.soap_server.php');*/
52
53// class variable emulation
54// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
55if(!isset($GLOBALS['_transient']['static']['nusoap_base'])){
56        $GLOBALS['_transient']['static']['nusoap_base'] = new stdClass();
57}
58$GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
59
60/**
61*
62* nusoap_base
63*
64* @author   Dietrich Ayala <dietrich@ganx4.com>
65* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
66* @access   public
67*/
68class nusoap_base {
69        /**
70         * Identification for HTTP headers.
71         *
72         * @var string
73         * @access private
74         */
75        var $title = 'NuSOAP';
76        /**
77         * Version for HTTP headers.
78         *
79         * @var string
80         * @access private
81         */
82        var $version = '0.7.2';
83        /**
84         * CVS revision for HTTP headers.
85         *
86         * @var string
87         * @access private
88         */
89        var $revision = '$Revision: 1.9 $';
90    /**
91     * Current error string (manipulated by getError/setError)
92         *
93         * @var string
94         * @access private
95         */
96        var $error_str = '';
97    /**
98     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
99         *
100         * @var string
101         * @access private
102         */
103    var $debug_str = '';
104    /**
105         * toggles automatic encoding of special characters as entities
106         * (should always be true, I think)
107         *
108         * @var boolean
109         * @access private
110         */
111        var $charencoding = true;
112        /**
113         * the debug level for this instance
114         *
115         * @var integer
116         * @access private
117         */
118        var $debugLevel;
119
120    /**
121        * set schema version
122        *
123        * @var      string
124        * @access   public
125        */
126        var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
127       
128    /**
129        * charset encoding for outgoing messages
130        *
131        * @var      string
132        * @access   public
133        */
134    var $soap_defencoding = 'ISO-8859-1';
135        //var $soap_defencoding = 'UTF-8';
136
137        /**
138        * namespaces in an array of prefix => uri
139        *
140        * this is "seeded" by a set of constants, but it may be altered by code
141        *
142        * @var      array
143        * @access   public
144        */
145        var $namespaces = array(
146                'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
147                'xsd' => 'http://www.w3.org/2001/XMLSchema',
148                'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
149                'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
150                );
151
152        /**
153        * namespaces used in the current context, e.g. during serialization
154        *
155        * @var      array
156        * @access   private
157        */
158        var $usedNamespaces = array();
159
160        /**
161        * XML Schema types in an array of uri => (array of xml type => php type)
162        * is this legacy yet?
163        * no, this is used by the xmlschema class to verify type => namespace mappings.
164        * @var      array
165        * @access   public
166        */
167        var $typemap = array(
168        'http://www.w3.org/2001/XMLSchema' => array(
169                'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
170                'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
171                'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
172                // abstract "any" types
173                'anyType'=>'string','anySimpleType'=>'string',
174                // derived datatypes
175                'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
176                'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
177                'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
178                'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
179        'http://www.w3.org/2000/10/XMLSchema' => array(
180                'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
181                'float'=>'double','dateTime'=>'string',
182                'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
183        'http://www.w3.org/1999/XMLSchema' => array(
184                'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
185                'float'=>'double','dateTime'=>'string',
186                'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
187        'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
188        'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
189    'http://xml.apache.org/xml-soap' => array('Map')
190        );
191
192        /**
193        * XML entities to convert
194        *
195        * @var      array
196        * @access   public
197        * @deprecated
198        * @see  expandEntities
199        */
200        var $xmlEntities = array('quot' => '"','amp' => '&',
201                'lt' => '<','gt' => '>','apos' => "'");
202
203        /**
204        * constructor
205        *
206        * @access       public
207        */
208        function nusoap_base() {
209                $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
210        }
211
212        /**
213        * gets the global debug level, which applies to future instances
214        *
215        * @return       integer Debug level 0-9, where 0 turns off
216        * @access       public
217        */
218        function getGlobalDebugLevel() {
219                return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
220        }
221
222        /**
223        * sets the global debug level, which applies to future instances
224        *
225        * @param        int     $level  Debug level 0-9, where 0 turns off
226        * @access       public
227        */
228        function setGlobalDebugLevel($level) {
229                $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
230        }
231
232        /**
233        * gets the debug level for this instance
234        *
235        * @return       int     Debug level 0-9, where 0 turns off
236        * @access       public
237        */
238        function getDebugLevel() {
239                return $this->debugLevel;
240        }
241
242        /**
243        * sets the debug level for this instance
244        *
245        * @param        int     $level  Debug level 0-9, where 0 turns off
246        * @access       public
247        */
248        function setDebugLevel($level) {
249                $this->debugLevel = $level;
250        }
251
252        /**
253        * adds debug data to the instance debug string with formatting
254        *
255        * @param    string $string debug data
256        * @access   private
257        */
258        function debug($string){
259                if ($this->debugLevel > 0) {
260                        $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
261                }
262        }
263
264        /**
265        * adds debug data to the instance debug string without formatting
266        *
267        * @param    string $string debug data
268        * @access   public
269        */
270        function appendDebug($string){
271                if ($this->debugLevel > 0) {
272                        // it would be nice to use a memory stream here to use
273                        // memory more efficiently
274                        $this->debug_str .= $string;
275                }
276        }
277
278        /**
279        * clears the current debug data for this instance
280        *
281        * @access   public
282        */
283        function clearDebug() {
284                // it would be nice to use a memory stream here to use
285                // memory more efficiently
286                $this->debug_str = '';
287        }
288
289        /**
290        * gets the current debug data for this instance
291        *
292        * @return   debug data
293        * @access   public
294        */
295        function &getDebug() {
296                // it would be nice to use a memory stream here to use
297                // memory more efficiently
298                return $this->debug_str;
299        }
300
301        /**
302        * gets the current debug data for this instance as an XML comment
303        * this may change the contents of the debug data
304        *
305        * @return   debug data as an XML comment
306        * @access   public
307        */
308        function &getDebugAsXMLComment() {
309                // it would be nice to use a memory stream here to use
310                // memory more efficiently
311                while (strpos($this->debug_str, '--')) {
312                        $this->debug_str = str_replace('--', '- -', $this->debug_str);
313                }
314        return "<!--\n" . $this->debug_str . "\n-->";
315        }
316
317        /**
318        * expands entities, e.g. changes '<' to '&lt;'.
319        *
320        * @param        string  $val    The string in which to expand entities.
321        * @access       private
322        */
323        function expandEntities($val) {
324                if ($this->charencoding) {
325                $val = str_replace('&', '&amp;', $val);
326                $val = str_replace("'", '&apos;', $val);
327                $val = str_replace('"', '&quot;', $val);
328                $val = str_replace('<', '&lt;', $val);
329                $val = str_replace('>', '&gt;', $val);
330            }
331            return $val;
332        }
333
334        /**
335        * returns error string if present
336        *
337        * @return   mixed error string or false
338        * @access   public
339        */
340        function getError(){
341                if($this->error_str != ''){
342                        return $this->error_str;
343                }
344                return false;
345        }
346
347        /**
348        * sets error string
349        *
350        * @return   boolean $string error string
351        * @access   private
352        */
353        function setError($str){
354                $this->error_str = $str;
355        }
356
357        /**
358        * detect if array is a simple array or a struct (associative array)
359        *
360        * @param        mixed   $val    The PHP array
361        * @return       string  (arraySimple|arrayStruct)
362        * @access       private
363        */
364        function isArraySimpleOrStruct($val) {
365        $keyList = array_keys($val);
366                foreach ($keyList as $keyListValue) {
367                        if (!is_int($keyListValue)) {
368                                return 'arrayStruct';
369                        }
370                }
371                return 'arraySimple';
372        }
373
374        /**
375        * serializes PHP values in accordance w/ section 5. Type information is
376        * not serialized if $use == 'literal'.
377        *
378        * @param        mixed   $val    The value to serialize
379        * @param        string  $name   The name (local part) of the XML element
380        * @param        string  $type   The XML schema type (local part) for the element
381        * @param        string  $name_ns        The namespace for the name of the XML element
382        * @param        string  $type_ns        The namespace for the type of the element
383        * @param        array   $attributes     The attributes to serialize as name=>value pairs
384        * @param        string  $use    The WSDL "use" (encoded|literal)
385        * @return       string  The serialized element, possibly with child elements
386    * @access   public
387        */
388        function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
389                $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
390                $this->appendDebug('value=' . $this->varDump($val));
391                $this->appendDebug('attributes=' . $this->varDump($attributes));
392               
393        if(is_object($val) && get_class($val) == 'soapval'){
394                return $val->serialize($use);
395        }
396                // force valid name if necessary
397                if (is_numeric($name)) {
398                        $name = '__numeric_' . $name;
399                } elseif (! $name) {
400                        $name = 'noname';
401                }
402                // if name has ns, add ns prefix to name
403                $xmlns = '';
404        if($name_ns){
405                        $prefix = 'nu'.rand(1000,9999);
406                        $name = $prefix.':'.$name;
407                        $xmlns .= " xmlns:$prefix=\"$name_ns\"";
408                }
409                // if type is prefixed, create type prefix
410                if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
411                        // need to fix this. shouldn't default to xsd if no ns specified
412                    // w/o checking against typemap
413                        $type_prefix = 'xsd';
414                } elseif($type_ns){
415                        $type_prefix = 'ns'.rand(1000,9999);
416                        $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
417                }
418                // serialize attributes if present
419                $atts = '';
420                if($attributes){
421                        foreach($attributes as $k => $v){
422                                $atts .= " $k=\"".$this->expandEntities($v).'"';
423                        }
424                }
425                // serialize null value
426                if (is_null($val)) {
427                        if ($use == 'literal') {
428                                // TODO: depends on minOccurs
429                        return "<$name$xmlns $atts/>";
430                } else {
431                                if (isset($type) && isset($type_prefix)) {
432                                        $type_str = " xsi:type=\"$type_prefix:$type\"";
433                                } else {
434                                        $type_str = '';
435                                }
436                        return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
437                }
438                }
439        // serialize if an xsd built-in primitive type
440        if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
441                if (is_bool($val)) {
442                        if ($type == 'boolean') {
443                                $val = $val ? 'true' : 'false';
444                        } elseif (! $val) {
445                                $val = 0;
446                        }
447                        } else if (is_string($val)) {
448                                $val = $this->expandEntities($val);
449                        }
450                        if ($use == 'literal') {
451                        return "<$name$xmlns $atts>$val</$name>";
452                } else {
453                        return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
454                }
455        }
456                // detect type and serialize
457                $xml = '';
458                switch(true) {
459                        case (is_bool($val) || $type == 'boolean'):
460                        if ($type == 'boolean') {
461                                $val = $val ? 'true' : 'false';
462                        } elseif (! $val) {
463                                $val = 0;
464                        }
465                                if ($use == 'literal') {
466                                        $xml .= "<$name$xmlns $atts>$val</$name>";
467                                } else {
468                                        $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
469                                }
470                                break;
471                        case (is_int($val) || is_long($val) || $type == 'int'):
472                                if ($use == 'literal') {
473                                        $xml .= "<$name$xmlns $atts>$val</$name>";
474                                } else {
475                                        $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
476                                }
477                                break;
478                        case (is_float($val)|| is_double($val) || $type == 'float'):
479                                if ($use == 'literal') {
480                                        $xml .= "<$name$xmlns $atts>$val</$name>";
481                                } else {
482                                        $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
483                                }
484                                break;
485                        case (is_string($val) || $type == 'string'):
486                                $val = $this->expandEntities($val);
487                                if ($use == 'literal') {
488                                        $xml .= "<$name$xmlns $atts>$val</$name>";
489                                } else {
490                                        $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
491                                }
492                                break;
493                        case is_object($val):
494                                if (! $name) {
495                                        $name = get_class($val);
496                                        $this->debug("In serialize_val, used class name $name as element name");
497                                } else {
498                                        $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
499                                }
500                                foreach(get_object_vars($val) as $k => $v){
501                                        $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
502                                }
503                                $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
504                                break;
505                        break;
506                        case (is_array($val) || $type):
507                                // detect if struct or array
508                                $valueType = $this->isArraySimpleOrStruct($val);
509                if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
510                                        $i = 0;
511                                        if(is_array($val) && count($val)> 0){
512                                                foreach($val as $v){
513                                if(is_object($v) && get_class($v) ==  'soapval'){
514                                                                $tt_ns = $v->type_ns;
515                                                                $tt = $v->type;
516                                                        } elseif (is_array($v)) {
517                                                                $tt = $this->isArraySimpleOrStruct($v);
518                                                        } else {
519                                                                $tt = gettype($v);
520                                }
521                                                        $array_types[$tt] = 1;
522                                                        // TODO: for literal, the name should be $name
523                                                        $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
524                                                        ++$i;
525                                                }
526                                                if(count($array_types) > 1){
527                                                        $array_typename = 'xsd:anyType';
528                                                } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
529                                                        if ($tt == 'integer') {
530                                                                $tt = 'int';
531                                                        }
532                                                        $array_typename = 'xsd:'.$tt;
533                                                } elseif(isset($tt) && $tt == 'arraySimple'){
534                                                        $array_typename = 'SOAP-ENC:Array';
535                                                } elseif(isset($tt) && $tt == 'arrayStruct'){
536                                                        $array_typename = 'unnamed_struct_use_soapval';
537                                                } else {
538                                                        // if type is prefixed, create type prefix
539                                                        if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
540                                                                 $array_typename = 'xsd:' . $tt;
541                                                        } elseif ($tt_ns) {
542                                                                $tt_prefix = 'ns' . rand(1000, 9999);
543                                                                $array_typename = "$tt_prefix:$tt";
544                                                                $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
545                                                        } else {
546                                                                $array_typename = $tt;
547                                                        }
548                                                }
549                                                $array_type = $i;
550                                                if ($use == 'literal') {
551                                                        $type_str = '';
552                                                } else if (isset($type) && isset($type_prefix)) {
553                                                        $type_str = " xsi:type=\"$type_prefix:$type\"";
554                                                } else {
555                                                        $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
556                                                }
557                                        // empty array
558                                        } else {
559                                                if ($use == 'literal') {
560                                                        $type_str = '';
561                                                } else if (isset($type) && isset($type_prefix)) {
562                                                        $type_str = " xsi:type=\"$type_prefix:$type\"";
563                                                } else {
564                                                        $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
565                                                }
566                                        }
567                                        // TODO: for array in literal, there is no wrapper here
568                                        $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
569                                } else {
570                                        // got a struct
571                                        if(isset($type) && isset($type_prefix)){
572                                                $type_str = " xsi:type=\"$type_prefix:$type\"";
573                                        } else {
574                                                $type_str = '';
575                                        }
576                                        if ($use == 'literal') {
577                                                $xml .= "<$name$xmlns $atts>";
578                                        } else {
579                                                $xml .= "<$name$xmlns$type_str$atts>";
580                                        }
581                                        foreach($val as $k => $v){
582                                                // Apache Map
583                                                if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
584                                                        $xml .= '<item>';
585                                                        $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
586                                                        $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
587                                                        $xml .= '</item>';
588                                                } else {
589                                                        $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
590                                                }
591                                        }
592                                        $xml .= "</$name>";
593                                }
594                                break;
595                        default:
596                                $xml .= 'not detected, got '.gettype($val).' for '.$val;
597                                break;
598                }
599                return $xml;
600        }
601
602    /**
603    * serializes a message
604    *
605    * @param string $body the XML of the SOAP body
606    * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
607    * @param array $namespaces optional the namespaces used in generating the body and headers
608    * @param string $style optional (rpc|document)
609    * @param string $use optional (encoded|literal)
610    * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
611    * @return string the message
612    * @access public
613    */
614    function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
615    // TODO: add an option to automatically run utf8_encode on $body and $headers
616    // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
617    // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
618
619        $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
620        $this->debug("headers:");
621        $this->appendDebug($this->varDump($headers));
622        $this->debug("namespaces:");
623        $this->appendDebug($this->varDump($namespaces));
624
625        // serialize namespaces
626    $ns_string = '';
627        foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
628                $ns_string .= " xmlns:$k=\"$v\"";
629        }
630        if($encodingStyle) {
631                $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
632        }
633
634        // serialize headers
635        if($headers){
636                if (is_array($headers)) {
637                        $xml = '';
638                        foreach ($headers as $header) {
639                                $xml .= $this->serialize_val($header, false, false, false, false, false, $use);
640                        }
641                        $headers = $xml;
642                        $this->debug("In serializeEnvelope, serialzied array of headers to $headers");
643                }
644                $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
645        }
646        // serialize envelope
647        return
648        '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
649        '<SOAP-ENV:Envelope'.$ns_string.">".
650        $headers.
651        "<SOAP-ENV:Body>".
652                $body.
653        "</SOAP-ENV:Body>".
654        "</SOAP-ENV:Envelope>";
655    }
656
657        /**
658         * formats a string to be inserted into an HTML stream
659         *
660         * @param string $str The string to format
661         * @return string The formatted string
662         * @access public
663         * @deprecated
664         */
665    function formatDump($str){
666        global $charset;
667                $str = htmlspecialchars($str,ENT_QUOTES,$charset);
668                return nl2br($str);
669    }
670
671        /**
672        * contracts (changes namespace to prefix) a qualified name
673        *
674        * @param    string $qname qname
675        * @return       string contracted qname
676        * @access   private
677        */
678        function contractQname($qname){
679                // get element namespace
680                //$this->xdebug("Contract $qname");
681                if (strrpos($qname, ':')) {
682                        // get unqualified name
683                        $name = substr($qname, strrpos($qname, ':') + 1);
684                        // get ns
685                        $ns = substr($qname, 0, strrpos($qname, ':'));
686                        $p = $this->getPrefixFromNamespace($ns);
687                        if ($p) {
688                                return $p . ':' . $name;
689                        }
690                        return $qname;
691                } else {
692                        return $qname;
693                }
694        }
695
696        /**
697        * expands (changes prefix to namespace) a qualified name
698        *
699        * @param    string $string qname
700        * @return       string expanded qname
701        * @access   private
702        */
703        function expandQname($qname){
704                // get element prefix
705                if(strpos($qname,':') && !ereg('^http://',$qname)){
706                        // get unqualified name
707                        $name = substr(strstr($qname,':'),1);
708                        // get ns prefix
709                        $prefix = substr($qname,0,strpos($qname,':'));
710                        if(isset($this->namespaces[$prefix])){
711                                return $this->namespaces[$prefix].':'.$name;
712                        } else {
713                                return $qname;
714                        }
715                } else {
716                        return $qname;
717                }
718        }
719
720    /**
721    * returns the local part of a prefixed string
722    * returns the original string, if not prefixed
723    *
724    * @param string $str The prefixed string
725    * @return string The local part
726    * @access public
727    */
728        function getLocalPart($str){
729                if($sstr = strrchr($str,':')){
730                        // get unqualified name
731                        return substr( $sstr, 1 );
732                } else {
733                        return $str;
734                }
735        }
736
737        /**
738    * returns the prefix part of a prefixed string
739    * returns false, if not prefixed
740    *
741    * @param string $str The prefixed string
742    * @return mixed The prefix or false if there is no prefix
743    * @access public
744    */
745        function getPrefix($str){
746                if($pos = strrpos($str,':')){
747                        // get prefix
748                        return substr($str,0,$pos);
749                }
750                return false;
751        }
752
753        /**
754    * pass it a prefix, it returns a namespace
755    *
756    * @param string $prefix The prefix
757    * @return mixed The namespace, false if no namespace has the specified prefix
758    * @access public
759    */
760        function getNamespaceFromPrefix($prefix){
761                if (isset($this->namespaces[$prefix])) {
762                        return $this->namespaces[$prefix];
763                }
764                //$this->setError("No namespace registered for prefix '$prefix'");
765                return false;
766        }
767
768        /**
769    * returns the prefix for a given namespace (or prefix)
770    * or false if no prefixes registered for the given namespace
771    *
772    * @param string $ns The namespace
773    * @return mixed The prefix, false if the namespace has no prefixes
774    * @access public
775    */
776        function getPrefixFromNamespace($ns) {
777                foreach ($this->namespaces as $p => $n) {
778                        if ($ns == $n || $ns == $p) {
779                            $this->usedNamespaces[$p] = $n;
780                                return $p;
781                        }
782                }
783                return false;
784        }
785
786        /**
787    * returns the time in ODBC canonical form with microseconds
788    *
789    * @return string The time in ODBC canonical form with microseconds
790    * @access public
791    */
792        function getmicrotime() {
793                if (function_exists('gettimeofday')) {
794                        $tod = gettimeofday();
795                        $sec = $tod['sec'];
796                        $usec = $tod['usec'];
797                } else {
798                        $sec = time();
799                        $usec = 0;
800                }
801                return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
802        }
803
804        /**
805         * Returns a string with the output of var_dump
806         *
807         * @param mixed $data The variable to var_dump
808         * @return string The output of var_dump
809         * @access public
810         */
811    function varDump($data) {
812                ob_start();
813                var_dump($data);
814                $ret_val = ob_get_contents();
815                ob_end_clean();
816                return $ret_val;
817        }
818}
819
820// XML Schema Datatype Helper Functions
821
822//xsd:dateTime helpers
823
824/**
825* convert unix timestamp to ISO 8601 compliant date string
826*
827* @param    string $timestamp Unix time stamp
828* @access   public
829*/
830function timestamp_to_iso8601($timestamp,$utc=true){
831        $datestr = date('Y-m-d\TH:i:sO',$timestamp);
832        if($utc){
833                $eregStr =
834                '([0-9]{4})-'.  // centuries & years CCYY-
835                '([0-9]{2})-'.  // months MM-
836                '([0-9]{2})'.   // days DD
837                'T'.                    // separator T
838                '([0-9]{2}):'.  // hours hh:
839                '([0-9]{2}):'.  // minutes mm:
840                '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
841                '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
842
843                if(ereg($eregStr,$datestr,$regs)){
844                        return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
845                }
846                return false;
847        } else {
848                return $datestr;
849        }
850}
851
852/**
853* convert ISO 8601 compliant date string to unix timestamp
854*
855* @param    string $datestr ISO 8601 compliant date string
856* @access   public
857*/
858function iso8601_to_timestamp($datestr){
859        $eregStr =
860        '([0-9]{4})-'.  // centuries & years CCYY-
861        '([0-9]{2})-'.  // months MM-
862        '([0-9]{2})'.   // days DD
863        'T'.                    // separator T
864        '([0-9]{2}):'.  // hours hh:
865        '([0-9]{2}):'.  // minutes mm:
866        '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
867        '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
868        if(ereg($eregStr,$datestr,$regs)){
869                // not utc
870                if($regs[8] != 'Z'){
871                        $op = substr($regs[8],0,1);
872                        $h = substr($regs[8],1,2);
873                        $m = substr($regs[8],strlen($regs[8])-2,2);
874                        if($op == '-'){
875                                $regs[4] = $regs[4] + $h;
876                                $regs[5] = $regs[5] + $m;
877                        } elseif($op == '+'){
878                                $regs[4] = $regs[4] - $h;
879                                $regs[5] = $regs[5] - $m;
880                        }
881                }
882                return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
883        } else {
884                return false;
885        }
886}
887
888/**
889* sleeps some number of microseconds
890*
891* @param    string $usec the number of microseconds to sleep
892* @access   public
893* @deprecated
894*/
895function usleepWindows($usec)
896{
897        $start = gettimeofday();
898       
899        do
900        {
901                $stop = gettimeofday();
902                $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
903                + $stop['usec'] - $start['usec'];
904        }
905        while ($timePassed < $usec);
906}
907
908?><?php
909
910
911
912/**
913* Contains information for a SOAP fault.
914* Mainly used for returning faults from deployed functions
915* in a server instance.
916* @author   Dietrich Ayala <dietrich@ganx4.com>
917* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
918* @access public
919*/
920class soap_fault extends nusoap_base {
921        /**
922         * The fault code (client|server)
923         * @var string
924         * @access private
925         */
926        var $faultcode;
927        /**
928         * The fault actor
929         * @var string
930         * @access private
931         */
932        var $faultactor;
933        /**
934         * The fault string, a description of the fault
935         * @var string
936         * @access private
937         */
938        var $faultstring;
939        /**
940         * The fault detail, typically a string or array of string
941         * @var mixed
942         * @access private
943         */
944        var $faultdetail;
945
946        /**
947        * constructor
948    *
949    * @param string $faultcode (client | server)
950    * @param string $faultactor only used when msg routed between multiple actors
951    * @param string $faultstring human readable error message
952    * @param mixed $faultdetail detail, typically a string or array of string
953        */
954        function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
955                parent::nusoap_base();
956                $this->faultcode = $faultcode;
957                $this->faultactor = $faultactor;
958                $this->faultstring = $faultstring;
959                $this->faultdetail = $faultdetail;
960        }
961
962        /**
963        * serialize a fault
964        *
965        * @return       string  The serialization of the fault instance.
966        * @access   public
967        */
968        function serialize(){
969                $ns_string = '';
970                foreach($this->namespaces as $k => $v){
971                        $ns_string .= "\n  xmlns:$k=\"$v\"";
972                }
973                $return_msg =
974                        '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
975                        '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
976                                '<SOAP-ENV:Body>'.
977                                '<SOAP-ENV:Fault>'.
978                                        $this->serialize_val($this->faultcode, 'faultcode').
979                                        $this->serialize_val($this->faultactor, 'faultactor').
980                                        $this->serialize_val($this->faultstring, 'faultstring').
981                                        $this->serialize_val($this->faultdetail, 'detail').
982                                '</SOAP-ENV:Fault>'.
983                                '</SOAP-ENV:Body>'.
984                        '</SOAP-ENV:Envelope>';
985                return $return_msg;
986        }
987}
988
989
990
991?><?php
992
993
994
995/**
996* parses an XML Schema, allows access to it's data, other utility methods
997* no validation... yet.
998* very experimental and limited. As is discussed on XML-DEV, I'm one of the people
999* that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
1000* tutorials I refer to :)
1001*
1002* @author   Dietrich Ayala <dietrich@ganx4.com>
1003* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
1004* @access   public
1005*/
1006class XMLSchema extends nusoap_base  {
1007       
1008        // files
1009        var $schema = '';
1010        var $xml = '';
1011        // namespaces
1012        var $enclosingNamespaces;
1013        // schema info
1014        var $schemaInfo = array();
1015        var $schemaTargetNamespace = '';
1016        // types, elements, attributes defined by the schema
1017        var $attributes = array();
1018        var $complexTypes = array();
1019        var $complexTypeStack = array();
1020        var $currentComplexType = null;
1021        var $elements = array();
1022        var $elementStack = array();
1023        var $currentElement = null;
1024        var $simpleTypes = array();
1025        var $simpleTypeStack = array();
1026        var $currentSimpleType = null;
1027        // imports
1028        var $imports = array();
1029        // parser vars
1030        var $parser;
1031        var $position = 0;
1032        var $depth = 0;
1033        var $depth_array = array();
1034        var $message = array();
1035        var $defaultNamespace = array();
1036   
1037        /**
1038        * constructor
1039        *
1040        * @param    string $schema schema document URI
1041        * @param    string $xml xml document URI
1042        * @param        string $namespaces namespaces defined in enclosing XML
1043        * @access   public
1044        */
1045        function XMLSchema($schema='',$xml='',$namespaces=array()){
1046                parent::nusoap_base();
1047                $this->debug('xmlschema class instantiated, inside constructor');
1048                // files
1049                $this->schema = $schema;
1050                $this->xml = $xml;
1051
1052                // namespaces
1053                $this->enclosingNamespaces = $namespaces;
1054                $this->namespaces = array_merge($this->namespaces, $namespaces);
1055
1056                // parse schema file
1057                if($schema != ''){
1058                        $this->debug('initial schema file: '.$schema);
1059                        $this->parseFile($schema, 'schema');
1060                }
1061
1062                // parse xml file
1063                if($xml != ''){
1064                        $this->debug('initial xml file: '.$xml);
1065                        $this->parseFile($xml, 'xml');
1066                }
1067
1068        }
1069
1070    /**
1071    * parse an XML file
1072    *
1073    * @param string $xml, path/URL to XML file
1074    * @param string $type, (schema | xml)
1075        * @return boolean
1076    * @access public
1077    */
1078        function parseFile($xml,$type){
1079                // parse xml file
1080                if($xml != ""){
1081                        $xmlStr = @join("",@file($xml));
1082                        if($xmlStr == ""){
1083                                $msg = 'Error reading XML from '.$xml;
1084                                $this->setError($msg);
1085                                $this->debug($msg);
1086                        return false;
1087                        } else {
1088                                $this->debug("parsing $xml");
1089                                $this->parseString($xmlStr,$type);
1090                                $this->debug("done parsing $xml");
1091                        return true;
1092                        }
1093                }
1094                return false;
1095        }
1096
1097        /**
1098        * parse an XML string
1099        *
1100        * @param    string $xml path or URL
1101    * @param string $type, (schema|xml)
1102        * @access   private
1103        */
1104        function parseString($xml,$type){
1105                // parse xml string
1106                if($xml != ""){
1107
1108                // Create an XML parser.
1109                $this->parser = xml_parser_create();
1110                // Set the options for parsing the XML data.
1111                xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1112
1113                // Set the object for the parser.
1114                xml_set_object($this->parser, $this);
1115
1116                // Set the element handlers for the parser.
1117                        if($type == "schema"){
1118                        xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1119                        xml_set_character_data_handler($this->parser,'schemaCharacterData');
1120                        } elseif($type == "xml"){
1121                                xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1122                        xml_set_character_data_handler($this->parser,'xmlCharacterData');
1123                        }
1124
1125                    // Parse the XML file.
1126                    if(!xml_parse($this->parser,$xml,true)){
1127                        // Display an error message.
1128                                $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1129                                xml_get_current_line_number($this->parser),
1130                                xml_error_string(xml_get_error_code($this->parser))
1131                                );
1132                                $this->debug($errstr);
1133                                $this->debug("XML payload:\n" . $xml);
1134                                $this->setError($errstr);
1135                }
1136           
1137                        xml_parser_free($this->parser);
1138                } else{
1139                        $this->debug('no xml passed to parseString()!!');
1140                        $this->setError('no xml passed to parseString()!!');
1141                }
1142        }
1143
1144        /**
1145        * start-element handler
1146        *
1147        * @param    string $parser XML parser object
1148        * @param    string $name element name
1149        * @param    string $attrs associative array of attributes
1150        * @access   private
1151        */
1152        function schemaStartElement($parser, $name, $attrs) {
1153               
1154                // position in the total number of elements, starting from 0
1155                $pos = $this->position++;
1156                $depth = $this->depth++;
1157                // set self as current value for this depth
1158                $this->depth_array[$depth] = $pos;
1159                $this->message[$pos] = array('cdata' => '');
1160                if ($depth > 0) {
1161                        $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1162                } else {
1163                        $this->defaultNamespace[$pos] = false;
1164                }
1165
1166                // get element prefix
1167                if($prefix = $this->getPrefix($name)){
1168                        // get unqualified name
1169                        $name = $this->getLocalPart($name);
1170                } else {
1171                $prefix = '';
1172        }
1173               
1174        // loop thru attributes, expanding, and registering namespace declarations
1175        if(count($attrs) > 0){
1176                foreach($attrs as $k => $v){
1177                // if ns declarations, add to class level array of valid namespaces
1178                                if(ereg("^xmlns",$k)){
1179                        //$this->xdebug("$k: $v");
1180                        //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1181                        if($ns_prefix = substr(strrchr($k,':'),1)){
1182                                //$this->xdebug("Add namespace[$ns_prefix] = $v");
1183                                                $this->namespaces[$ns_prefix] = $v;
1184                                        } else {
1185                                                $this->defaultNamespace[$pos] = $v;
1186                                                if (! $this->getPrefixFromNamespace($v)) {
1187                                                        $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1188                                                }
1189                                        }
1190                                        if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1191                                                $this->XMLSchemaVersion = $v;
1192                                                $this->namespaces['xsi'] = $v.'-instance';
1193                                        }
1194                                }
1195                }
1196                foreach($attrs as $k => $v){
1197                // expand each attribute
1198                $k = strpos($k,':') ? $this->expandQname($k) : $k;
1199                $v = strpos($v,':') ? $this->expandQname($v) : $v;
1200                        $eAttrs[$k] = $v;
1201                }
1202                $attrs = $eAttrs;
1203        } else {
1204                $attrs = array();
1205        }
1206                // find status, register data
1207                switch($name){
1208                        case 'all':                     // (optional) compositor content for a complexType
1209                        case 'choice':
1210                        case 'group':
1211                        case 'sequence':
1212                                //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1213                                $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1214                                //if($name == 'all' || $name == 'sequence'){
1215                                //      $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1216                                //}
1217                        break;
1218                        case 'attribute':       // complexType attribute
1219                //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1220                $this->xdebug("parsing attribute:");
1221                $this->appendDebug($this->varDump($attrs));
1222                                if (!isset($attrs['form'])) {
1223                                        $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1224                                }
1225                if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1226                                        $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1227                                        if (!strpos($v, ':')) {
1228                                                // no namespace in arrayType attribute value...
1229                                                if ($this->defaultNamespace[$pos]) {
1230                                                        // ...so use the default
1231                                                        $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1232                                                }
1233                                        }
1234                }
1235                if(isset($attrs['name'])){
1236                                        $this->attributes[$attrs['name']] = $attrs;
1237                                        $aname = $attrs['name'];
1238                                } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1239                                        if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1240                                $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1241                        } else {
1242                                $aname = '';
1243                        }
1244                                } elseif(isset($attrs['ref'])){
1245                                        $aname = $attrs['ref'];
1246                    $this->attributes[$attrs['ref']] = $attrs;
1247                                }
1248               
1249                                if($this->currentComplexType){  // This should *always* be
1250                                        $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1251                                }
1252                                // arrayType attribute
1253                                if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1254                                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1255                        $prefix = $this->getPrefix($aname);
1256                                        if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1257                                                $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1258                                        } else {
1259                                                $v = '';
1260                                        }
1261                    if(strpos($v,'[,]')){
1262                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1263                    }
1264                    $v = substr($v,0,strpos($v,'[')); // clip the []
1265                    if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1266                        $v = $this->XMLSchemaVersion.':'.$v;
1267                    }
1268                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1269                                }
1270                        break;
1271                        case 'complexContent':  // (optional) content for a complexType
1272                        break;
1273                        case 'complexType':
1274                                array_push($this->complexTypeStack, $this->currentComplexType);
1275                                if(isset($attrs['name'])){
1276                                        $this->xdebug('processing named complexType '.$attrs['name']);
1277                                        //$this->currentElement = false;
1278                                        $this->currentComplexType = $attrs['name'];
1279                                        $this->complexTypes[$this->currentComplexType] = $attrs;
1280                                        $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1281                                        // This is for constructs like
1282                                        //           <complexType name="ListOfString" base="soap:Array">
1283                                        //                <sequence>
1284                                        //                    <element name="string" type="xsd:string"
1285                                        //                        minOccurs="0" maxOccurs="unbounded" />
1286                                        //                </sequence>
1287                                        //            </complexType>
1288                                        if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1289                                                $this->xdebug('complexType is unusual array');
1290                                                $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1291                                        } else {
1292                                                $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1293                                        }
1294                                }else{
1295                                        $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
1296                                        $this->currentComplexType = $this->currentElement . '_ContainedType';
1297                                        //$this->currentElement = false;
1298                                        $this->complexTypes[$this->currentComplexType] = $attrs;
1299                                        $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1300                                        // This is for constructs like
1301                                        //           <complexType name="ListOfString" base="soap:Array">
1302                                        //                <sequence>
1303                                        //                    <element name="string" type="xsd:string"
1304                                        //                        minOccurs="0" maxOccurs="unbounded" />
1305                                        //                </sequence>
1306                                        //            </complexType>
1307                                        if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1308                                                $this->xdebug('complexType is unusual array');
1309                                                $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1310                                        } else {
1311                                                $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1312                                        }
1313                                }
1314                        break;
1315                        case 'element':
1316                                array_push($this->elementStack, $this->currentElement);
1317                                // elements defined as part of a complex type should
1318                                // not really be added to $this->elements, but for some
1319                                // reason, they are
1320                                if (!isset($attrs['form'])) {
1321                                        $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1322                                }
1323                                if(isset($attrs['type'])){
1324                                        $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1325                                        if (! $this->getPrefix($attrs['type'])) {
1326                                                if ($this->defaultNamespace[$pos]) {
1327                                                        $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1328                                                        $this->xdebug('used default namespace to make type ' . $attrs['type']);
1329                                                }
1330                                        }
1331                                        // This is for constructs like
1332                                        //           <complexType name="ListOfString" base="soap:Array">
1333                                        //                <sequence>
1334                                        //                    <element name="string" type="xsd:string"
1335                                        //                        minOccurs="0" maxOccurs="unbounded" />
1336                                        //                </sequence>
1337                                        //            </complexType>
1338                                        if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1339                                                $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1340                                                $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1341                                        }
1342                                        $this->currentElement = $attrs['name'];
1343                                        $this->elements[ $attrs['name'] ] = $attrs;
1344                                        $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1345                                        $ename = $attrs['name'];
1346                                } elseif(isset($attrs['ref'])){
1347                                        $this->xdebug("processing element as ref to ".$attrs['ref']);
1348                                        $this->currentElement = "ref to ".$attrs['ref'];
1349                                        $ename = $this->getLocalPart($attrs['ref']);
1350                                } else {
1351                                        $this->xdebug("processing untyped element ".$attrs['name']);
1352                                        $this->currentElement = $attrs['name'];
1353                                        $this->elements[ $attrs['name'] ] = $attrs;
1354                                        $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1355                                        $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1356                                        $this->elements[ $attrs['name'] ]['type'] = $attrs['type'];
1357                                        $ename = $attrs['name'];
1358                                }
1359                                if(isset($ename) && $this->currentComplexType){
1360                                        $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1361                                }
1362                        break;
1363                        case 'enumeration':     //      restriction value list member
1364                                $this->xdebug('enumeration ' . $attrs['value']);
1365                                if ($this->currentSimpleType) {
1366                                        $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1367                                } elseif ($this->currentComplexType) {
1368                                        $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1369                                }
1370                        break;
1371                        case 'extension':       // simpleContent or complexContent type extension
1372                                $this->xdebug('extension ' . $attrs['base']);
1373                                if ($this->currentComplexType) {
1374                                        $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1375                                }
1376                        break;
1377                        case 'import':
1378                            if (isset($attrs['schemaLocation'])) {
1379                                        //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1380                    $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1381                                } else {
1382                                        //$this->xdebug('import namespace ' . $attrs['namespace']);
1383                    $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1384                                        if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1385                                                $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1386                                        }
1387                                }
1388                        break;
1389                        case 'list':    // simpleType value list
1390                        break;
1391                        case 'restriction':     // simpleType, simpleContent or complexContent value restriction
1392                                $this->xdebug('restriction ' . $attrs['base']);
1393                                if($this->currentSimpleType){
1394                                        $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1395                                } elseif($this->currentComplexType){
1396                                        $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1397                                        if(strstr($attrs['base'],':') == ':Array'){
1398                                                $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1399                                        }
1400                                }
1401                        break;
1402                        case 'schema':
1403                                $this->schemaInfo = $attrs;
1404                                $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1405                                if (isset($attrs['targetNamespace'])) {
1406                                        $this->schemaTargetNamespace = $attrs['targetNamespace'];
1407                                }
1408                                if (!isset($attrs['elementFormDefault'])) {
1409                                        $this->schemaInfo['elementFormDefault'] = 'unqualified';
1410                                }
1411                                if (!isset($attrs['attributeFormDefault'])) {
1412                                        $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1413                                }
1414                        break;
1415                        case 'simpleContent':   // (optional) content for a complexType
1416                        break;
1417                        case 'simpleType':
1418                                array_push($this->simpleTypeStack, $this->currentSimpleType);
1419                                if(isset($attrs['name'])){
1420                                        $this->xdebug("processing simpleType for name " . $attrs['name']);
1421                                        $this->currentSimpleType = $attrs['name'];
1422                                        $this->simpleTypes[ $attrs['name'] ] = $attrs;
1423                                        $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1424                                        $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1425                                } else {
1426                                        $this->xdebug('processing unnamed simpleType for element '.$this->currentElement);
1427                                        $this->currentSimpleType = $this->currentElement . '_ContainedType';
1428                                        //$this->currentElement = false;
1429                                        $this->simpleTypes[$this->currentSimpleType] = $attrs;
1430                                        $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1431                                }
1432                        break;
1433                        case 'union':   // simpleType type list
1434                        break;
1435                        default:
1436                                //$this->xdebug("do not have anything to do for element $name");
1437                }
1438        }
1439
1440        /**
1441        * end-element handler
1442        *
1443        * @param    string $parser XML parser object
1444        * @param    string $name element name
1445        * @access   private
1446        */
1447        function schemaEndElement($parser, $name) {
1448                // bring depth down a notch
1449                $this->depth--;
1450                // position of current element is equal to the last value left in depth_array for my depth
1451                if(isset($this->depth_array[$this->depth])){
1452                $pos = $this->depth_array[$this->depth];
1453        }
1454                // get element prefix
1455                if ($prefix = $this->getPrefix($name)){
1456                        // get unqualified name
1457                        $name = $this->getLocalPart($name);
1458                } else {
1459                $prefix = '';
1460        }
1461                // move on...
1462                if($name == 'complexType'){
1463                        $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1464                        $this->currentComplexType = array_pop($this->complexTypeStack);
1465                        //$this->currentElement = false;
1466                }
1467                if($name == 'element'){
1468                        $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1469                        $this->currentElement = array_pop($this->elementStack);
1470                }
1471                if($name == 'simpleType'){
1472                        $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1473                        $this->currentSimpleType = array_pop($this->simpleTypeStack);
1474                }
1475        }
1476
1477        /**
1478        * element content handler
1479        *
1480        * @param    string $parser XML parser object
1481        * @param    string $data element content
1482        * @access   private
1483        */
1484        function schemaCharacterData($parser, $data){
1485                $pos = $this->depth_array[$this->depth - 1];
1486                $this->message[$pos]['cdata'] .= $data;
1487        }
1488
1489        /**
1490        * serialize the schema
1491        *
1492        * @access   public
1493        */
1494        function serializeSchema(){
1495
1496                $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1497                $xml = '';
1498                // imports
1499                if (sizeof($this->imports) > 0) {
1500                        foreach($this->imports as $ns => $list) {
1501                                foreach ($list as $ii) {
1502                                        if ($ii['location'] != '') {
1503                                                $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1504                                        } else {
1505                                                $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1506                                        }
1507                                }
1508                        } 
1509                } 
1510                // complex types
1511                foreach($this->complexTypes as $typeName => $attrs){
1512                        $contentStr = '';
1513                        // serialize child elements
1514                        if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1515                                foreach($attrs['elements'] as $element => $eParts){
1516                                        if(isset($eParts['ref'])){
1517                                                $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1518                                        } else {
1519                                                $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1520                                                foreach ($eParts as $aName => $aValue) {
1521                                                        // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1522                                                        if ($aName != 'name' && $aName != 'type') {
1523                                                                $contentStr .= " $aName=\"$aValue\"";
1524                                                        }
1525                                                }
1526                                                $contentStr .= "/>\n";
1527                                        }
1528                                }
1529                                // compositor wraps elements
1530                                if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1531                                        $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1532                                }
1533                        }
1534                        // attributes
1535                        if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1536                                foreach($attrs['attrs'] as $attr => $aParts){
1537                                        $contentStr .= "    <$schemaPrefix:attribute";
1538                                        foreach ($aParts as $a => $v) {
1539                                                if ($a == 'ref' || $a == 'type') {
1540                                                        $contentStr .= " $a=\"".$this->contractQName($v).'"';
1541                                                } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1542                                                        $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1543                                                        $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1544                                                } else {
1545                                                        $contentStr .= " $a=\"$v\"";
1546                                                }
1547                                        }
1548                                        $contentStr .= "/>\n";
1549                                }
1550                        }
1551                        // if restriction
1552                        if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1553                                $contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1554                                // complex or simple content
1555                                if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1556                                        $contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1557                                }
1558                        }
1559                        // finalize complex type
1560                        if($contentStr != ''){
1561                                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1562                        } else {
1563                                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1564                        }
1565                        $xml .= $contentStr;
1566                }
1567                // simple types
1568                if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1569                        foreach($this->simpleTypes as $typeName => $eParts){
1570                                $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n";
1571                                if (isset($eParts['enumeration'])) {
1572                                        foreach ($eParts['enumeration'] as $e) {
1573                                                $xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1574                                        }
1575                                }
1576                                $xml .= " </$schemaPrefix:simpleType>";
1577                        }
1578                }
1579                // elements
1580                if(isset($this->elements) && count($this->elements) > 0){
1581                        foreach($this->elements as $element => $eParts){
1582                                $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1583                        }
1584                }
1585                // attributes
1586                if(isset($this->attributes) && count($this->attributes) > 0){
1587                        foreach($this->attributes as $attr => $aParts){
1588                                $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1589                        }
1590                }
1591                // finish 'er up
1592                $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1593                foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1594                        $el .= " xmlns:$nsp=\"$ns\"\n";
1595                }
1596                $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1597                return $xml;
1598        }
1599
1600        /**
1601        * adds debug data to the clas level debug string
1602        *
1603        * @param    string $string debug data
1604        * @access   private
1605        */
1606        function xdebug($string){
1607                $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1608        }
1609
1610    /**
1611    * get the PHP type of a user defined type in the schema
1612    * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1613    * returns false if no type exists, or not w/ the given namespace
1614    * else returns a string that is either a native php type, or 'struct'
1615    *
1616    * @param string $type, name of defined type
1617    * @param string $ns, namespace of type
1618    * @return mixed
1619    * @access public
1620    * @deprecated
1621    */
1622        function getPHPType($type,$ns){
1623                if(isset($this->typemap[$ns][$type])){
1624                        //print "found type '$type' and ns $ns in typemap<br />";
1625                        return $this->typemap[$ns][$type];
1626                } elseif(isset($this->complexTypes[$type])){
1627                        //print "getting type '$type' and ns $ns from complexTypes array<br />";
1628                        return $this->complexTypes[$type]['phpType'];
1629                }
1630                return false;
1631        }
1632
1633        /**
1634    * returns an associative array of information about a given type
1635    * returns false if no type exists by the given name
1636    *
1637        *       For a complexType typeDef = array(
1638        *       'restrictionBase' => '',
1639        *       'phpType' => '',
1640        *       'compositor' => '(sequence|all)',
1641        *       'elements' => array(), // refs to elements array
1642        *       'attrs' => array() // refs to attributes array
1643        *       ... and so on (see addComplexType)
1644        *       )
1645        *
1646        *   For simpleType or element, the array has different keys.
1647    *
1648    * @param string
1649    * @return mixed
1650    * @access public
1651    * @see addComplexType
1652    * @see addSimpleType
1653    * @see addElement
1654    */
1655        function getTypeDef($type){
1656                //$this->debug("in getTypeDef for type $type");
1657                if(isset($this->complexTypes[$type])){
1658                        $this->xdebug("in getTypeDef, found complexType $type");
1659                        return $this->complexTypes[$type];
1660                } elseif(isset($this->simpleTypes[$type])){
1661                        $this->xdebug("in getTypeDef, found simpleType $type");
1662                        if (!isset($this->simpleTypes[$type]['phpType'])) {
1663                                // get info for type to tack onto the simple type
1664                                // TODO: can this ever really apply (i.e. what is a simpleType really?)
1665                                $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1666                                $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1667                                $etype = $this->getTypeDef($uqType);
1668                                if ($etype) {
1669                                        $this->xdebug("in getTypeDef, found type for simpleType $type:");
1670                                        $this->xdebug($this->varDump($etype));
1671                                        if (isset($etype['phpType'])) {
1672                                                $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1673                                        }
1674                                        if (isset($etype['elements'])) {
1675                                                $this->simpleTypes[$type]['elements'] = $etype['elements'];
1676                                        }
1677                                }
1678                        }
1679                        return $this->simpleTypes[$type];
1680                } elseif(isset($this->elements[$type])){
1681                        $this->xdebug("in getTypeDef, found element $type");
1682                        if (!isset($this->elements[$type]['phpType'])) {
1683                                // get info for type to tack onto the element
1684                                $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1685                                $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1686                                $etype = $this->getTypeDef($uqType);
1687                                if ($etype) {
1688                                        $this->xdebug("in getTypeDef, found type for element $type:");
1689                                        $this->xdebug($this->varDump($etype));
1690                                        if (isset($etype['phpType'])) {
1691                                                $this->elements[$type]['phpType'] = $etype['phpType'];
1692                                        }
1693                                        if (isset($etype['elements'])) {
1694                                                $this->elements[$type]['elements'] = $etype['elements'];
1695                                        }
1696                                } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1697                                        $this->xdebug("in getTypeDef, element $type is an XSD type");
1698                                        $this->elements[$type]['phpType'] = 'scalar';
1699                                }
1700                        }
1701                        return $this->elements[$type];
1702                } elseif(isset($this->attributes[$type])){
1703                        $this->xdebug("in getTypeDef, found attribute $type");
1704                        return $this->attributes[$type];
1705                } elseif (ereg('_ContainedType$', $type)) {
1706                        $this->xdebug("in getTypeDef, have an untyped element $type");
1707                        $typeDef['typeClass'] = 'simpleType';
1708                        $typeDef['phpType'] = 'scalar';
1709                        $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1710                        return $typeDef;
1711                }
1712                $this->xdebug("in getTypeDef, did not find $type");
1713                return false;
1714        }
1715
1716        /**
1717    * returns a sample serialization of a given type, or false if no type by the given name
1718    *
1719    * @param string $type, name of type
1720    * @return mixed
1721    * @access public
1722    * @deprecated
1723    */
1724    function serializeTypeDef($type){
1725        //print "in sTD() for type $type<br />";
1726        if($typeDef = $this->getTypeDef($type)){
1727                $str .= '<'.$type;
1728            if(is_array($typeDef['attrs'])){
1729                foreach($attrs as $attName => $data){
1730                    $str .= " $attName=\"{type = ".$data['type']."}\"";
1731                }
1732            }
1733            $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1734            if(count($typeDef['elements']) > 0){
1735                $str .= ">";
1736                foreach($typeDef['elements'] as $element => $eData){
1737                    $str .= $this->serializeTypeDef($element);
1738                }
1739                $str .= "</$type>";
1740            } elseif($typeDef['typeClass'] == 'element') {
1741                $str .= "></$type>";
1742            } else {
1743                $str .= "/>";
1744            }
1745                        return $str;
1746        }
1747        return false;
1748    }
1749
1750    /**
1751    * returns HTML form elements that allow a user
1752    * to enter values for creating an instance of the given type.
1753    *
1754    * @param string $name, name for type instance
1755    * @param string $type, name of type
1756    * @return string
1757    * @access public
1758    * @deprecated
1759        */
1760        function typeToForm($name,$type){
1761                // get typedef
1762                if($typeDef = $this->getTypeDef($type)){
1763                        // if struct
1764                        if($typeDef['phpType'] == 'struct'){
1765                                $buffer .= '<table>';
1766                                foreach($typeDef['elements'] as $child => $childDef){
1767                                        $buffer .= "
1768                                        <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1769                                        <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1770                                }
1771                                $buffer .= '</table>';
1772                        // if array
1773                        } elseif($typeDef['phpType'] == 'array'){
1774                                $buffer .= '<table>';
1775                                for($i=0;$i < 3; $i++){
1776                                        $buffer .= "
1777                                        <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1778                                        <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1779                                }
1780                                $buffer .= '</table>';
1781                        // if scalar
1782                        } else {
1783                                $buffer .= "<input type='text' name='parameters[$name]'>";
1784                        }
1785                } else {
1786                        $buffer .= "<input type='text' name='parameters[$name]'>";
1787                }
1788                return $buffer;
1789        }
1790       
1791        /**
1792        * adds a complex type to the schema
1793        *
1794        * example: array
1795        *
1796        * addType(
1797        *       'ArrayOfstring',
1798        *       'complexType',
1799        *       'array',
1800        *       '',
1801        *       'SOAP-ENC:Array',
1802        *       array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1803        *       'xsd:string'
1804        * );
1805        *
1806        * example: PHP associative array ( SOAP Struct )
1807        *
1808        * addType(
1809        *       'SOAPStruct',
1810        *       'complexType',
1811        *       'struct',
1812        *       'all',
1813        *       array('myVar'=> array('name'=>'myVar','type'=>'string')
1814        * );
1815        *
1816        * @param name
1817        * @param typeClass (complexType|simpleType|attribute)
1818        * @param phpType: currently supported are array and struct (php assoc array)
1819        * @param compositor (all|sequence|choice)
1820        * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1821        * @param elements = array ( name = array(name=>'',type=>'') )
1822        * @param attrs = array(
1823        *       array(
1824        *               'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1825        *               "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1826        *       )
1827        * )
1828        * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1829        * @access public
1830        * @see getTypeDef
1831        */
1832        function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1833                $this->complexTypes[$name] = array(
1834            'name'              => $name,
1835            'typeClass' => $typeClass,
1836            'phpType'   => $phpType,
1837                'compositor'=> $compositor,
1838            'restrictionBase' => $restrictionBase,
1839                'elements'      => $elements,
1840            'attrs'             => $attrs,
1841            'arrayType' => $arrayType
1842                );
1843               
1844                $this->xdebug("addComplexType $name:");
1845                $this->appendDebug($this->varDump($this->complexTypes[$name]));
1846        }
1847       
1848        /**
1849        * adds a simple type to the schema
1850        *
1851        * @param string $name
1852        * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1853        * @param string $typeClass (should always be simpleType)
1854        * @param string $phpType (should always be scalar)
1855        * @param array $enumeration array of values
1856        * @access public
1857        * @see xmlschema
1858        * @see getTypeDef
1859        */
1860        function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1861                $this->simpleTypes[$name] = array(
1862            'name'                      => $name,
1863            'typeClass'         => $typeClass,
1864            'phpType'           => $phpType,
1865            'type'                      => $restrictionBase,
1866            'enumeration'       => $enumeration
1867                );
1868               
1869                $this->xdebug("addSimpleType $name:");
1870                $this->appendDebug($this->varDump($this->simpleTypes[$name]));
1871        }
1872
1873        /**
1874        * adds an element to the schema
1875        *
1876        * @param array $attrs attributes that must include name and type
1877        * @see xmlschema
1878        * @access public
1879        */
1880        function addElement($attrs) {
1881                if (! $this->getPrefix($attrs['type'])) {
1882                        $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1883                }
1884                $this->elements[ $attrs['name'] ] = $attrs;
1885                $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1886               
1887                $this->xdebug("addElement " . $attrs['name']);
1888                $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1889        }
1890}
1891
1892
1893
1894?><?php
1895
1896
1897
1898/**
1899* For creating serializable abstractions of native PHP types.  This class
1900* allows element name/namespace, XSD type, and XML attributes to be
1901* associated with a value.  This is extremely useful when WSDL is not
1902* used, but is also useful when WSDL is used with polymorphic types, including
1903* xsd:anyType and user-defined types.
1904*
1905* @author   Dietrich Ayala <dietrich@ganx4.com>
1906* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
1907* @access   public
1908*/
1909class soapval extends nusoap_base {
1910        /**
1911         * The XML element name
1912         *
1913         * @var string
1914         * @access private
1915         */
1916        var $name;
1917        /**
1918         * The XML type name (string or false)
1919         *
1920         * @var mixed
1921         * @access private
1922         */
1923        var $type;
1924        /**
1925         * The PHP value
1926         *
1927         * @var mixed
1928         * @access private
1929         */
1930        var $value;
1931        /**
1932         * The XML element namespace (string or false)
1933         *
1934         * @var mixed
1935         * @access private
1936         */
1937        var $element_ns;
1938        /**
1939         * The XML type namespace (string or false)
1940         *
1941         * @var mixed
1942         * @access private
1943         */
1944        var $type_ns;
1945        /**
1946         * The XML element attributes (array or false)
1947         *
1948         * @var mixed
1949         * @access private
1950         */
1951        var $attributes;
1952
1953        /**
1954        * constructor
1955        *
1956        * @param    string $name optional name
1957        * @param    mixed $type optional type name
1958        * @param        mixed $value optional value
1959        * @param        mixed $element_ns optional namespace of value
1960        * @param        mixed $type_ns optional namespace of type
1961        * @param        mixed $attributes associative array of attributes to add to element serialization
1962        * @access   public
1963        */
1964        function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1965                parent::nusoap_base();
1966                $this->name = $name;
1967                $this->type = $type;
1968                $this->value = $value;
1969                $this->element_ns = $element_ns;
1970                $this->type_ns = $type_ns;
1971                $this->attributes = $attributes;
1972    }
1973
1974        /**
1975        * return serialized value
1976        *
1977        * @param        string $use The WSDL use value (encoded|literal)
1978        * @return       string XML data
1979        * @access   public
1980        */
1981        function serialize($use='encoded') {
1982                return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1983    }
1984
1985        /**
1986        * decodes a soapval object into a PHP native type
1987        *
1988        * @return       mixed
1989        * @access   public
1990        */
1991        function decode(){
1992                return $this->value;
1993        }
1994}
1995
1996
1997
1998?><?php
1999
2000
2001
2002/**
2003* transport class for sending/receiving data via HTTP and HTTPS
2004* NOTE: PHP must be compiled with the CURL extension for HTTPS support
2005*
2006* @author   Dietrich Ayala <dietrich@ganx4.com>
2007* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
2008* @access public
2009*/
2010class soap_transport_http extends nusoap_base {
2011
2012        var $url = '';
2013        var $uri = '';
2014        var $digest_uri = '';
2015        var $scheme = '';
2016        var $host = '';
2017        var $port = '';
2018        var $path = '';
2019        var $request_method = 'POST';
2020        var $protocol_version = '1.0';
2021        var $encoding = '';
2022        var $outgoing_headers = array();
2023        var $incoming_headers = array();
2024        var $incoming_cookies = array();
2025        var $outgoing_payload = '';
2026        var $incoming_payload = '';
2027        var $useSOAPAction = true;
2028        var $persistentConnection = false;
2029        var $ch = false;        // cURL handle
2030        var $username = '';
2031        var $password = '';
2032        var $authtype = '';
2033        var $digestRequest = array();
2034        var $certRequest = array();     // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
2035                                                                // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2036                                                                // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2037                                                                // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2038                                                                // passphrase: SSL key password/passphrase
2039                                                                // verifypeer: default is 1
2040                                                                // verifyhost: default is 1
2041
2042        /**
2043        * constructor
2044        */
2045        function soap_transport_http($url){
2046                parent::nusoap_base();
2047                $this->setURL($url);
2048                ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2049                $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
2050                $this->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']);
2051        }
2052
2053        function setURL($url) {
2054                $this->url = $url;
2055
2056                $u = parse_url($url);
2057                foreach($u as $k => $v){
2058                        $this->debug("$k = $v");
2059                        $this->$k = $v;
2060                }
2061               
2062                // add any GET params to path
2063                if(isset($u['query']) && $u['query'] != ''){
2064            $this->path .= '?' . $u['query'];
2065                }
2066               
2067                // set default port
2068                if(!isset($u['port'])){
2069                        if($u['scheme'] == 'https'){
2070                                $this->port = 443;
2071                        } else {
2072                                $this->port = 80;
2073                        }
2074                }
2075               
2076                $this->uri = $this->path;
2077                $this->digest_uri = $this->uri;
2078               
2079                // build headers
2080                if (!isset($u['port'])) {
2081                        $this->outgoing_headers['Host'] = $this->host;
2082                } else {
2083                        $this->outgoing_headers['Host'] = $this->host.':'.$this->port;
2084                }
2085                $this->debug('set Host: ' . $this->outgoing_headers['Host']);
2086
2087                if (isset($u['user']) && $u['user'] != '') {
2088                        $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2089                }
2090        }
2091       
2092        function connect($connection_timeout=0,$response_timeout=30){
2093                // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2094                // "regular" socket.
2095                // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2096                //       loaded), and until PHP5 stream_get_wrappers is not available.
2097//              if ($this->scheme == 'https') {
2098//                      if (version_compare(phpversion(), '4.3.0') >= 0) {
2099//                              if (extension_loaded('openssl')) {
2100//                                      $this->scheme = 'ssl';
2101//                                      $this->debug('Using SSL over OpenSSL');
2102//                              }
2103//                      }
2104//              }
2105                $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2106          if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2107                // use persistent connection
2108                if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2109                        if (!feof($this->fp)) {
2110                                $this->debug('Re-use persistent connection');
2111                                return true;
2112                        }
2113                        fclose($this->fp);
2114                        $this->debug('Closed persistent connection at EOF');
2115                }
2116
2117                // munge host if using OpenSSL
2118                if ($this->scheme == 'ssl') {
2119                        $host = 'ssl://' . $this->host;
2120                } else {
2121                        $host = $this->host;
2122                }
2123                $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2124
2125                // open socket
2126                if($connection_timeout > 0){
2127                        $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2128                } else {
2129                        $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2130                }
2131               
2132                // test pointer
2133                if(!$this->fp) {
2134                        $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2135                        if ($this->errno) {
2136                                $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2137                        } else {
2138                                $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2139                        }
2140                        $this->debug($msg);
2141                        $this->setError($msg);
2142                        return false;
2143                }
2144               
2145                // set response timeout
2146                $this->debug('set response timeout to ' . $response_timeout);
2147                socket_set_timeout( $this->fp, $response_timeout);
2148
2149                $this->debug('socket connected');
2150                return true;
2151          } else if ($this->scheme == 'https') {
2152                if (!extension_loaded('curl')) {
2153                        $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2154                        return false;
2155                }
2156                $this->debug('connect using https');
2157                // init CURL
2158                $this->ch = curl_init();
2159                // set url
2160                $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
2161                // add path
2162                $hostURL .= $this->path;
2163                curl_setopt($this->ch, CURLOPT_URL, $hostURL);
2164                // follow location headers (re-directs)
2165                curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
2166                // ask for headers in the response output
2167                curl_setopt($this->ch, CURLOPT_HEADER, 1);
2168                // ask for the response output as the return value
2169                curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
2170                // encode
2171                // We manage this ourselves through headers and encoding
2172//              if(function_exists('gzuncompress')){
2173//                      curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
2174//              }
2175                // persistent connection
2176                if ($this->persistentConnection) {
2177                        // The way we send data, we cannot use persistent connections, since
2178                        // there will be some "junk" at the end of our request.
2179                        //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
2180                        $this->persistentConnection = false;
2181                        $this->outgoing_headers['Connection'] = 'close';
2182                        $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2183                }
2184                // set timeout
2185                if ($connection_timeout != 0) {
2186                        curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
2187                }
2188                // TODO: cURL has added a connection timeout separate from the response timeout
2189                //if ($connection_timeout != 0) {
2190                //      curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2191                //}
2192                //if ($response_timeout != 0) {
2193                //      curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);
2194                //}
2195
2196                // recent versions of cURL turn on peer/host checking by default,
2197                // while PHP binaries are not compiled with a default location for the
2198                // CA cert bundle, so disable peer/host checking.
2199//curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');           
2200                curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
2201                curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
2202
2203                // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2204                if ($this->authtype == 'certificate') {
2205                        if (isset($this->certRequest['cainfofile'])) {
2206                                curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2207                        }
2208                        if (isset($this->certRequest['verifypeer'])) {
2209                                curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2210                        } else {
2211                                curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
2212                        }
2213                        if (isset($this->certRequest['verifyhost'])) {
2214                                curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2215                        } else {
2216                                curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
2217                        }
2218                        if (isset($this->certRequest['sslcertfile'])) {
2219                                curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2220                        }
2221                        if (isset($this->certRequest['sslkeyfile'])) {
2222                                curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2223                        }
2224                        if (isset($this->certRequest['passphrase'])) {
2225                                curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $this->certRequest['passphrase']);
2226                        }
2227                }
2228                $this->debug('cURL connection set up');
2229                return true;
2230          } else {
2231                $this->setError('Unknown scheme ' . $this->scheme);
2232                $this->debug('Unknown scheme ' . $this->scheme);
2233                return false;
2234          }
2235        }
2236       
2237        /**
2238        * send the SOAP message via HTTP
2239        *
2240        * @param    string $data message data
2241        * @param    integer $timeout set connection timeout in seconds
2242        * @param        integer $response_timeout set response timeout in seconds
2243        * @param        array $cookies cookies to send
2244        * @return       string data
2245        * @access   public
2246        */
2247        function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2248               
2249                $this->debug('entered send() with data of length: '.strlen($data));
2250
2251                $this->tryagain = true;
2252                $tries = 0;
2253                while ($this->tryagain) {
2254                        $this->tryagain = false;
2255                        if ($tries++ < 2) {
2256                                // make connnection
2257                                if (!$this->connect($timeout, $response_timeout)){
2258                                        return false;
2259                                }
2260                               
2261                                // send request
2262                                if (!$this->sendRequest($data, $cookies)){
2263                                        return false;
2264                                }
2265                               
2266                                // get response
2267                                $respdata = $this->getResponse();
2268                        } else {
2269                                $this->setError('Too many tries to get an OK response');
2270                        }
2271                }               
2272                $this->debug('end of send()');
2273                return $respdata;
2274        }
2275
2276
2277        /**
2278        * send the SOAP message via HTTPS 1.0 using CURL
2279        *
2280        * @param    string $msg message data
2281        * @param    integer $timeout set connection timeout in seconds
2282        * @param        integer $response_timeout set response timeout in seconds
2283        * @param        array $cookies cookies to send
2284        * @return       string data
2285        * @access   public
2286        */
2287        function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2288                return $this->send($data, $timeout, $response_timeout, $cookies);
2289        }
2290       
2291        /**
2292        * if authenticating, set user credentials here
2293        *
2294        * @param    string $username
2295        * @param    string $password
2296        * @param        string $authtype (basic, digest, certificate)
2297        * @param        array $digestRequest (keys must be nonce, nc, realm, qop)
2298        * @param        array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2299        * @access   public
2300        */
2301        function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2302                $this->debug("Set credentials for authtype $authtype");
2303                // cf. RFC 2617
2304                if ($authtype == 'basic') {
2305                        $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
2306                } elseif ($authtype == 'digest') {
2307                        if (isset($digestRequest['nonce'])) {
2308                                $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2309                               
2310                                // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2311       
2312                                // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2313                                $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2314       
2315                                // H(A1) = MD5(A1)
2316                                $HA1 = md5($A1);
2317       
2318                                // A2 = Method ":" digest-uri-value
2319                                $A2 = 'POST:' . $this->digest_uri;
2320       
2321                                // H(A2)
2322                                $HA2 =  md5($A2);
2323       
2324                                // KD(secret, data) = H(concat(secret, ":", data))
2325                                // if qop == auth:
2326                                // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2327                                //                              ":" nc-value
2328                                //                              ":" unq(cnonce-value)
2329                                //                              ":" unq(qop-value)
2330                                //                              ":" H(A2)
2331                                //                            ) <">
2332                                // if qop is missing,
2333                                // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2334       
2335                                $unhashedDigest = '';
2336                                $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2337                                $cnonce = $nonce;
2338                                if ($digestRequest['qop'] != '') {
2339                                        $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2340                                } else {
2341                                        $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2342                                }
2343       
2344                                $hashedDigest = md5($unhashedDigest);
2345       
2346                                $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
2347                        }
2348                } elseif ($authtype == 'certificate') {
2349                        $this->certRequest = $certRequest;
2350                }
2351                $this->username = $username;
2352                $this->password = $password;
2353                $this->authtype = $authtype;
2354                $this->digestRequest = $digestRequest;
2355               
2356                if (isset($this->outgoing_headers['Authorization'])) {
2357                        $this->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
2358                } else {
2359                        $this->debug('Authorization header not set');
2360                }
2361        }
2362       
2363        /**
2364        * set the soapaction value
2365        *
2366        * @param    string $soapaction
2367        * @access   public
2368        */
2369        function setSOAPAction($soapaction) {
2370                $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
2371                $this->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']);
2372        }
2373       
2374        /**
2375        * use http encoding
2376        *
2377        * @param    string $enc encoding style. supported values: gzip, deflate, or both
2378        * @access   public
2379        */
2380        function setEncoding($enc='gzip, deflate') {
2381                if (function_exists('gzdeflate')) {
2382                        $this->protocol_version = '1.1';
2383                        $this->outgoing_headers['Accept-Encoding'] = $enc;
2384                        $this->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']);
2385                        if (!isset($this->outgoing_headers['Connection'])) {
2386                                $this->outgoing_headers['Connection'] = 'close';
2387                                $this->persistentConnection = false;
2388                                $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2389                        }
2390                        $magic_quotes = get_magic_quotes_runtime();
2391                        if($magic_quotes){
2392                                ini_set('magic_quotes_runtime', 0);
2393                        }
2394                        // deprecated
2395                        $this->encoding = $enc;
2396                }
2397        }
2398       
2399        /**
2400        * set proxy info here
2401        *
2402        * @param    string $proxyhost
2403        * @param    string $proxyport
2404        * @param        string $proxyusername
2405        * @param        string $proxypassword
2406        * @access   public
2407        */
2408        function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
2409                $this->uri = $this->url;
2410                $this->host = $proxyhost;
2411                $this->port = $proxyport;
2412                if ($proxyusername != '' && $proxypassword != '') {
2413                        $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
2414                        $this->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']);
2415                }
2416        }
2417       
2418        /**
2419        * decode a string that is encoded w/ "chunked' transfer encoding
2420        * as defined in RFC2068 19.4.6
2421        *
2422        * @param    string $buffer
2423        * @param    string $lb
2424        * @returns      string
2425        * @access   public
2426        * @deprecated
2427        */
2428        function decodeChunked($buffer, $lb){
2429                // length := 0
2430                $length = 0;
2431                $new = '';
2432               
2433                // read chunk-size, chunk-extension (if any) and CRLF
2434                // get the position of the linebreak
2435                $chunkend = strpos($buffer, $lb);
2436                if ($chunkend == FALSE) {
2437                        $this->debug('no linebreak found in decodeChunked');
2438                        return $new;
2439                }
2440                $temp = substr($buffer,0,$chunkend);
2441                $chunk_size = hexdec( trim($temp) );
2442                $chunkstart = $chunkend + strlen($lb);
2443                // while (chunk-size > 0) {
2444                while ($chunk_size > 0) {
2445                        $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2446                        $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2447                       
2448                        // Just in case we got a broken connection
2449                        if ($chunkend == FALSE) {
2450                            $chunk = substr($buffer,$chunkstart);
2451                                // append chunk-data to entity-body
2452                        $new .= $chunk;
2453                            $length += strlen($chunk);
2454                            break;
2455                        }
2456                       
2457                        // read chunk-data and CRLF
2458                        $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2459                        // append chunk-data to entity-body
2460                        $new .= $chunk;
2461                        // length := length + chunk-size
2462                        $length += strlen($chunk);
2463                        // read chunk-size and CRLF
2464                        $chunkstart = $chunkend + strlen($lb);
2465                       
2466                        $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2467                        if ($chunkend == FALSE) {
2468                                break; //Just in case we got a broken connection
2469                        }
2470                        $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2471                        $chunk_size = hexdec( trim($temp) );
2472                        $chunkstart = $chunkend;
2473                }
2474                return $new;
2475        }
2476       
2477        /*
2478         *      Writes payload, including HTTP headers, to $this->outgoing_payload.
2479         */
2480        function buildPayload($data, $cookie_str = '') {
2481                // add content-length header
2482                $this->outgoing_headers['Content-Length'] = strlen($data);
2483                $this->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']);
2484
2485                // start building outgoing payload:
2486                $req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2487                $this->debug("HTTP request: $req");
2488                $this->outgoing_payload = "$req\r\n";
2489
2490                // loop thru headers, serializing
2491                foreach($this->outgoing_headers as $k => $v){
2492                        $hdr = $k.': '.$v;
2493                        $this->debug("HTTP header: $hdr");
2494                        $this->outgoing_payload .= "$hdr\r\n";
2495                }
2496
2497                // add any cookies
2498                if ($cookie_str != '') {
2499                        $hdr = 'Cookie: '.$cookie_str;
2500                        $this->debug("HTTP header: $hdr");
2501                        $this->outgoing_payload .= "$hdr\r\n";
2502                }
2503
2504                // header/body separator
2505                $this->outgoing_payload .= "\r\n";
2506               
2507                // add data
2508                $this->outgoing_payload .= $data;
2509        }
2510
2511        function sendRequest($data, $cookies = NULL) {
2512                // build cookie string
2513                $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2514
2515                // build payload
2516                $this->buildPayload($data, $cookie_str);
2517
2518          if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2519                // send payload
2520                if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2521                        $this->setError('couldn\'t write message data to socket');
2522                        $this->debug('couldn\'t write message data to socket');
2523                        return false;
2524                }
2525                $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2526                return true;
2527          } else if ($this->scheme == 'https') {
2528                // set payload
2529                // TODO: cURL does say this should only be the verb, and in fact it
2530                // turns out that the URI and HTTP version are appended to this, which
2531                // some servers refuse to work with
2532                //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2533                foreach($this->outgoing_headers as $k => $v){
2534                        $curl_headers[] = "$k: $v";
2535                }
2536                if ($cookie_str != '') {
2537                        $curl_headers[] = 'Cookie: ' . $cookie_str;
2538                }
2539                curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
2540                if ($this->request_method == "POST") {
2541                        curl_setopt($this->ch, CURLOPT_POST, 1);
2542                        curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
2543                } else {
2544                }
2545                $this->debug('set cURL payload');
2546                return true;
2547          }
2548        }
2549
2550        function getResponse(){
2551                $this->incoming_payload = '';
2552           
2553          if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2554            // loop until headers have been retrieved
2555            $data = '';
2556            while (!isset($lb)){
2557
2558                        // We might EOF during header read.
2559                        if(feof($this->fp)) {
2560                                $this->incoming_payload = $data;
2561                                $this->debug('found no headers before EOF after length ' . strlen($data));
2562                                $this->debug("received before EOF:\n" . $data);
2563                                $this->setError('server failed to send headers');
2564                                return false;
2565                        }
2566
2567                        $tmp = fgets($this->fp, 256);
2568                        $tmplen = strlen($tmp);
2569                        $this->debug("read line of $tmplen bytes: " . trim($tmp));
2570
2571                        if ($tmplen == 0) {
2572                                $this->incoming_payload = $data;
2573                                $this->debug('socket read of headers timed out after length ' . strlen($data));
2574                                $this->debug("read before timeout: " . $data);
2575                                $this->setError('socket read of headers timed out');
2576                                return false;
2577                        }
2578
2579                        $data .= $tmp;
2580                        $pos = strpos($data,"\r\n\r\n");
2581                        if($pos > 1){
2582                                $lb = "\r\n";
2583                        } else {
2584                                $pos = strpos($data,"\n\n");
2585                                if($pos > 1){
2586                                        $lb = "\n";
2587                                }
2588                        }
2589                        // remove 100 header
2590                        if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2591                                unset($lb);
2592                                $data = '';
2593                        }//
2594                }
2595                // store header data
2596                $this->incoming_payload .= $data;
2597                $this->debug('found end of headers after length ' . strlen($data));
2598                // process headers
2599                $header_data = trim(substr($data,0,$pos));
2600                $header_array = explode($lb,$header_data);
2601                $this->incoming_headers = array();
2602                $this->incoming_cookies = array();
2603                foreach($header_array as $header_line){
2604                        $arr = explode(':',$header_line, 2);
2605                        if(count($arr) > 1){
2606                                $header_name = strtolower(trim($arr[0]));
2607                                $this->incoming_headers[$header_name] = trim($arr[1]);
2608                                if ($header_name == 'set-cookie') {
2609                                        // TODO: allow multiple cookies from parseCookie
2610                                        $cookie = $this->parseCookie(trim($arr[1]));
2611                                        if ($cookie) {
2612                                                $this->incoming_cookies[] = $cookie;
2613                                                $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2614                                        } else {
2615                                                $this->debug('did not find cookie in ' . trim($arr[1]));
2616                                        }
2617                        }
2618                        } else if (isset($header_name)) {
2619                                // append continuation line to previous header
2620                                $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2621                        }
2622                }
2623               
2624                // loop until msg has been received
2625                if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2626                        $content_length =  2147483647;  // ignore any content-length header
2627                        $chunked = true;
2628                        $this->debug("want to read chunked content");
2629                } elseif (isset($this->incoming_headers['content-length'])) {
2630                        $content_length = $this->incoming_headers['content-length'];
2631                        $chunked = false;
2632                        $this->debug("want to read content of length $content_length");
2633                } else {
2634                        $content_length =  2147483647;
2635                        $chunked = false;
2636                        $this->debug("want to read content to EOF");
2637                }
2638                $data = '';
2639                do {
2640                        if ($chunked) {
2641                                $tmp = fgets($this->fp, 256);
2642                                $tmplen = strlen($tmp);
2643                                $this->debug("read chunk line of $tmplen bytes");
2644                                if ($tmplen == 0) {
2645                                        $this->incoming_payload = $data;
2646                                        $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2647                                        $this->debug("read before timeout:\n" . $data);
2648                                        $this->setError('socket read of chunk length timed out');
2649                                        return false;
2650                                }
2651                                $content_length = hexdec(trim($tmp));
2652                                $this->debug("chunk length $content_length");
2653                        }
2654                        $strlen = 0;
2655                    while (($strlen < $content_length) && (!feof($this->fp))) {
2656                        $readlen = min(8192, $content_length - $strlen);
2657                                $tmp = fread($this->fp, $readlen);
2658                                $tmplen = strlen($tmp);
2659                                $this->debug("read buffer of $tmplen bytes");
2660                                if (($tmplen == 0) && (!feof($this->fp))) {
2661                                        $this->incoming_payload = $data;
2662                                        $this->debug('socket read of body timed out after length ' . strlen($data));
2663                                        $this->debug("read before timeout:\n" . $data);
2664                                        $this->setError('socket read of body timed out');
2665                                        return false;
2666                                }
2667                                $strlen += $tmplen;
2668                                $data .= $tmp;
2669                        }
2670                        if ($chunked && ($content_length > 0)) {
2671                                $tmp = fgets($this->fp, 256);
2672                                $tmplen = strlen($tmp);
2673                                $this->debug("read chunk terminator of $tmplen bytes");
2674                                if ($tmplen == 0) {
2675                                        $this->incoming_payload = $data;
2676                                        $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2677                                        $this->debug("read before timeout:\n" . $data);
2678                                        $this->setError('socket read of chunk terminator timed out');
2679                                        return false;
2680                                }
2681                        }
2682                } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
2683                if (feof($this->fp)) {
2684                        $this->debug('read to EOF');
2685                }
2686                $this->debug('read body of length ' . strlen($data));
2687                $this->incoming_payload .= $data;
2688                $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2689               
2690                // close filepointer
2691                if(
2692                        (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') || 
2693                        (! $this->persistentConnection) || feof($this->fp)){
2694                        fclose($this->fp);
2695                        $this->fp = false;
2696                        $this->debug('closed socket');
2697                }
2698               
2699                // connection was closed unexpectedly
2700                if($this->incoming_payload == ''){
2701                        $this->setError('no response from server');
2702                        return false;
2703                }
2704               
2705                // decode transfer-encoding
2706//              if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2707//                      if(!$data = $this->decodeChunked($data, $lb)){
2708//                              $this->setError('Decoding of chunked data failed');
2709//                              return false;
2710//                      }
2711                        //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2712                        // set decoded payload
2713//                      $this->incoming_payload = $header_data.$lb.$lb.$data;
2714//              }
2715       
2716          } else if ($this->scheme == 'https') {
2717                // send and receive
2718                $this->debug('send and receive with cURL');
2719                $this->incoming_payload = curl_exec($this->ch);
2720                $data = $this->incoming_payload;
2721
2722        $cErr = curl_error($this->ch);
2723                if ($cErr != '') {
2724                $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br />';
2725                // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
2726                        foreach(curl_getinfo($this->ch) as $k => $v){
2727                                $err .= "$k: $v<br />";
2728                        }
2729                        $this->debug($err);
2730                        $this->setError($err);
2731                        curl_close($this->ch);
2732                return false;
2733                } else {
2734                        //echo '<pre>';
2735                        //var_dump(curl_getinfo($this->ch));
2736                        //echo '</pre>';
2737                }
2738                // close curl
2739                $this->debug('No cURL error, closing cURL');
2740                curl_close($this->ch);
2741               
2742                // remove 100 header(s)
2743                while (ereg('^HTTP/1.1 100',$data)) {
2744                        if ($pos = strpos($data,"\r\n\r\n")) {
2745                                $data = ltrim(substr($data,$pos));
2746                        } elseif($pos = strpos($data,"\n\n") ) {
2747                                $data = ltrim(substr($data,$pos));
2748                        }
2749                }
2750               
2751                // separate content from HTTP headers
2752                if ($pos = strpos($data,"\r\n\r\n")) {
2753                        $lb = "\r\n";
2754                } elseif( $pos = strpos($data,"\n\n")) {
2755                        $lb = "\n";
2756                } else {
2757                        $this->debug('no proper separation of headers and document');
2758                        $this->setError('no proper separation of headers and document');
2759                        return false;
2760                }
2761                $header_data = trim(substr($data,0,$pos));
2762                $header_array = explode($lb,$header_data);
2763                $data = ltrim(substr($data,$pos));
2764                $this->debug('found proper separation of headers and document');
2765                $this->debug('cleaned data, stringlen: '.strlen($data));
2766                // clean headers
2767                foreach ($header_array as $header_line) {
2768                        $arr = explode(':',$header_line,2);
2769                        if(count($arr) > 1){
2770                                $header_name = strtolower(trim($arr[0]));
2771                                $this->incoming_headers[$header_name] = trim($arr[1]);
2772                                if ($header_name == 'set-cookie') {
2773                                        // TODO: allow multiple cookies from parseCookie
2774                                        $cookie = $this->parseCookie(trim($arr[1]));
2775                                        if ($cookie) {
2776                                                $this->incoming_cookies[] = $cookie;
2777                                                $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2778                                        } else {
2779                                                $this->debug('did not find cookie in ' . trim($arr[1]));
2780                                        }
2781                        }
2782                        } else if (isset($header_name)) {
2783                                // append continuation line to previous header
2784                                $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2785                        }
2786                }
2787          }
2788
2789                $arr = explode(' ', $header_array[0], 3);
2790                $http_version = $arr[0];
2791                $http_status = intval($arr[1]);
2792                $http_reason = count($arr) > 2 ? $arr[2] : '';
2793
2794                // see if we need to resend the request with http digest authentication
2795                if (isset($this->incoming_headers['location']) && $http_status == 301) {
2796                        $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers['location']);
2797                        $this->setURL($this->incoming_headers['location']);
2798                        $this->tryagain = true;
2799                        return false;
2800                }
2801
2802                // see if we need to resend the request with http digest authentication
2803                if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
2804                        $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
2805                        if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
2806                                $this->debug('Server wants digest authentication');
2807                                // remove "Digest " from our elements
2808                                $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2809                               
2810                                // parse elements into array
2811                                $digestElements = explode(',', $digestString);
2812                                foreach ($digestElements as $val) {
2813                                        $tempElement = explode('=', trim($val), 2);
2814                                        $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2815                                }
2816
2817                                // should have (at least) qop, realm, nonce
2818                                if (isset($digestRequest['nonce'])) {
2819                                        $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2820                                        $this->tryagain = true;
2821                                        return false;
2822                                }
2823                        }
2824                        $this->debug('HTTP authentication failed');
2825                        $this->setError('HTTP authentication failed');
2826                        return false;
2827                }
2828               
2829                if (
2830                        ($http_status >= 300 && $http_status <= 307) ||
2831                        ($http_status >= 400 && $http_status <= 417) ||
2832                        ($http_status >= 501 && $http_status <= 505)
2833                   ) {
2834                        $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
2835                        return false;
2836                }
2837
2838                // decode content-encoding
2839                if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2840                        if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2841                        // if decoding works, use it. else assume data wasn't gzencoded
2842                        if(function_exists('gzinflate')){
2843                                        //$timer->setMarker('starting decoding of gzip/deflated content');
2844                                        // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2845                                        // this means there are no Zlib headers, although there should be
2846                                        $this->debug('The gzinflate function exists');
2847                                        $datalen = strlen($data);
2848                                        if ($this->incoming_headers['content-encoding'] == 'deflate') {
2849                                                if ($degzdata = @gzinflate($data)) {
2850                                                $data = $degzdata;
2851                                                $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2852                                                if (strlen($data) < $datalen) {
2853                                                        // test for the case that the payload has been compressed twice
2854                                                        $this->debug('The inflated payload is smaller than the gzipped one; try again');
2855                                                                if ($degzdata = @gzinflate($data)) {
2856                                                                $data = $degzdata;
2857                                                                $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2858                                                                }
2859                                                }
2860                                        } else {
2861                                                $this->debug('Error using gzinflate to inflate the payload');
2862                                                $this->setError('Error using gzinflate to inflate the payload');
2863                                        }
2864                                        } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
2865                                                if ($degzdata = @gzinflate(substr($data, 10))) {        // do our best
2866                                                        $data = $degzdata;
2867                                                $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2868                                                if (strlen($data) < $datalen) {
2869                                                        // test for the case that the payload has been compressed twice
2870                                                        $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2871                                                                if ($degzdata = @gzinflate(substr($data, 10))) {
2872                                                                $data = $degzdata;
2873                                                                $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2874                                                                }
2875                                                }
2876                                        } else {
2877                                                $this->debug('Error using gzinflate to un-gzip the payload');
2878                                                        $this->setError('Error using gzinflate to un-gzip the payload');
2879                                        }
2880                                        }
2881                                        //$timer->setMarker('finished decoding of gzip/deflated content');
2882                                        //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2883                                        // set decoded payload
2884                                        $this->incoming_payload = $header_data.$lb.$lb.$data;
2885                        } else {
2886                                        $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2887                                        $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2888                                }
2889                        } else {
2890                                $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2891                                $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2892                        }
2893                } else {
2894                        $this->debug('No Content-Encoding header');
2895                }
2896               
2897                if(strlen($data) == 0){
2898                        $this->debug('no data after headers!');
2899                        $this->setError('no data present after HTTP headers');
2900                        return false;
2901                }
2902               
2903                return $data;
2904        }
2905
2906        function setContentType($type, $charset = false) {
2907                $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2908                $this->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']);
2909        }
2910
2911        function usePersistentConnection(){
2912                if (isset($this->outgoing_headers['Accept-Encoding'])) {
2913                        return false;
2914                }
2915                $this->protocol_version = '1.1';
2916                $this->persistentConnection = true;
2917                $this->outgoing_headers['Connection'] = 'Keep-Alive';
2918                $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2919                return true;
2920        }
2921
2922        /**
2923         * parse an incoming Cookie into it's parts
2924         *
2925         * @param       string $cookie_str content of cookie
2926         * @return      array with data of that cookie
2927         * @access      private
2928         */
2929        /*
2930         * TODO: allow a Set-Cookie string to be parsed into multiple cookies
2931         */
2932        function parseCookie($cookie_str) {
2933                $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
2934                $data = explode(';', $cookie_str);
2935                $value_str = $data[0];
2936
2937                $cookie_param = 'domain=';
2938                $start = strpos($cookie_str, $cookie_param);
2939                if ($start > 0) {
2940                        $domain = substr($cookie_str, $start + strlen($cookie_param));
2941                        $domain = substr($domain, 0, strpos($domain, ';'));
2942                } else {
2943                        $domain = '';
2944                }
2945
2946                $cookie_param = 'expires=';
2947                $start = strpos($cookie_str, $cookie_param);
2948                if ($start > 0) {
2949                        $expires = substr($cookie_str, $start + strlen($cookie_param));
2950                        $expires = substr($expires, 0, strpos($expires, ';'));
2951                } else {
2952                        $expires = '';
2953                }
2954
2955                $cookie_param = 'path=';
2956                $start = strpos($cookie_str, $cookie_param);
2957                if ( $start > 0 ) {
2958                        $path = substr($cookie_str, $start + strlen($cookie_param));
2959                        $path = substr($path, 0, strpos($path, ';'));
2960                } else {
2961                        $path = '/';
2962                }
2963                                               
2964                $cookie_param = ';secure;';
2965                if (strpos($cookie_str, $cookie_param) !== FALSE) {
2966                        $secure = true;
2967                } else {
2968                        $secure = false;
2969                }
2970
2971                $sep_pos = strpos($value_str, '=');
2972
2973                if ($sep_pos) {
2974                        $name = substr($value_str, 0, $sep_pos);
2975                        $value = substr($value_str, $sep_pos + 1);
2976                        $cookie= array( 'name' => $name,
2977                                        'value' => $value,
2978                                                        'domain' => $domain,
2979                                                        'path' => $path,
2980                                                        'expires' => $expires,
2981                                                        'secure' => $secure
2982                                                        );             
2983                        return $cookie;
2984                }
2985                return false;
2986        }
2987 
2988        /**
2989         * sort out cookies for the current request
2990         *
2991         * @param       array $cookies array with all cookies
2992         * @param       boolean $secure is the send-content secure or not?
2993         * @return      string for Cookie-HTTP-Header
2994         * @access      private
2995         */
2996        function getCookiesForRequest($cookies, $secure=false) {
2997                $cookie_str = '';
2998                if ((! is_null($cookies)) && (is_array($cookies))) {
2999                        foreach ($cookies as $cookie) {
3000                                if (! is_array($cookie)) {
3001                                        continue;
3002                                }
3003                        $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3004                                if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3005                                        if (strtotime($cookie['expires']) <= time()) {
3006                                                $this->debug('cookie has expired');
3007                                                continue;
3008                                        }
3009                                }
3010                                if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3011                                        $domain = preg_quote($cookie['domain']);
3012                                        if (! preg_match("'.*$domain$'i", $this->host)) {
3013                                                $this->debug('cookie has different domain');
3014                                                continue;
3015                                        }
3016                                }
3017                                if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3018                                        $path = preg_quote($cookie['path']);
3019                                        if (! preg_match("'^$path.*'i", $this->path)) {
3020                                                $this->debug('cookie is for a different path');
3021                                                continue;
3022                                        }
3023                                }
3024                                if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3025                                        $this->debug('cookie is secure, transport is not');
3026                                        continue;
3027                                }
3028                                $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3029                        $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3030                        }
3031                }
3032                return $cookie_str;
3033  }
3034}
3035
3036?><?php
3037
3038
3039
3040/**
3041*
3042* soap_server allows the user to create a SOAP server
3043* that is capable of receiving messages and returning responses
3044*
3045* NOTE: WSDL functionality is experimental
3046*
3047* @author   Dietrich Ayala <dietrich@ganx4.com>
3048* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
3049* @access   public
3050*/
3051class soap_server extends nusoap_base {
3052        /**
3053         * HTTP headers of request
3054         * @var array
3055         * @access private
3056         */
3057        var $headers = array();
3058        /**
3059         * HTTP request
3060         * @var string
3061         * @access private
3062         */
3063        var $request = '';
3064        /**
3065         * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3066         * @var string
3067         * @access public
3068         */
3069        var $requestHeaders = '';
3070        /**
3071         * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3072         * @var string
3073         * @access public
3074         */
3075        var $document = '';
3076        /**
3077         * SOAP payload for request (text)
3078         * @var string
3079         * @access public
3080         */
3081        var $requestSOAP = '';
3082        /**
3083         * requested method namespace URI
3084         * @var string
3085         * @access private
3086         */
3087        var $methodURI = '';
3088        /**
3089         * name of method requested
3090         * @var string
3091         * @access private
3092         */
3093        var $methodname = '';
3094        /**
3095         * method parameters from request
3096         * @var array
3097         * @access private
3098         */
3099        var $methodparams = array();
3100        /**
3101         * SOAP Action from request
3102         * @var string
3103         * @access private
3104         */
3105        var $SOAPAction = '';
3106        /**
3107         * character set encoding of incoming (request) messages
3108         * @var string
3109         * @access public
3110         */
3111        var $xml_encoding = '';
3112        /**
3113         * toggles whether the parser decodes element content w/ utf8_decode()
3114         * @var boolean
3115         * @access public
3116         */
3117    var $decode_utf8 = true;
3118
3119        /**
3120         * HTTP headers of response
3121         * @var array
3122         * @access public
3123         */
3124        var $outgoing_headers = array();
3125        /**
3126         * HTTP response
3127         * @var string
3128         * @access private
3129         */
3130        var $response = '';
3131        /**
3132         * SOAP headers for response (text)
3133         * @var string
3134         * @access public
3135         */
3136        var $responseHeaders = '';
3137        /**
3138         * SOAP payload for response (text)
3139         * @var string
3140         * @access private
3141         */
3142        var $responseSOAP = '';
3143        /**
3144         * method return value to place in response
3145         * @var mixed
3146         * @access private
3147         */
3148        var $methodreturn = false;
3149        /**
3150         * whether $methodreturn is a string of literal XML
3151         * @var boolean
3152         * @access public
3153         */
3154        var $methodreturnisliteralxml = false;
3155        /**
3156         * SOAP fault for response (or false)
3157         * @var mixed
3158         * @access private
3159         */
3160        var $fault = false;
3161        /**
3162         * text indication of result (for debugging)
3163         * @var string
3164         * @access private
3165         */
3166        var $result = 'successful';
3167
3168        /**
3169         * assoc array of operations => opData; operations are added by the register()
3170         * method or by parsing an external WSDL definition
3171         * @var array
3172         * @access private
3173         */
3174        var $operations = array();
3175        /**
3176         * wsdl instance (if one)
3177         * @var mixed
3178         * @access private
3179         */
3180        var $wsdl = false;
3181        /**
3182         * URL for WSDL (if one)
3183         * @var mixed
3184         * @access private
3185         */
3186        var $externalWSDLURL = false;
3187        /**
3188         * whether to append debug to response as XML comment
3189         * @var boolean
3190         * @access public
3191         */
3192        var $debug_flag = false;
3193
3194
3195        /**
3196        * constructor
3197    * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3198        *
3199    * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3200        * @access   public
3201        */
3202        function soap_server($wsdl=false){
3203                parent::nusoap_base();
3204                // turn on debugging?
3205                global $debug;
3206                global $HTTP_SERVER_VARS;
3207
3208                if (isset($_SERVER)) {
3209                        $this->debug("_SERVER is defined:");
3210                        $this->appendDebug($this->varDump($_SERVER));
3211                } elseif (isset($HTTP_SERVER_VARS)) {
3212                        $this->debug("HTTP_SERVER_VARS is defined:");
3213                        $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3214                } else {
3215                        $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3216                }
3217
3218                if (isset($debug)) {
3219                        $this->debug("In soap_server, set debug_flag=$debug based on global flag");
3220                        $this->debug_flag = $debug;
3221                } elseif (isset($_SERVER['QUERY_STRING'])) {
3222                        $qs = explode('&', $_SERVER['QUERY_STRING']);
3223                        foreach ($qs as $v) {
3224                                if (substr($v, 0, 6) == 'debug=') {
3225                                        $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3226                                        $this->debug_flag = substr($v, 6);
3227                                }
3228                        }
3229                } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3230                        $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3231                        foreach ($qs as $v) {
3232                                if (substr($v, 0, 6) == 'debug=') {
3233                                        $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3234                                        $this->debug_flag = substr($v, 6);
3235                                }
3236                        }
3237                }
3238
3239                // wsdl
3240                if($wsdl){
3241                        $this->debug("In soap_server, WSDL is specified");
3242                        if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3243                                $this->wsdl = $wsdl;
3244                                $this->externalWSDLURL = $this->wsdl->wsdl;
3245                                $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3246                        } else {
3247                                $this->debug('Create wsdl from ' . $wsdl);
3248                                $this->wsdl = new wsdl($wsdl);
3249                                $this->externalWSDLURL = $wsdl;
3250                        }
3251                        $this->appendDebug($this->wsdl->getDebug());
3252                        $this->wsdl->clearDebug();
3253                        if($err = $this->wsdl->getError()){
3254                                die('WSDL ERROR: '.$err);
3255                        }
3256                }
3257        }
3258
3259        /**
3260        * processes request and returns response
3261        *
3262        * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3263        * @access   public
3264        */
3265        function service($data){
3266                global $HTTP_SERVER_VARS;
3267
3268                if (isset($_SERVER['QUERY_STRING'])) {
3269                        $qs = $_SERVER['QUERY_STRING'];
3270                } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3271                        $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3272                } else {
3273                        $qs = '';
3274                }
3275                $this->debug("In service, query string=$qs");
3276
3277                if (ereg('wsdl', $qs) ){
3278                        $this->debug("In service, this is a request for WSDL");
3279                        if($this->externalWSDLURL){
3280              if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3281                                header('Location: '.$this->externalWSDLURL);
3282              } else { // assume file
3283                header("Content-Type: text/xml\r\n");
3284                $fp = fopen($this->externalWSDLURL, 'r');
3285                fpassthru($fp);
3286              }
3287                        } elseif ($this->wsdl) {
3288                                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3289                                print $this->wsdl->serialize($this->debug_flag);
3290                                if ($this->debug_flag) {
3291                                        $this->debug('wsdl:');
3292                                        $this->appendDebug($this->varDump($this->wsdl));
3293                                        print $this->getDebugAsXMLComment();
3294                                }
3295                        } else {
3296                                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3297                                print "This service does not provide WSDL";
3298                        }
3299                } elseif ($data == '' && $this->wsdl) {
3300                        $this->debug("In service, there is no data, so return Web description");
3301                        print $this->wsdl->webDescription();
3302                } else {
3303                        $this->debug("In service, invoke the request");
3304                        $this->parse_request($data);
3305                        if (! $this->fault) {
3306                                $this->invoke_method();
3307                        }
3308                        if (! $this->fault) {
3309                                $this->serialize_return();
3310                        }
3311                        $this->send_response();
3312                }
3313        }
3314
3315        /**
3316        * parses HTTP request headers.
3317        *
3318        * The following fields are set by this function (when successful)
3319        *
3320        * headers
3321        * request
3322        * xml_encoding
3323        * SOAPAction
3324        *
3325        * @access   private
3326        */
3327        function parse_http_headers() {
3328                global $HTTP_SERVER_VARS;
3329
3330                $this->request = '';
3331                $this->SOAPAction = '';
3332                if(function_exists('getallheaders')){
3333                        $this->debug("In parse_http_headers, use getallheaders");
3334                        $headers = getallheaders();
3335                        foreach($headers as $k=>$v){
3336                                $k = strtolower($k);
3337                                $this->headers[$k] = $v;
3338                                $this->request .= "$k: $v\r\n";
3339                                $this->debug("$k: $v");
3340                        }
3341                        // get SOAPAction header
3342                        if(isset($this->headers['soapaction'])){
3343                                $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3344                        }
3345                        // get the character encoding of the incoming request
3346                        if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3347                                $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3348                                if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3349                                        $this->xml_encoding = strtoupper($enc);
3350                                } else {
3351                                        $this->xml_encoding = 'US-ASCII';
3352                                }
3353                        } else {
3354                                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3355                                $this->xml_encoding = 'ISO-8859-1';
3356                        }
3357                } elseif(isset($_SERVER) && is_array($_SERVER)){
3358                        $this->debug("In parse_http_headers, use _SERVER");
3359                        foreach ($_SERVER as $k => $v) {
3360                                if (substr($k, 0, 5) == 'HTTP_') {
3361                                        $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));                                            $k = strtolower(substr($k, 5));
3362                                } else {
3363                                        $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));                                               $k = strtolower($k);
3364                                }
3365                                if ($k == 'soapaction') {
3366                                        // get SOAPAction header
3367                                        $k = 'SOAPAction';
3368                                        $v = str_replace('"', '', $v);
3369                                        $v = str_replace('\\', '', $v);
3370                                        $this->SOAPAction = $v;
3371                                } else if ($k == 'content-type') {
3372                                        // get the character encoding of the incoming request
3373                                        if (strpos($v, '=')) {
3374                                                $enc = substr(strstr($v, '='), 1);
3375                                                $enc = str_replace('"', '', $enc);
3376                                                $enc = str_replace('\\', '', $enc);
3377                                                if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3378                                                        $this->xml_encoding = strtoupper($enc);
3379                                                } else {
3380                                                        $this->xml_encoding = 'US-ASCII';
3381                                                }
3382                                        } else {
3383                                                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3384                                                $this->xml_encoding = 'ISO-8859-1';
3385                                        }
3386                                }
3387                                $this->headers[$k] = $v;
3388                                $this->request .= "$k: $v\r\n";
3389                                $this->debug("$k: $v");
3390                        }
3391                } elseif (is_array($HTTP_SERVER_VARS)) {
3392                        $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3393                        foreach ($HTTP_SERVER_VARS as $k => $v) {
3394                                if (substr($k, 0, 5) == 'HTTP_') {
3395                                        $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));                                            $k = strtolower(substr($k, 5));
3396                                } else {
3397                                        $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));                                               $k = strtolower($k);
3398                                }
3399                                if ($k == 'soapaction') {
3400                                        // get SOAPAction header
3401                                        $k = 'SOAPAction';
3402                                        $v = str_replace('"', '', $v);
3403                                        $v = str_replace('\\', '', $v);
3404                                        $this->SOAPAction = $v;
3405                                } else if ($k == 'content-type') {
3406                                        // get the character encoding of the incoming request
3407                                        if (strpos($v, '=')) {
3408                                                $enc = substr(strstr($v, '='), 1);
3409                                                $enc = str_replace('"', '', $enc);
3410                                                $enc = str_replace('\\', '', $enc);
3411                                                if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3412                                                        $this->xml_encoding = strtoupper($enc);
3413                                                } else {
3414                                                        $this->xml_encoding = 'US-ASCII';
3415                                                }
3416                                        } else {
3417                                                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3418                                                $this->xml_encoding = 'ISO-8859-1';
3419                                        }
3420                                }
3421                                $this->headers[$k] = $v;
3422                                $this->request .= "$k: $v\r\n";
3423                                $this->debug("$k: $v");
3424                        }
3425                } else {
3426                        $this->debug("In parse_http_headers, HTTP headers not accessible");
3427                        $this->setError("HTTP headers not accessible");
3428                }
3429        }
3430
3431        /**
3432        * parses a request
3433        *
3434        * The following fields are set by this function (when successful)
3435        *
3436        * headers
3437        * request
3438        * xml_encoding
3439        * SOAPAction
3440        * request
3441        * requestSOAP
3442        * methodURI
3443        * methodname
3444        * methodparams
3445        * requestHeaders
3446        * document
3447        *
3448        * This sets the fault field on error
3449        *
3450        * @param    string $data XML string
3451        * @access   private
3452        */
3453        function parse_request($data='') {
3454                $this->debug('entering parse_request()');
3455                $this->parse_http_headers();
3456                $this->debug('got character encoding: '.$this->xml_encoding);
3457                // uncompress if necessary
3458                if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3459                        $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3460                        if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3461                        // if decoding works, use it. else assume data wasn't gzencoded
3462                                if (function_exists('gzuncompress')) {
3463                                        if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3464                                                $data = $degzdata;
3465                                        } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3466                                                $data = $degzdata;
3467                                        } else {
3468                                                $this->fault('Client', 'Errors occurred when trying to decode the data');
3469                                                return;
3470                                        }
3471                                } else {
3472                                        $this->fault('Client', 'This Server does not support compressed data');
3473                                        return;
3474                                }
3475                        }
3476                }
3477                $this->request .= "\r\n".$data;
3478                $data = $this->parseRequest($this->headers, $data);
3479                $this->requestSOAP = $data;
3480                $this->debug('leaving parse_request');
3481        }
3482
3483        /**
3484        * invokes a PHP function for the requested SOAP method
3485        *
3486        * The following fields are set by this function (when successful)
3487        *
3488        * methodreturn
3489        *
3490        * Note that the PHP function that is called may also set the following
3491        * fields to affect the response sent to the client
3492        *
3493        * responseHeaders
3494        * outgoing_headers
3495        *
3496        * This sets the fault field on error
3497        *
3498        * @access   private
3499        */
3500        function invoke_method() {
3501                $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3502
3503                if ($this->wsdl) {
3504                        if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3505                                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3506                                $this->appendDebug('opData=' . $this->varDump($this->opData));
3507                        } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3508                                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3509                                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3510                                $this->appendDebug('opData=' . $this->varDump($this->opData));
3511                                $this->methodname = $this->opData['name'];
3512                        } else {
3513                                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3514                                $this->fault('Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3515                                return;
3516                        }
3517                } else {
3518                        $this->debug('in invoke_method, no WSDL to validate method');
3519                }
3520
3521                // if a . is present in $this->methodname, we see if there is a class in scope,
3522                // which could be referred to. We will also distinguish between two deliminators,
3523                // to allow methods to be called a the class or an instance
3524                $class = '';
3525                $method = '';
3526                if (strpos($this->methodname, '..') > 0) {
3527                        $delim = '..';
3528                } else if (strpos($this->methodname, '.') > 0) {
3529                        $delim = '.';
3530                } else {
3531                        $delim = '';
3532                }
3533
3534                if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
3535                        class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
3536                        // get the class and method name
3537                        $class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3538                        $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
3539                        $this->debug("in invoke_method, class=$class method=$method delim=$delim");
3540                }
3541
3542                // does method exist?
3543                if ($class == '') {
3544                        if (!function_exists($this->methodname)) {
3545                                $this->debug("in invoke_method, function '$this->methodname' not found!");
3546                                $this->result = 'fault: method not found';
3547                                $this->fault('Client',"method '$this->methodname' not defined in service");
3548                                return;
3549                        }
3550                } else {
3551                        $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
3552                        if (in_array($method_to_compare, get_class_methods($class))===false) {
3553                                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3554                                $this->result = 'fault: method not found';
3555                                $this->fault('Client',"method '$this->methodname' not defined in service");
3556                                return;
3557                        }
3558                }
3559
3560                // evaluate message, getting back parameters
3561                // verify that request parameters match the method's signature
3562                if(! $this->verify_method($this->methodname,$this->methodparams)){
3563                        // debug
3564                        $this->debug('ERROR: request not verified against method signature');
3565                        $this->result = 'fault: request failed validation against method signature';
3566                        // return fault
3567                        $this->fault('Client',"Operation '$this->methodname' not defined in service.");
3568                        return;
3569                }
3570
3571                // if there are parameters to pass
3572                $this->debug('in invoke_method, params:');
3573                $this->appendDebug($this->varDump($this->methodparams));
3574                $this->debug("in invoke_method, calling '$this->methodname'");
3575                if (!function_exists('call_user_func_array')) {
3576                        if ($class == '') {
3577                                $this->debug('in invoke_method, calling function using eval()');
3578                                $funcCall = "\$this->methodreturn = $this->methodname(";
3579                        } else {
3580                                if ($delim == '..') {
3581                                        $this->debug('in invoke_method, calling class method using eval()');
3582                                        $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3583                                } else {
3584                                        $this->debug('in invoke_method, calling instance method using eval()');
3585                                        // generate unique instance name
3586                                        $instname = "\$inst_".time();
3587                                        $funcCall = $instname." = new ".$class."(); ";
3588                                        $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3589                                }
3590                        }
3591                        if ($this->methodparams) {
3592                                foreach ($this->methodparams as $param) {
3593                                        if (is_array($param)) {
3594                                                $this->fault('Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3595                                                return;
3596                                        }
3597                                        $funcCall .= "\"$param\",";
3598                                }
3599                                $funcCall = substr($funcCall, 0, -1);
3600                        }
3601                        $funcCall .= ');';
3602                        $this->debug('in invoke_method, function call: '.$funcCall);
3603                        @eval($funcCall);
3604                } else {
3605                        if ($class == '') {
3606                                $this->debug('in invoke_method, calling function using call_user_func_array()');
3607                                $call_arg = "$this->methodname";        // straight assignment changes $this->methodname to lower case after call_user_func_array()
3608                        } elseif ($delim == '..') {
3609                                $this->debug('in invoke_method, calling class method using call_user_func_array()');
3610                                $call_arg = array ($class, $method);
3611                        } else {
3612                                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
3613                                $instance = new $class ();
3614                                $call_arg = array(&$instance, $method);
3615                        }
3616                        $this->methodreturn = call_user_func_array($call_arg, $this->methodparams);
3617                }
3618        $this->debug('in invoke_method, methodreturn:');
3619        $this->appendDebug($this->varDump($this->methodreturn));
3620                $this->debug("in invoke_method, called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn));
3621        }
3622
3623        /**
3624        * serializes the return value from a PHP function into a full SOAP Envelope
3625        *
3626        * The following fields are set by this function (when successful)
3627        *
3628        * responseSOAP
3629        *
3630        * This sets the fault field on error
3631        *
3632        * @access   private
3633        */
3634        function serialize_return() {
3635                $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
3636                // if fault
3637                if (isset($this->methodreturn) && (get_class($this->methodreturn) == 'soap_fault')) {
3638                        $this->debug('got a fault object from method');
3639                        $this->fault = $this->methodreturn;
3640                        return;
3641                } elseif ($this->methodreturnisliteralxml) {
3642                        $return_val = $this->methodreturn;
3643                // returned value(s)
3644                } else {
3645                        $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
3646                        $this->debug('serializing return value');
3647                        if($this->wsdl){
3648                                // weak attempt at supporting multiple output params
3649                                if(sizeof($this->opData['output']['parts']) > 1){
3650                                $opParams = $this->methodreturn;
3651                            } else {
3652                                // TODO: is this really necessary?
3653                                $opParams = array($this->methodreturn);
3654                            }
3655                            $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
3656                            $this->appendDebug($this->wsdl->getDebug());
3657                            $this->wsdl->clearDebug();
3658                                if($errstr = $this->wsdl->getError()){
3659                                        $this->debug('got wsdl error: '.$errstr);
3660                                        $this->fault('Server', 'unable to serialize result');
3661                                        return;
3662                                }
3663                        } else {
3664                                if (isset($this->methodreturn)) {
3665                                        $return_val = $this->serialize_val($this->methodreturn, 'return');
3666                                } else {
3667                                        $return_val = '';
3668                                        $this->debug('in absence of WSDL, assume void return for backward compatibility');
3669                                }
3670                        }
3671                }
3672                $this->debug('return value:');
3673                $this->appendDebug($this->varDump($return_val));
3674
3675                $this->debug('serializing response');
3676                if ($this->wsdl) {
3677                        $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
3678                        if ($this->opData['style'] == 'rpc') {
3679                                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
3680                                if ($this->opData['output']['use'] == 'literal') {
3681                                        $payload = '<'.$this->methodname.'Response xmlns="'.$this->methodURI.'">'.$return_val.'</'.$this->methodname."Response>";
3682                                } else {
3683                                        $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3684                                }
3685                        } else {
3686                                $this->debug('style is not rpc for serialization: assume document');
3687                                $payload = $return_val;
3688                        }
3689                } else {
3690                        $this->debug('do not have WSDL for serialization: assume rpc/encoded');
3691                        $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3692                }
3693                $this->result = 'successful';
3694                if($this->wsdl){
3695                        //if($this->debug_flag){
3696                $this->appendDebug($this->wsdl->getDebug());
3697            //  }
3698                        if (isset($opData['output']['encodingStyle'])) {
3699                                $encodingStyle = $opData['output']['encodingStyle'];
3700                        } else {
3701                                $encodingStyle = '';
3702                        }
3703                        // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
3704                        $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$encodingStyle);
3705                } else {
3706                        $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
3707                }
3708                $this->debug("Leaving serialize_return");
3709        }
3710
3711        /**
3712        * sends an HTTP response
3713        *
3714        * The following fields are set by this function (when successful)
3715        *
3716        * outgoing_headers
3717        * response
3718        *
3719        * @access   private
3720        */
3721        function send_response() {
3722                $this->debug('Enter send_response');
3723                if ($this->fault) {
3724                        $payload = $this->fault->serialize();
3725                        $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
3726                        $this->outgoing_headers[] = "Status: 500 Internal Server Error";
3727                } else {
3728                        $payload = $this->responseSOAP;
3729                        // Some combinations of PHP+Web server allow the Status
3730                        // to come through as a header.  Since OK is the default
3731                        // just do nothing.
3732                        // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
3733                        // $this->outgoing_headers[] = "Status: 200 OK";
3734                }
3735        // add debug data if in debug mode
3736                if(isset($this->debug_flag) && $this->debug_flag){
3737                $payload .= $this->getDebugAsXMLComment();
3738        }
3739                $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
3740                ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
3741                $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
3742                // Let the Web server decide about this
3743                //$this->outgoing_headers[] = "Connection: Close\r\n";
3744                $payload = $this->getHTTPBody($payload);
3745                $type = $this->getHTTPContentType();
3746                $charset = $this->getHTTPContentTypeCharset();
3747                $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
3748                //begin code to compress payload - by John
3749                // NOTE: there is no way to know whether the Web server will also compress
3750                // this data.
3751                if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {     
3752                        if (strstr($this->headers['accept-encoding'], 'gzip')) {
3753                                if (function_exists('gzencode')) {
3754                                        if (isset($this->debug_flag) && $this->debug_flag) {
3755                                                $payload .= "<!-- Content being gzipped -->";
3756                                        }
3757                                        $this->outgoing_headers[] = "Content-Encoding: gzip";
3758                                        $payload = gzencode($payload);
3759                                } else {
3760                                        if (isset($this->debug_flag) && $this->debug_flag) {
3761                                                $payload .= "<!-- Content will not be gzipped: no gzencode -->";
3762                                        }
3763                                }
3764                        } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
3765                                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
3766                                // instead of gzcompress output,
3767                                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
3768                                if (function_exists('gzdeflate')) {
3769                                        if (isset($this->debug_flag) && $this->debug_flag) {
3770                                                $payload .= "<!-- Content being deflated -->";
3771                                        }
3772                                        $this->outgoing_headers[] = "Content-Encoding: deflate";
3773                                        $payload = gzdeflate($payload);
3774                                } else {
3775                                        if (isset($this->debug_flag) && $this->debug_flag) {
3776                                                $payload .= "<!-- Content will not be deflated: no gzcompress -->";
3777                                        }
3778                                }
3779                        }
3780                }
3781                //end code
3782                $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
3783                reset($this->outgoing_headers);
3784                foreach($this->outgoing_headers as $hdr){
3785                        header($hdr, false);
3786                }
3787                print $payload;
3788                $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
3789        }
3790
3791        /**
3792        * takes the value that was created by parsing the request
3793        * and compares to the method's signature, if available.
3794        *
3795        * @param        string  $operation      The operation to be invoked
3796        * @param        array   $request        The array of parameter values
3797        * @return       boolean Whether the operation was found
3798        * @access   private
3799        */
3800        function verify_method($operation,$request){
3801                if(isset($this->wsdl) && is_object($this->wsdl)){
3802                        if($this->wsdl->getOperationData($operation)){
3803                                return true;
3804                        }
3805            } elseif(isset($this->operations[$operation])){
3806                        return true;
3807                }
3808                return false;
3809        }
3810
3811        /**
3812        * processes SOAP message received from client
3813        *
3814        * @param        array   $headers        The HTTP headers
3815        * @param        string  $data           unprocessed request data from client
3816        * @return       mixed   value of the message, decoded into a PHP type
3817        * @access   private
3818        */
3819    function parseRequest($headers, $data) {
3820                $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
3821                if (!strstr($headers['content-type'], 'text/xml')) {
3822                        $this->setError('Request not of type text/xml');
3823                        return false;
3824                }
3825                if (strpos($headers['content-type'], '=')) {
3826                        $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
3827                        $this->debug('Got response encoding: ' . $enc);
3828                        if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3829                                $this->xml_encoding = strtoupper($enc);
3830                        } else {
3831                                $this->xml_encoding = 'US-ASCII';
3832                        }
3833                } else {
3834                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3835                        $this->xml_encoding = 'ISO-8859-1';
3836                }
3837                $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
3838                // parse response, get soap parser obj
3839                $parser = new soap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
3840                // parser debug
3841                $this->debug("parser debug: \n".$parser->getDebug());
3842                // if fault occurred during message parsing
3843                if($err = $parser->getError()){
3844                        $this->result = 'fault: error in msg parsing: '.$err;
3845                        $this->fault('Client',"error in msg parsing:\n".$err);
3846                // else successfully parsed request into soapval object
3847                } else {
3848                        // get/set methodname
3849                        $this->methodURI = $parser->root_struct_namespace;
3850                        $this->methodname = $parser->root_struct_name;
3851                        $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
3852                        $this->debug('calling parser->get_response()');
3853                        $this->methodparams = $parser->get_response();
3854                        // get SOAP headers
3855                        $this->requestHeaders = $parser->getHeaders();
3856            // add document for doclit support
3857            $this->document = $parser->document;
3858                }
3859         }
3860
3861        /**
3862        * gets the HTTP body for the current response.
3863        *
3864        * @param string $soapmsg The SOAP payload
3865        * @return string The HTTP body, which includes the SOAP payload
3866        * @access private
3867        */
3868        function getHTTPBody($soapmsg) {
3869                return $soapmsg;
3870        }
3871       
3872        /**
3873        * gets the HTTP content type for the current response.
3874        *
3875        * Note: getHTTPBody must be called before this.
3876        *
3877        * @return string the HTTP content type for the current response.
3878        * @access private
3879        */
3880        function getHTTPContentType() {
3881                return 'text/xml';
3882        }
3883       
3884        /**
3885        * gets the HTTP content type charset for the current response.
3886        * returns false for non-text content types.
3887        *
3888        * Note: getHTTPBody must be called before this.
3889        *
3890        * @return string the HTTP content type charset for the current response.
3891        * @access private
3892        */
3893        function getHTTPContentTypeCharset() {
3894                return $this->soap_defencoding;
3895        }
3896
3897        /**
3898        * add a method to the dispatch map (this has been replaced by the register method)
3899        *
3900        * @param    string $methodname
3901        * @param    string $in array of input values
3902        * @param    string $out array of output values
3903        * @access   public
3904        * @deprecated
3905        */
3906        function add_to_map($methodname,$in,$out){
3907                        $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
3908        }
3909
3910        /**
3911        * register a service function with the server
3912        *
3913        * @param    string $name the name of the PHP function, class.method or class..method
3914        * @param    array $in assoc array of input values: key = param name, value = param type
3915        * @param    array $out assoc array of output values: key = param name, value = param type
3916        * @param        mixed $namespace the element namespace for the method or false
3917        * @param        mixed $soapaction the soapaction for the method or false
3918        * @param        mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
3919        * @param        mixed $use optional (encoded|literal) or false
3920        * @param        string $documentation optional Description to include in WSDL
3921        * @param        string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
3922        * @access   public
3923        */
3924        function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
3925                global $HTTP_SERVER_VARS;
3926
3927                if($this->externalWSDLURL){
3928                        die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
3929                }
3930                if (! $name) {
3931                        die('You must specify a name when you register an operation');
3932                }
3933                if (!is_array($in)) {
3934                        die('You must provide an array for operation inputs');
3935                }
3936                if (!is_array($out)) {
3937                        die('You must provide an array for operation outputs');
3938                }
3939                if(false == $namespace) {
3940                }
3941                if(false == $soapaction) {
3942                        if (isset($_SERVER)) {
3943                                $SERVER_NAME = $_SERVER['SERVER_NAME'];
3944                                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
3945                        } elseif (isset($HTTP_SERVER_VARS)) {
3946                                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
3947                                $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
3948                        } else {
3949                                $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
3950                        }
3951                        $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
3952                }
3953                if(false == $style) {
3954                        $style = "rpc";
3955                }
3956                if(false == $use) {
3957                        $use = "encoded";
3958                }
3959                if ($use == 'encoded' && $encodingStyle = '') {
3960                        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3961                }
3962
3963                $this->operations[$name] = array(
3964            'name' => $name,
3965            'in' => $in,
3966            'out' => $out,
3967            'namespace' => $namespace,
3968            'soapaction' => $soapaction,
3969            'style' => $style);
3970        if($this->wsdl){
3971                $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
3972            }
3973                return true;
3974        }
3975
3976        /**
3977        * Specify a fault to be returned to the client.
3978        * This also acts as a flag to the server that a fault has occured.
3979        *
3980        * @param        string $faultcode
3981        * @param        string $faultstring
3982        * @param        string $faultactor
3983        * @param        string $faultdetail
3984        * @access   public
3985        */
3986        function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
3987                if ($faultdetail == '' && $this->debug_flag) {
3988                        $faultdetail = $this->getDebug();
3989                }
3990                $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
3991                $this->fault->soap_defencoding = $this->soap_defencoding;
3992        }
3993
3994    /**
3995    * Sets up wsdl object.
3996    * Acts as a flag to enable internal WSDL generation
3997    *
3998    * @param string $serviceName, name of the service
3999    * @param mixed $namespace optional 'tns' service namespace or false
4000    * @param mixed $endpoint optional URL of service endpoint or false
4001    * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4002    * @param string $transport optional SOAP transport
4003    * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4004    */
4005    function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4006    {
4007        global $HTTP_SERVER_VARS;
4008
4009                if (isset($_SERVER)) {
4010                        $SERVER_NAME = $_SERVER['SERVER_NAME'];
4011                        $SERVER_PORT = $_SERVER['SERVER_PORT'];
4012                        $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4013                        $HTTPS = $_SERVER['HTTPS'];
4014                } elseif (isset($HTTP_SERVER_VARS)) {
4015                        $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4016                        $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4017                        $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4018                        $HTTPS = $HTTP_SERVER_VARS['HTTPS'];
4019                } else {
4020                        $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4021                }
4022                if ($SERVER_PORT == 80) {
4023                        $SERVER_PORT = '';
4024                } else {
4025                        $SERVER_PORT = ':' . $SERVER_PORT;
4026                }
4027        if(false == $namespace) {
4028            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4029        }
4030       
4031        if(false == $endpoint) {
4032                if ($HTTPS == '1' || $HTTPS == 'on') {
4033                        $SCHEME = 'https';
4034                } else {
4035                        $SCHEME = 'http';
4036                }
4037            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4038        }
4039       
4040        if(false == $schemaTargetNamespace) {
4041            $schemaTargetNamespace = $namespace;
4042        }
4043       
4044                $this->wsdl = new wsdl;
4045                $this->wsdl->serviceName = $serviceName;
4046        $this->wsdl->endpoint = $endpoint;
4047                $this->wsdl->namespaces['tns'] = $namespace;
4048                $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4049                $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4050                if ($schemaTargetNamespace != $namespace) {
4051                        $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4052                }
4053        $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
4054        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4055        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4056        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4057        $this->wsdl->bindings[$serviceName.'Binding'] = array(
4058                'name'=>$serviceName.'Binding',
4059            'style'=>$style,
4060            'transport'=>$transport,
4061            'portType'=>$serviceName.'PortType');
4062        $this->wsdl->ports[$serviceName.'Port'] = array(
4063                'binding'=>$serviceName.'Binding',
4064            'location'=>$endpoint,
4065            'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4066    }
4067}
4068
4069
4070
4071?><?php
4072
4073
4074
4075/**
4076* parses a WSDL file, allows access to it's data, other utility methods
4077* 
4078* @author   Dietrich Ayala <dietrich@ganx4.com>
4079* @version  $Id: nusoap.php,v 1.9 2013-04-25 07:14:00 mbertin Exp $
4080* @access public 
4081*/
4082class wsdl extends nusoap_base {
4083        // URL or filename of the root of this WSDL
4084    var $wsdl; 
4085    // define internal arrays of bindings, ports, operations, messages, etc.
4086    var $schemas = array();
4087    var $currentSchema;
4088    var $message = array();
4089    var $complexTypes = array();
4090    var $messages = array();
4091    var $currentMessage;
4092    var $currentOperation;
4093    var $portTypes = array();
4094    var $currentPortType;
4095    var $bindings = array();
4096    var $currentBinding;
4097    var $ports = array();
4098    var $currentPort;
4099    var $opData = array();
4100    var $status = '';
4101    var $documentation = false;
4102    var $endpoint = ''; 
4103    // array of wsdl docs to import
4104    var $import = array(); 
4105    // parser vars
4106    var $parser;
4107    var $position = 0;
4108    var $depth = 0;
4109    var $depth_array = array();
4110        // for getting wsdl
4111        var $proxyhost = '';
4112    var $proxyport = '';
4113        var $proxyusername = '';
4114        var $proxypassword = '';
4115        var $timeout = 0;
4116        var $response_timeout = 30;
4117
4118    /**
4119     * constructor
4120     *
4121     * @param string $wsdl WSDL document URL
4122         * @param string $proxyhost
4123         * @param string $proxyport
4124         * @param string $proxyusername
4125         * @param string $proxypassword
4126         * @param integer $timeout set the connection timeout
4127         * @param integer $response_timeout set the response timeout
4128     * @access public
4129     */
4130    function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
4131                parent::nusoap_base();
4132        $this->wsdl = $wsdl;
4133        $this->proxyhost = $proxyhost;
4134        $this->proxyport = $proxyport;
4135                $this->proxyusername = $proxyusername;
4136                $this->proxypassword = $proxypassword;
4137                $this->timeout = $timeout;
4138                $this->response_timeout = $response_timeout;
4139       
4140        // parse wsdl file
4141        if ($wsdl != "") {
4142            $this->debug('initial wsdl URL: ' . $wsdl);
4143            $this->parseWSDL($wsdl);
4144        }
4145        // imports
4146        // TODO: handle imports more properly, grabbing them in-line and nesting them
4147                $imported_urls = array();
4148                $imported = 1;
4149                while ($imported > 0) {
4150                        $imported = 0;
4151                        // Schema imports
4152                        foreach ($this->schemas as $ns => $list) {
4153                                foreach ($list as $xs) {
4154                                                $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4155                                    foreach ($xs->imports as $ns2 => $list2) {
4156                                        for ($ii = 0; $ii < count($list2); $ii++) {
4157                                                if (! $list2[$ii]['loaded']) {
4158                                                        $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4159                                                        $url = $list2[$ii]['location'];
4160                                                                        if ($url != '') {
4161                                                                                $urlparts = parse_url($url);
4162                                                                                if (!isset($urlparts['host'])) {
4163                                                                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4164                                                                                                        substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4165                                                                                }
4166                                                                                if (in_array($url, $imported_urls)===false) {
4167                                                                        $this->parseWSDL($url);
4168                                                                        $imported++;
4169                                                                        $imported_urls[] = $url;
4170                                                                }
4171                                                                        } else {
4172                                                                                $this->debug("Unexpected scenario: empty URL for unloaded import");
4173                                                                        }
4174                                                                }
4175                                                        }
4176                                    } 
4177                                }
4178                        }
4179                        // WSDL imports
4180                                $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4181                    foreach ($this->import as $ns => $list) {
4182                        for ($ii = 0; $ii < count($list); $ii++) {
4183                                if (! $list[$ii]['loaded']) {
4184                                        $this->import[$ns][$ii]['loaded'] = true;
4185                                        $url = $list[$ii]['location'];
4186                                                        if ($url != '') {
4187                                                                $urlparts = parse_url($url);
4188                                                                if (!isset($urlparts['host'])) {
4189                                                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4190                                                                                        substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4191                                                                }
4192                                                                if (in_array($url, $imported_urls)===false) {
4193                                                        $this->parseWSDL($url);
4194                                                        $imported++;
4195                                                        $imported_urls[] = $url;
4196                                                }
4197                                                        } else {
4198                                                                $this->debug("Unexpected scenario: empty URL for unloaded import");
4199                                                        }
4200                                                }
4201                                        }
4202                    } 
4203                        }
4204        // add new data to operation data
4205        foreach($this->bindings as $binding => $bindingData) {
4206            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4207                foreach($bindingData['operations'] as $operation => $data) {
4208                    $this->debug('post-parse data gathering for ' . $operation);
4209                    $this->bindings[$binding]['operations'][$operation]['input'] = 
4210                                                isset($this->bindings[$binding]['operations'][$operation]['input']) ? 
4211                                                array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4212                                                $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4213                    $this->bindings[$binding]['operations'][$operation]['output'] = 
4214                                                isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4215                                                array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4216                                                $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4217                    if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4218                                                $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4219                                        }
4220                                        if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4221                                $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4222                    }
4223                                        if (isset($bindingData['style'])) {
4224                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4225                    }
4226                    $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4227                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4228                    $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4229                } 
4230            } 
4231        }
4232    }
4233
4234    /**
4235     * parses the wsdl document
4236     *
4237     * @param string $wsdl path or URL
4238     * @access private
4239     */
4240    function parseWSDL($wsdl = '')
4241    {
4242        if ($wsdl == '') {
4243            $this->debug('no wsdl passed to parseWSDL()!!');
4244            $this->setError('no wsdl passed to parseWSDL()!!');
4245            return false;
4246        }
4247       
4248        // parse $wsdl for url format
4249        $wsdl_props = parse_url($wsdl);
4250
4251        if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4252            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4253                // get wsdl
4254                $tr = new soap_transport_http($wsdl);
4255                        $tr->request_method = 'GET';
4256                        $tr->useSOAPAction = false;
4257                        if($this->proxyhost && $this->proxyport){
4258                                $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4259                        }
4260                        $tr->setEncoding('gzip, deflate');
4261                        $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4262                        //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4263                        //$this->debug("WSDL response\n" . $tr->incoming_payload);
4264                        $this->appendDebug($tr->getDebug());
4265                        // catch errors
4266                        if($err = $tr->getError() ){
4267                                $errstr = 'HTTP ERROR: '.$err;
4268                                $this->debug($errstr);
4269                    $this->setError($errstr);
4270                                unset($tr);
4271                    return false;
4272                        }
4273                        unset($tr);
4274                        $this->debug("got WSDL URL");
4275        } else {
4276            // $wsdl is not http(s), so treat it as a file URL or plain file path
4277                if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4278                        $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4279                } else {
4280                        $path = $wsdl;
4281                }
4282            $this->debug('getting WSDL file ' . $path);
4283            if ($fp = @fopen($path, 'r')) {
4284                $wsdl_string = '';
4285                while ($data = fread($fp, 32768)) {
4286                    $wsdl_string .= $data;
4287                } 
4288                fclose($fp);
4289            } else {
4290                $errstr = "Bad path to WSDL file $path";
4291                $this->debug($errstr);
4292                $this->setError($errstr);
4293                return false;
4294            } 
4295        }
4296        $this->debug('Parse WSDL');
4297        // end new code added
4298        // Create an XML parser.
4299        $this->parser = xml_parser_create(); 
4300        // Set the options for parsing the XML data.
4301        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4302        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
4303        // Set the object for the parser.
4304        xml_set_object($this->parser, $this); 
4305        // Set the element handlers for the parser.
4306        xml_set_element_handler($this->parser, 'start_element', 'end_element');
4307        xml_set_character_data_handler($this->parser, 'character_data');
4308        // Parse the XML file.
4309        if (!xml_parse($this->parser, $wsdl_string, true)) {
4310            // Display an error message.
4311            $errstr = sprintf(
4312                                'XML error parsing WSDL from %s on line %d: %s',
4313                                $wsdl,
4314                xml_get_current_line_number($this->parser),
4315                xml_error_string(xml_get_error_code($this->parser))
4316                );
4317            $this->debug($errstr);
4318                        $this->debug("XML payload:\n" . $wsdl_string);
4319            $this->setError($errstr);
4320            return false;
4321        } 
4322                // free the parser
4323        xml_parser_free($this->parser);
4324        $this->debug('Parsing WSDL done');
4325                // catch wsdl parse errors
4326                if($this->getError()){
4327                        return false;
4328                }
4329        return true;
4330    } 
4331
4332    /**
4333     * start-element handler
4334     *
4335     * @param string $parser XML parser object
4336     * @param string $name element name
4337     * @param string $attrs associative array of attributes
4338     * @access private
4339     */
4340    function start_element($parser, $name, $attrs)
4341    {
4342        if ($this->status == 'schema') {
4343            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4344            $this->appendDebug($this->currentSchema->getDebug());
4345            $this->currentSchema->clearDebug();
4346        } elseif (ereg('schema$', $name)) {
4347                $this->debug('Parsing WSDL schema');
4348            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4349            $this->status = 'schema';
4350            $this->currentSchema = new xmlschema('', '', $this->namespaces);
4351            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4352            $this->appendDebug($this->currentSchema->getDebug());
4353            $this->currentSchema->clearDebug();
4354        } else {
4355            // position in the total number of elements, starting from 0
4356            $pos = $this->position++;
4357            $depth = $this->depth++; 
4358            // set self as current value for this depth
4359            $this->depth_array[$depth] = $pos;
4360            $this->message[$pos] = array('cdata' => ''); 
4361            // process attributes
4362            if (count($attrs) > 0) {
4363                                // register namespace declarations
4364                foreach($attrs as $k => $v) {
4365                    if (ereg("^xmlns", $k)) {
4366                        if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4367                            $this->namespaces[$ns_prefix] = $v;
4368                        } else {
4369                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4370                        } 
4371                        if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4372                            $this->XMLSchemaVersion = $v;
4373                            $this->namespaces['xsi'] = $v . '-instance';
4374                        } 
4375                    }
4376                }
4377                // expand each attribute prefix to its namespace
4378                foreach($attrs as $k => $v) {
4379                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4380                    if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4381                        $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4382                    } 
4383                    $eAttrs[$k] = $v;
4384                } 
4385                $attrs = $eAttrs;
4386            } else {
4387                $attrs = array();
4388            } 
4389            // get element prefix, namespace and name
4390            if (ereg(':', $name)) {
4391                // get ns prefix
4392                $prefix = substr($name, 0, strpos($name, ':')); 
4393                // get ns
4394                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; 
4395                // get unqualified name
4396                $name = substr(strstr($name, ':'), 1);
4397            } 
4398                        // process attributes, expanding any prefixes to namespaces
4399            // find status, register data
4400            switch ($this->status) {
4401                case 'message':
4402                    if ($name == 'part') {
4403                                    if (isset($attrs['type'])) {
4404                                    $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4405                                    $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4406                                } 
4407                                    if (isset($attrs['element'])) {
4408                                    $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4409                                        $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
4410                                    } 
4411                                } 
4412                                break;
4413                            case 'portType':
4414                                switch ($name) {
4415                                    case 'operation':
4416                                        $this->currentPortOperation = $attrs['name'];
4417                                        $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4418                                        if (isset($attrs['parameterOrder'])) {
4419                                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4420                                                } 
4421                                                break;
4422                                            case 'documentation':
4423                                                $this->documentation = true;
4424                                                break; 
4425                                            // merge input/output data
4426                                            default:
4427                                                $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4428                                                $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4429                                                break;
4430                                        } 
4431                                break;
4432                                case 'binding':
4433                                    switch ($name) {
4434                                        case 'binding': 
4435                                            // get ns prefix
4436                                            if (isset($attrs['style'])) {
4437                                            $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4438                                                } 
4439                                                $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4440                                                break;
4441                                                case 'header':
4442                                                    $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4443                                                    break;
4444                                                case 'operation':
4445                                                    if (isset($attrs['soapAction'])) {
4446                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4447                                                    } 
4448                                                    if (isset($attrs['style'])) {
4449                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4450                                                    } 
4451                                                    if (isset($attrs['name'])) {
4452                                                        $this->currentOperation = $attrs['name'];
4453                                                        $this->debug("current binding operation: $this->currentOperation");
4454                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4455                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4456                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4457                                                    } 
4458                                                    break;
4459                                                case 'input':
4460                                                    $this->opStatus = 'input';
4461                                                    break;
4462                                                case 'output':
4463                                                    $this->opStatus = 'output';
4464                                                    break;
4465                                                case 'body':
4466                                                    if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
4467                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
4468                                                    } else {
4469                                                        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
4470                                                    } 
4471                                                    break;
4472                                        } 
4473                                        break;
4474                                case 'service':
4475                                        switch ($name) {
4476                                            case 'port':
4477                                                $this->currentPort = $attrs['name'];
4478                                                $this->debug('current port: ' . $this->currentPort);
4479                                                $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
4480                                       
4481                                                break;
4482                                            case 'address':
4483                                                $this->ports[$this->currentPort]['location'] = $attrs['location'];
4484                                                $this->ports[$this->currentPort]['bindingType'] = $namespace;
4485                                                $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
4486                                                $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
4487                                                break;
4488                                        } 
4489                                        break;
4490                        } 
4491                // set status
4492                switch ($name) {
4493                        case 'import':
4494                            if (isset($attrs['location'])) {
4495                    $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
4496                    $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
4497                                } else {
4498                    $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
4499                                        if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
4500                                                $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
4501                                        }
4502                    $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
4503                                }
4504                                break;
4505                        //wait for schema
4506                        //case 'types':
4507                        //      $this->status = 'schema';
4508                        //      break;
4509                        case 'message':
4510                                $this->status = 'message';
4511                                $this->messages[$attrs['name']] = array();
4512                                $this->currentMessage = $attrs['name'];
4513                                break;
4514                        case 'portType':
4515                                $this->status = 'portType';
4516                                $this->portTypes[$attrs['name']] = array();
4517                                $this->currentPortType = $attrs['name'];
4518                                break;
4519                        case "binding":
4520                                if (isset($attrs['name'])) {
4521                                // get binding name
4522                                        if (strpos($attrs['name'], ':')) {
4523                                        $this->currentBinding = $this->getLocalPart($attrs['name']);
4524                                        } else {
4525                                        $this->currentBinding = $attrs['name'];
4526                                        } 
4527                                        $this->status = 'binding';
4528                                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
4529                                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
4530                                } 
4531                                break;
4532                        case 'service':
4533                                $this->serviceName = $attrs['name'];
4534                                $this->status = 'service';
4535                                $this->debug('current service: ' . $this->serviceName);
4536                                break;
4537                        case 'definitions':
4538                                foreach ($attrs as $name => $value) {
4539                                        $this->wsdl_info[$name] = $value;
4540                                } 
4541                                break;
4542                        } 
4543                } 
4544        } 
4545
4546        /**
4547        * end-element handler
4548        *
4549        * @param string $parser XML parser object
4550        * @param string $name element name
4551        * @access private
4552        */
4553        function end_element($parser, $name){ 
4554                // unset schema status
4555                if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
4556                        $this->status = "";
4557            $this->appendDebug($this->currentSchema->getDebug());
4558            $this->currentSchema->clearDebug();
4559                        $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
4560                $this->debug('Parsing WSDL schema done');
4561                } 
4562                if ($this->status == 'schema') {
4563                        $this->currentSchema->schemaEndElement($parser, $name);
4564                } else {
4565                        // bring depth down a notch
4566                        $this->depth--;
4567                } 
4568                // end documentation
4569                if ($this->documentation) {
4570                        //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
4571                        //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
4572                        $this->documentation = false;
4573                } 
4574        } 
4575
4576        /**
4577         * element content handler
4578         *
4579         * @param string $parser XML parser object
4580         * @param string $data element content
4581         * @access private
4582         */
4583        function character_data($parser, $data)
4584        {
4585                $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
4586                if (isset($this->message[$pos]['cdata'])) {
4587                        $this->message[$pos]['cdata'] .= $data;
4588                } 
4589                if ($this->documentation) {
4590                        $this->documentation .= $data;
4591                } 
4592        } 
4593       
4594        function getBindingData($binding)
4595        {
4596                if (is_array($this->bindings[$binding])) {
4597                        return $this->bindings[$binding];
4598                } 
4599        }
4600       
4601        /**
4602         * returns an assoc array of operation names => operation data
4603         *
4604         * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
4605         * @return array
4606         * @access public
4607         */
4608        function getOperations($bindingType = 'soap')
4609        {
4610                $ops = array();
4611                if ($bindingType == 'soap') {
4612                        $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4613                }
4614                // loop thru ports
4615                foreach($this->ports as $port => $portData) {
4616                        // binding type of port matches parameter
4617                        if (