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