remove old readme
[atutor.git] / docs / include / classes / nusoap.php
1 <?php
2
3 /*
4 $Id$
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 If you have any questions or comments, please email:
25
26 Dietrich Ayala
27 dietrich@ganx4.com
28 http://dietrich.ganx4.com/nusoap
29
30 NuSphere Corporation
31 http://www.nusphere.com
32
33 */
34
35 /* load classes
36
37 // necessary classes
38 require_once('class.soapclient.php');
39 require_once('class.soap_val.php');
40 require_once('class.soap_parser.php');
41 require_once('class.soap_fault.php');
42
43 // transport classes
44 require_once('class.soap_transport_http.php');
45
46 // optional add-on classes
47 require_once('class.xmlschema.php');
48 require_once('class.wsdl.php');
49
50 // server class
51 require_once('class.soap_server.php');*/
52
53 /**
54 *
55 * nusoap_base
56 *
57 * @author   Dietrich Ayala <dietrich@ganx4.com>
58 * @version  $Id$
59 * @access   public
60 */
61 class nusoap_base {
62
63         var $title = 'NuSOAP';
64         var $version = '0.6.7';
65         var $revision = '$Revision: 1.1 $';
66         var $error_str = false;
67     var $debug_str = '';
68         // toggles automatic encoding of special characters as entities
69         // (should always be true, I think)
70         var $charencoding = true;
71
72     /**
73         *  set schema version
74         *
75         * @var      XMLSchemaVersion
76         * @access   public
77         */
78         var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
79         
80     /**
81         *  set charset encoding for outgoing messages
82         *
83         * @var      soap_defencoding
84         * @access   public
85         */
86         //var $soap_defencoding = 'UTF-8';
87     var $soap_defencoding = 'ISO-8859-1';
88
89         /**
90         *  load namespace uris into an array of uri => prefix
91         *
92         * @var      namespaces
93         * @access   public
94         */
95         var $namespaces = array(
96                 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
97                 'xsd' => 'http://www.w3.org/2001/XMLSchema',
98                 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
99                 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
100                 'si' => 'http://soapinterop.org/xsd');
101         var $usedNamespaces = array();
102
103         /**
104         * load types into typemap array
105         * is this legacy yet?
106         * no, this is used by the xmlschema class to verify type => namespace mappings.
107         * @var      typemap
108         * @access   public
109         */
110         var $typemap = array(
111         'http://www.w3.org/2001/XMLSchema' => array(
112                 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
113                 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
114                 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
115                 // derived datatypes
116                 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
117                 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
118                 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
119                 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
120         'http://www.w3.org/1999/XMLSchema' => array(
121                 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
122                 'float'=>'double','dateTime'=>'string',
123                 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
124         'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
125         'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
126     'http://xml.apache.org/xml-soap' => array('Map')
127         );
128
129         /**
130         *  entities to convert
131         *
132         * @var      xmlEntities
133         * @access   public
134         */
135         var $xmlEntities = array('quot' => '"','amp' => '&',
136                 'lt' => '<','gt' => '>','apos' => "'");
137
138         /**
139         * adds debug data to the class level debug string
140         *
141         * @param    string $string debug data
142         * @access   private
143         */
144         function debug($string){
145                 $this->debug_str .= get_class($this).": $string\n";
146         }
147
148         /**
149         * expands entities, e.g. changes '<' to '&lt;'.
150         *
151         * @param        string  $val    The string in which to expand entities.
152         * @access       private
153         */
154         function expandEntities($val) {
155                 if ($this->charencoding) {
156                 $val = str_replace('&', '&amp;', $val);
157                 $val = str_replace("'", '&apos;', $val);
158                 $val = str_replace('"', '&quot;', $val);
159                 $val = str_replace('<', '&lt;', $val);
160                 $val = str_replace('>', '&gt;', $val);
161             }
162             return $val;
163         }
164
165         /**
166         * returns error string if present
167         *
168         * @return   boolean $string error string
169         * @access   public
170         */
171         function getError(){
172                 if($this->error_str != ''){
173                         return $this->error_str;
174                 }
175                 return false;
176         }
177
178         /**
179         * sets error string
180         *
181         * @return   boolean $string error string
182         * @access   private
183         */
184         function setError($str){
185                 $this->error_str = $str;
186         }
187
188         /**
189         * detect if array is a simple array or a struct (associative array)
190         *
191         * @param        $val    The PHP array
192         * @return       string  (arraySimple|arrayStruct)
193         * @access       private
194         */
195         function isArraySimpleOrStruct($val) {
196         $keyList = array_keys($val);
197                 foreach ($keyList as $keyListValue) {
198                         if (!is_int($keyListValue)) {
199                                 return 'arrayStruct';
200                         }
201                 }
202                 return 'arraySimple';
203         }
204
205         /**
206         * serializes PHP values in accordance w/ section 5. Type information is
207         * not serialized if $use == 'literal'.
208         *
209         * @return       string
210     * @access   public
211         */
212         function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
213         if(is_object($val) && get_class($val) == 'soapval'){
214                 return $val->serialize($use);
215         }
216                 $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
217                 // if no name, use item
218                 $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
219                 // if name has ns, add ns prefix to name
220                 $xmlns = '';
221         if($name_ns){
222                         $prefix = 'nu'.rand(1000,9999);
223                         $name = $prefix.':'.$name;
224                         $xmlns .= " xmlns:$prefix=\"$name_ns\"";
225                 }
226                 // if type is prefixed, create type prefix
227                 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
228                         // need to fix this. shouldn't default to xsd if no ns specified
229                     // w/o checking against typemap
230                         $type_prefix = 'xsd';
231                 } elseif($type_ns){
232                         $type_prefix = 'ns'.rand(1000,9999);
233                         $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
234                 }
235                 // serialize attributes if present
236                 $atts = '';
237                 if($attributes){
238                         foreach($attributes as $k => $v){
239                                 $atts .= " $k=\"$v\"";
240                         }
241                 }
242         // serialize if an xsd built-in primitive type
243         if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
244                 if(is_bool($val) && !$val){
245                         $val = 0;
246                         } else if (is_string($val)) {
247                                 $val = $this->expandEntities($val);
248                         }
249                         if ($use == 'literal') {
250                         return "<$name$xmlns>$val</$name>";
251                 } else {
252                         return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
253                 }
254         }
255                 // detect type and serialize
256                 $xml = '';
257                 switch(true) {
258                         case ($type == '' && is_null($val)):
259                                 if ($use == 'literal') {
260                                         // TODO: depends on nillable
261                                         $xml .= "<$name$xmlns/>";
262                                 } else {
263                                         $xml .= "<$name$xmlns xsi:nil=\"true\"/>";
264                                 }
265                                 break;
266                         case (is_bool($val) || $type == 'boolean'):
267                                 if(!$val){
268                                 $val = 0;
269                                 }
270                                 if ($use == 'literal') {
271                                         $xml .= "<$name$xmlns $atts>$val</$name>";
272                                 } else {
273                                         $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
274                                 }
275                                 break;
276                         case (is_int($val) || is_long($val) || $type == 'int'):
277                                 if ($use == 'literal') {
278                                         $xml .= "<$name$xmlns $atts>$val</$name>";
279                                 } else {
280                                         $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
281                                 }
282                                 break;
283                         case (is_float($val)|| is_double($val) || $type == 'float'):
284                                 if ($use == 'literal') {
285                                         $xml .= "<$name$xmlns $atts>$val</$name>";
286                                 } else {
287                                         $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
288                                 }
289                                 break;
290                         case (is_string($val) || $type == 'string'):
291                                 $val = $this->expandEntities($val);
292                                 if ($use == 'literal') {
293                                         $xml .= "<$name$xmlns $atts>$val</$name>";
294                                 } else {
295                                         $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
296                                 }
297                                 break;
298                         case is_object($val):
299                                 $name = get_class($val);
300                                 foreach(get_object_vars($val) as $k => $v){
301                                         $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
302                                 }
303                                 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
304                                 break;
305                         break;
306                         case (is_array($val) || $type):
307                                 // detect if struct or array
308                                 $valueType = $this->isArraySimpleOrStruct($val);
309                 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
310                                         $i = 0;
311                                         if(is_array($val) && count($val)> 0){
312                                                 foreach($val as $v){
313                                 if(is_object($v) && get_class($v) ==  'soapval'){
314                                                                 $tt_ns = $v->type_ns;
315                                                                 $tt = $v->type;
316                                                         } else {
317                                                                 $tt = gettype($v);
318                                 }
319                                                         $array_types[$tt] = 1;
320                                                         $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
321                                                         ++$i;
322                                                 }
323                                                 if(count($array_types) > 1){
324                                                         $array_typename = 'xsd:ur-type';
325                                                 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
326                                                         $array_typename = 'xsd:'.$tt;
327                                                 } elseif($tt == 'array' || $tt == 'Array'){
328                                                         $array_typename = 'SOAP-ENC:Array';
329                                                 } else {
330                                                         // if type is prefixed, create type prefix
331                                                         if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
332                                                                  $array_typename = 'xsd:' . $tt;
333                                                         } elseif ($tt_ns) {
334                                                                 $tt_prefix = 'ns' . rand(1000, 9999);
335                                                                 $array_typename = "$tt_prefix:$tt";
336                                                                 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
337                                                         } else {
338                                                                 $array_typename = $tt;
339                                                         }
340                                                 }
341                                                 $array_type = $i;
342                                                 if ($use == 'literal') {
343                                                         $type_str = '';
344                                                 } else if (isset($type) && isset($type_prefix)) {
345                                                         $type_str = " xsi:type=\"$type_prefix:$type\"";
346                                                 } else {
347                                                         $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
348                                                 }
349                                         // empty array
350                                         } else {
351                                                 if ($use == 'literal') {
352                                                         $type_str = '';
353                                                 } else if (isset($type) && isset($type_prefix)) {
354                                                         $type_str = " xsi:type=\"$type_prefix:$type\"";
355                                                 } else {
356                                                         $type_str = " xsi:type=\"SOAP-ENC:Array\"";
357                                                 }
358                                         }
359                                         $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
360                                 } else {
361                                         // got a struct
362                                         if(isset($type) && isset($type_prefix)){
363                                                 $type_str = " xsi:type=\"$type_prefix:$type\"";
364                                         } else {
365                                                 $type_str = '';
366                                         }
367                                         if ($use == 'literal') {
368                                                 $xml .= "<$name$xmlns $atts>";
369                                         } else {
370                                                 $xml .= "<$name$xmlns$type_str$atts>";
371                                         }
372                                         foreach($val as $k => $v){
373                                                 // Apache Map
374                                                 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
375                                                         $xml .= '<item>';
376                                                         $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
377                                                         $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
378                                                         $xml .= '</item>';
379                                                 } else {
380                                                         $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
381                                                 }
382                                         }
383                                         $xml .= "</$name>";
384                                 }
385                                 break;
386                         default:
387                                 $xml .= 'not detected, got '.gettype($val).' for '.$val;
388                                 break;
389                 }
390                 return $xml;
391         }
392
393     /**
394     * serialize message
395     *
396     * @param string body
397     * @param string headers optional
398     * @param array namespaces optional
399     * @param string style optional (rpc|document)
400     * @param string use optional (encoded|literal)
401     * @return string message
402     * @access public
403     */
404     function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded'){
405     // TODO: add an option to automatically run utf8_encode on $body and $headers
406     // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
407     // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
408
409         // serialize namespaces
410     $ns_string = '';
411         foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
412                 $ns_string .= "  xmlns:$k=\"$v\"";
413         }
414         if($style == 'rpc' && $use == 'encoded') {
415                 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
416         }
417
418         // serialize headers
419         if($headers){
420                 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
421         }
422         // serialize envelope
423         return
424         '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
425         '<SOAP-ENV:Envelope'.$ns_string.">".
426         $headers.
427         "<SOAP-ENV:Body>".
428                 $body.
429         "</SOAP-ENV:Body>".
430         "</SOAP-ENV:Envelope>";
431     }
432
433     function formatDump($str){
434                 $str = htmlspecialchars($str);
435                 return nl2br($str);
436     }
437
438         /**
439         * contracts a qualified name
440         *
441         * @param    string $string qname
442         * @return       string contracted qname
443         * @access   private
444         */
445         function contractQname($qname){
446                 // get element namespace
447                 //$this->xdebug("Contract $qname");
448                 if (strrpos($qname, ':')) {
449                         // get unqualified name
450                         $name = substr($qname, strrpos($qname, ':') + 1);
451                         // get ns
452                         $ns = substr($qname, 0, strrpos($qname, ':'));
453                         $p = $this->getPrefixFromNamespace($ns);
454                         if ($p) {
455                                 return $p . ':' . $name;
456                         }
457                         return $qname;
458                 } else {
459                         return $qname;
460                 }
461         }
462
463         /**
464         * expands a qualified name
465         *
466         * @param    string $string qname
467         * @return       string expanded qname
468         * @access   private
469         */
470         function expandQname($qname){
471                 // get element prefix
472                 if(strpos($qname,':') && !ereg('^http://',$qname)){
473                         // get unqualified name
474                         $name = substr(strstr($qname,':'),1);
475                         // get ns prefix
476                         $prefix = substr($qname,0,strpos($qname,':'));
477                         if(isset($this->namespaces[$prefix])){
478                                 return $this->namespaces[$prefix].':'.$name;
479                         } else {
480                                 return $qname;
481                         }
482                 } else {
483                         return $qname;
484                 }
485         }
486
487     /**
488     * returns the local part of a prefixed string
489     * returns the original string, if not prefixed
490     *
491     * @param string
492     * @return string
493     * @access public
494     */
495         function getLocalPart($str){
496                 if($sstr = strrchr($str,':')){
497                         // get unqualified name
498                         return substr( $sstr, 1 );
499                 } else {
500                         return $str;
501                 }
502         }
503
504         /**
505     * returns the prefix part of a prefixed string
506     * returns false, if not prefixed
507     *
508     * @param string
509     * @return mixed
510     * @access public
511     */
512         function getPrefix($str){
513                 if($pos = strrpos($str,':')){
514                         // get prefix
515                         return substr($str,0,$pos);
516                 }
517                 return false;
518         }
519
520         /**
521     * pass it a prefix, it returns a namespace
522         * returns false if no namespace registered with the given prefix
523     *
524     * @param string
525     * @return mixed
526     * @access public
527     */
528         function getNamespaceFromPrefix($prefix){
529                 if (isset($this->namespaces[$prefix])) {
530                         return $this->namespaces[$prefix];
531                 }
532                 //$this->setError("No namespace registered for prefix '$prefix'");
533                 return false;
534         }
535
536         /**
537     * returns the prefix for a given namespace (or prefix)
538     * or false if no prefixes registered for the given namespace
539     *
540     * @param string
541     * @return mixed
542     * @access public
543     */
544         function getPrefixFromNamespace($ns) {
545                 foreach ($this->namespaces as $p => $n) {
546                         if ($ns == $n || $ns == $p) {
547                             $this->usedNamespaces[$p] = $n;
548                                 return $p;
549                         }
550                 }
551                 return false;
552         }
553
554     function varDump($data) {
555                 ob_start();
556                 var_dump($data);
557                 $ret_val = ob_get_contents();
558                 ob_end_clean();
559                 return $ret_val;
560         }
561 }
562
563 // XML Schema Datatype Helper Functions
564
565 //xsd:dateTime helpers
566
567 /**
568 * convert unix timestamp to ISO 8601 compliant date string
569 *
570 * @param    string $timestamp Unix time stamp
571 * @access   public
572 */
573 function timestamp_to_iso8601($timestamp,$utc=true){
574         $datestr = date('Y-m-d\TH:i:sO',$timestamp);
575         if($utc){
576                 $eregStr =
577                 '([0-9]{4})-'.  // centuries & years CCYY-
578                 '([0-9]{2})-'.  // months MM-
579                 '([0-9]{2})'.   // days DD
580                 'T'.                    // separator T
581                 '([0-9]{2}):'.  // hours hh:
582                 '([0-9]{2}):'.  // minutes mm:
583                 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
584                 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
585
586                 if(ereg($eregStr,$datestr,$regs)){
587                         return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
588                 }
589                 return false;
590         } else {
591                 return $datestr;
592         }
593 }
594
595 /**
596 * convert ISO 8601 compliant date string to unix timestamp
597 *
598 * @param    string $datestr ISO 8601 compliant date string
599 * @access   public
600 */
601 function iso8601_to_timestamp($datestr){
602         $eregStr =
603         '([0-9]{4})-'.  // centuries & years CCYY-
604         '([0-9]{2})-'.  // months MM-
605         '([0-9]{2})'.   // days DD
606         'T'.                    // separator T
607         '([0-9]{2}):'.  // hours hh:
608         '([0-9]{2}):'.  // minutes mm:
609         '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
610         '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
611         if(ereg($eregStr,$datestr,$regs)){
612                 // not utc
613                 if($regs[8] != 'Z'){
614                         $op = substr($regs[8],0,1);
615                         $h = substr($regs[8],1,2);
616                         $m = substr($regs[8],strlen($regs[8])-2,2);
617                         if($op == '-'){
618                                 $regs[4] = $regs[4] + $h;
619                                 $regs[5] = $regs[5] + $m;
620                         } elseif($op == '+'){
621                                 $regs[4] = $regs[4] - $h;
622                                 $regs[5] = $regs[5] - $m;
623                         }
624                 }
625                 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
626         } else {
627                 return false;
628         }
629 }
630
631 function usleepWindows($usec)
632 {
633         $start = gettimeofday();
634         
635         do
636         {
637                 $stop = gettimeofday();
638                 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
639                 + $stop['usec'] - $start['usec'];
640         }
641         while ($timePassed < $usec);
642 }
643
644 ?><?php
645
646
647
648 /**
649 * soap_fault class, allows for creation of faults
650 * mainly used for returning faults from deployed functions
651 * in a server instance.
652 * @author   Dietrich Ayala <dietrich@ganx4.com>
653 * @version  $Id$
654 * @access public
655 */
656 class soap_fault extends nusoap_base {
657
658         var $faultcode;
659         var $faultactor;
660         var $faultstring;
661         var $faultdetail;
662
663         /**
664         * constructor
665     *
666     * @param string $faultcode (client | server)
667     * @param string $faultactor only used when msg routed between multiple actors
668     * @param string $faultstring human readable error message
669     * @param string $faultdetail
670         */
671         function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
672                 $this->faultcode = $faultcode;
673                 $this->faultactor = $faultactor;
674                 $this->faultstring = $faultstring;
675                 $this->faultdetail = $faultdetail;
676         }
677
678         /**
679         * serialize a fault
680         *
681         * @access   public
682         */
683         function serialize(){
684                 $ns_string = '';
685                 foreach($this->namespaces as $k => $v){
686                         $ns_string .= "\n  xmlns:$k=\"$v\"";
687                 }
688                 $return_msg =
689                         '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
690                         '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
691                                 '<SOAP-ENV:Body>'.
692                                 '<SOAP-ENV:Fault>'.
693                                         '<faultcode>'.$this->expandEntities($this->faultcode).'</faultcode>'.
694                                         '<faultactor>'.$this->expandEntities($this->faultactor).'</faultactor>'.
695                                         '<faultstring>'.$this->expandEntities($this->faultstring).'</faultstring>'.
696                                         '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
697                                 '</SOAP-ENV:Fault>'.
698                                 '</SOAP-ENV:Body>'.
699                         '</SOAP-ENV:Envelope>';
700                 return $return_msg;
701         }
702 }
703
704
705
706 ?><?php
707
708
709
710 /**
711 * parses an XML Schema, allows access to it's data, other utility methods
712 * no validation... yet.
713 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
714 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
715 * tutorials I refer to :)
716 *
717 * @author   Dietrich Ayala <dietrich@ganx4.com>
718 * @version  $Id$
719 * @access   public
720 */
721 class XMLSchema extends nusoap_base  {
722         
723         // files
724         var $schema = '';
725         var $xml = '';
726         // namespaces
727         var $enclosingNamespaces;
728         // schema info
729         var $schemaInfo = array();
730         var $schemaTargetNamespace = '';
731         // types, elements, attributes defined by the schema
732         var $attributes = array();
733         var $complexTypes = array();
734         var $currentComplexType = false;
735         var $elements = array();
736         var $currentElement = false;
737         var $simpleTypes = array();
738         var $currentSimpleType = false;
739         // imports
740         var $imports = array();
741         // parser vars
742         var $parser;
743         var $position = 0;
744         var $depth = 0;
745         var $depth_array = array();
746         var $message = array();
747         var $defaultNamespace = array();
748     
749         /**
750         * constructor
751         *
752         * @param    string $schema schema document URI
753         * @param    string $xml xml document URI
754         * @param        string $namespaces namespaces defined in enclosing XML
755         * @access   public
756         */
757         function XMLSchema($schema='',$xml='',$namespaces=array()){
758
759                 $this->debug('xmlschema class instantiated, inside constructor');
760                 // files
761                 $this->schema = $schema;
762                 $this->xml = $xml;
763
764                 // namespaces
765                 $this->enclosingNamespaces = $namespaces;
766                 $this->namespaces = array_merge($this->namespaces, $namespaces);
767
768                 // parse schema file
769                 if($schema != ''){
770                         $this->debug('initial schema file: '.$schema);
771                         $this->parseFile($schema, 'schema');
772                 }
773
774                 // parse xml file
775                 if($xml != ''){
776                         $this->debug('initial xml file: '.$xml);
777                         $this->parseFile($xml, 'xml');
778                 }
779
780         }
781
782     /**
783     * parse an XML file
784     *
785     * @param string $xml, path/URL to XML file
786     * @param string $type, (schema | xml)
787         * @return boolean
788     * @access public
789     */
790         function parseFile($xml,$type){
791                 // parse xml file
792                 if($xml != ""){
793                         $xmlStr = @join("",@file($xml));
794                         if($xmlStr == ""){
795                                 $msg = 'Error reading XML from '.$xml;
796                                 $this->setError($msg);
797                                 $this->debug($msg);
798                         return false;
799                         } else {
800                                 $this->debug("parsing $xml");
801                                 $this->parseString($xmlStr,$type);
802                                 $this->debug("done parsing $xml");
803                         return true;
804                         }
805                 }
806                 return false;
807         }
808
809         /**
810         * parse an XML string
811         *
812         * @param    string $xml path or URL
813     * @param string $type, (schema|xml)
814         * @access   private
815         */
816         function parseString($xml,$type){
817                 // parse xml string
818                 if($xml != ""){
819
820                 // Create an XML parser.
821                 $this->parser = xml_parser_create();
822                 // Set the options for parsing the XML data.
823                 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
824
825                 // Set the object for the parser.
826                 xml_set_object($this->parser, $this);
827
828                 // Set the element handlers for the parser.
829                         if($type == "schema"){
830                         xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
831                         xml_set_character_data_handler($this->parser,'schemaCharacterData');
832                         } elseif($type == "xml"){
833                                 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
834                         xml_set_character_data_handler($this->parser,'xmlCharacterData');
835                         }
836
837                     // Parse the XML file.
838                     if(!xml_parse($this->parser,$xml,true)){
839                         // Display an error message.
840                                 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
841                                 xml_get_current_line_number($this->parser),
842                                 xml_error_string(xml_get_error_code($this->parser))
843                                 );
844                                 $this->debug($errstr);
845                                 $this->debug("XML payload:\n" . $xml);
846                                 $this->setError($errstr);
847                 }
848             
849                         xml_parser_free($this->parser);
850                 } else{
851                         $this->debug('no xml passed to parseString()!!');
852                         $this->setError('no xml passed to parseString()!!');
853                 }
854         }
855
856         /**
857         * start-element handler
858         *
859         * @param    string $parser XML parser object
860         * @param    string $name element name
861         * @param    string $attrs associative array of attributes
862         * @access   private
863         */
864         function schemaStartElement($parser, $name, $attrs) {
865                 
866                 // position in the total number of elements, starting from 0
867                 $pos = $this->position++;
868                 $depth = $this->depth++;
869                 // set self as current value for this depth
870                 $this->depth_array[$depth] = $pos;
871                 $this->message[$pos] = array('cdata' => ''); 
872                 if ($depth > 0) {
873                         $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
874                 } else {
875                         $this->defaultNamespace[$pos] = false;
876                 }
877
878                 // get element prefix
879                 if($prefix = $this->getPrefix($name)){
880                         // get unqualified name
881                         $name = $this->getLocalPart($name);
882                 } else {
883                 $prefix = '';
884         }
885                 
886         // loop thru attributes, expanding, and registering namespace declarations
887         if(count($attrs) > 0){
888                 foreach($attrs as $k => $v){
889                 // if ns declarations, add to class level array of valid namespaces
890                                 if(ereg("^xmlns",$k)){
891                         //$this->xdebug("$k: $v");
892                         //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
893                         if($ns_prefix = substr(strrchr($k,':'),1)){
894                                 //$this->xdebug("Add namespace[$ns_prefix] = $v");
895                                                 $this->namespaces[$ns_prefix] = $v;
896                                         } else {
897                                                 $this->defaultNamespace[$pos] = $v;
898                                                 if (! $this->getPrefixFromNamespace($v)) {
899                                                         $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
900                                                 }
901                                         }
902                                         if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
903                                                 $this->XMLSchemaVersion = $v;
904                                                 $this->namespaces['xsi'] = $v.'-instance';
905                                         }
906                                 }
907                 }
908                 foreach($attrs as $k => $v){
909                 // expand each attribute
910                 $k = strpos($k,':') ? $this->expandQname($k) : $k;
911                 $v = strpos($v,':') ? $this->expandQname($v) : $v;
912                         $eAttrs[$k] = $v;
913                 }
914                 $attrs = $eAttrs;
915         } else {
916                 $attrs = array();
917         }
918                 // find status, register data
919                 switch($name){
920                         case 'all':
921                         case 'choice':
922                         case 'sequence':
923                                 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
924                                 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
925                                 if($name == 'all' || $name == 'sequence'){
926                                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
927                                 }
928                         break;
929                         case 'attribute':
930                 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
931                 $this->xdebug("parsing attribute " . $this->varDump($attrs));
932                 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
933                                         $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
934                                         if (!strpos($v, ':')) {
935                                                 // no namespace in arrayType attribute value...
936                                                 if ($this->defaultNamespace[$pos]) {
937                                                         // ...so use the default
938                                                         $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
939                                                 }
940                                         }
941                 }
942                 if(isset($attrs['name'])){
943                                         $this->attributes[$attrs['name']] = $attrs;
944                                         $aname = $attrs['name'];
945                                 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
946                                         if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
947                                 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
948                         } else {
949                                 $aname = '';
950                         }
951                                 } elseif(isset($attrs['ref'])){
952                                         $aname = $attrs['ref'];
953                     $this->attributes[$attrs['ref']] = $attrs;
954                                 }
955                 
956                                 if(isset($this->currentComplexType)){
957                                         $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
958                                 } elseif(isset($this->currentElement)){
959                                         $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
960                                 }
961                                 // arrayType attribute
962                                 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
963                                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
964                         $prefix = $this->getPrefix($aname);
965                                         if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
966                                                 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
967                                         } else {
968                                                 $v = '';
969                                         }
970                     if(strpos($v,'[,]')){
971                         $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
972                     }
973                     $v = substr($v,0,strpos($v,'[')); // clip the []
974                     if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
975                         $v = $this->XMLSchemaVersion.':'.$v;
976                     }
977                     $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
978                                 }
979                         break;
980                         case 'complexType':
981                                 if(isset($attrs['name'])){
982                                         $this->xdebug('processing named complexType '.$attrs['name']);
983                                         $this->currentElement = false;
984                                         $this->currentComplexType = $attrs['name'];
985                                         $this->complexTypes[$this->currentComplexType] = $attrs;
986                                         $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
987                                         if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
988                                                 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
989                                         } else {
990                                                 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
991                                         }
992                                 }else{
993                                         $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
994                                         $this->currentComplexType = $this->currentElement . '_ContainedType';
995                                         $this->currentElement = false;
996                                         $this->complexTypes[$this->currentComplexType] = $attrs;
997                                         $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
998                                         if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
999                                                 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1000                                         } else {
1001                                                 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1002                                         }
1003                                 }
1004                         break;
1005                         case 'element':
1006                                 // elements defined as part of a complex type should
1007                                 // not really be added to $this->elements, but for some
1008                                 // reason, they are
1009                                 if(isset($attrs['type'])){
1010                                         $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1011                                         $this->currentElement = $attrs['name'];
1012                                         $this->elements[ $attrs['name'] ] = $attrs;
1013                                         $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1014                                         if (!isset($this->elements[ $attrs['name'] ]['form'])) {
1015                                                 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
1016                                         }
1017                                         $ename = $attrs['name'];
1018                                 } elseif(isset($attrs['ref'])){
1019                                         $ename = $attrs['ref'];
1020                                 } else {
1021                                         $this->xdebug("processing untyped element ".$attrs['name']);
1022                                         $this->currentElement = $attrs['name'];
1023                                         $this->elements[ $attrs['name'] ] = $attrs;
1024                                         $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1025                                         $this->elements[ $attrs['name'] ]['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1026                                         if (!isset($this->elements[ $attrs['name'] ]['form'])) {
1027                                                 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
1028                                         }
1029                                 }
1030                                 if(isset($ename) && $this->currentComplexType){
1031                                         $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1032                                 }
1033                         break;
1034                         // we ignore enumeration values
1035                         //case 'enumeration':
1036                         //break;
1037                         case 'import':
1038                             if (isset($attrs['schemaLocation'])) {
1039                                         //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1040                     $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1041                                 } else {
1042                                         //$this->xdebug('import namespace ' . $attrs['namespace']);
1043                     $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1044                                         if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1045                                                 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1046                                         }
1047                                 }
1048                         break;
1049                         case 'restriction':
1050                                 //$this->xdebug("in restriction for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1051                                 if($this->currentElement){
1052                                         $this->elements[$this->currentElement]['type'] = $attrs['base'];
1053                                 } elseif($this->currentSimpleType){
1054                                         $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1055                                 } elseif($this->currentComplexType){
1056                                         $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1057                                         if(strstr($attrs['base'],':') == ':Array'){
1058                                                 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1059                                         }
1060                                 }
1061                         break;
1062                         case 'schema':
1063                                 $this->schemaInfo = $attrs;
1064                                 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1065                                 if (isset($attrs['targetNamespace'])) {
1066                                         $this->schemaTargetNamespace = $attrs['targetNamespace'];
1067                                 }
1068                                 if (!isset($attrs['elementFormDefault'])) {
1069                                         $this->schemaInfo['elementFormDefault'] = 'unqualified';
1070                                 }
1071                         break;
1072                         case 'simpleType':
1073                                 if(isset($attrs['name'])){
1074                                         $this->xdebug("processing simpleType for name " . $attrs['name']);
1075                                         $this->currentSimpleType = $attrs['name'];
1076                                         $this->simpleTypes[ $attrs['name'] ] = $attrs;
1077                                         $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1078                                         $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1079                                 } else {
1080                                         //echo 'not parsing: '.$name;
1081                                         //var_dump($attrs);
1082                                 }
1083                         break;
1084                         default:
1085                                 //$this->xdebug("do not have anything to do for element $name");
1086                 }
1087         }
1088
1089         /**
1090         * end-element handler
1091         *
1092         * @param    string $parser XML parser object
1093         * @param    string $name element name
1094         * @access   private
1095         */
1096         function schemaEndElement($parser, $name) {
1097                 // bring depth down a notch
1098                 $this->depth--;
1099                 // position of current element is equal to the last value left in depth_array for my depth
1100                 if(isset($this->depth_array[$this->depth])){
1101                 $pos = $this->depth_array[$this->depth];
1102         }
1103                 // move on...
1104                 if($name == 'complexType'){
1105                         $this->currentComplexType = false;
1106                         $this->currentElement = false;
1107                 }
1108                 if($name == 'element'){
1109                         $this->currentElement = false;
1110                 }
1111                 if($name == 'simpleType'){
1112                         $this->currentSimpleType = false;
1113                 }
1114         }
1115
1116         /**
1117         * element content handler
1118         *
1119         * @param    string $parser XML parser object
1120         * @param    string $data element content
1121         * @access   private
1122         */
1123         function schemaCharacterData($parser, $data){
1124                 $pos = $this->depth_array[$this->depth - 1];
1125                 $this->message[$pos]['cdata'] .= $data;
1126         }
1127
1128         /**
1129         * serialize the schema
1130         *
1131         * @access   public
1132         */
1133         function serializeSchema(){
1134
1135                 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1136                 $xml = '';
1137                 // imports
1138                 if (sizeof($this->imports) > 0) {
1139                         foreach($this->imports as $ns => $list) {
1140                                 foreach ($list as $ii) {
1141                                         if ($ii['location'] != '') {
1142                                                 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1143                                         } else {
1144                                                 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1145                                         }
1146                                 }
1147                         } 
1148                 } 
1149                 // complex types
1150                 foreach($this->complexTypes as $typeName => $attrs){
1151                         $contentStr = '';
1152                         // serialize child elements
1153                         if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1154                                 foreach($attrs['elements'] as $element => $eParts){
1155                                         if(isset($eParts['ref'])){
1156                                                 $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1157                                         } else {
1158                                                 $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
1159                                         }
1160                                 }
1161                         }
1162                         // attributes
1163                         if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1164                                 foreach($attrs['attrs'] as $attr => $aParts){
1165                                         $contentStr .= "    <$schemaPrefix:attribute ref=\"".$this->contractQName($aParts['ref']).'"';
1166                                         if(isset($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1167                                                 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1168                                                 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType']).'"';
1169                                         }
1170                                         $contentStr .= "/>\n";
1171                                 }
1172                         }
1173                         // if restriction
1174                         if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1175                                 $contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1176                         }
1177                         // compositor obviates complex/simple content
1178                         if(isset($attrs['compositor']) && ($attrs['compositor'] != '')){
1179                                 $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1180                         }
1181                         // complex or simple content
1182                         elseif((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1183                                 $contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1184                         }
1185                         // finalize complex type
1186                         if($contentStr != ''){
1187                                 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1188                         } else {
1189                                 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1190                         }
1191                         $xml .= $contentStr;
1192                 }
1193                 // simple types
1194                 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1195                         foreach($this->simpleTypes as $typeName => $attr){
1196                                 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n </$schemaPrefix:simpleType>";
1197                         }
1198                 }
1199                 // elements
1200                 if(isset($this->elements) && count($this->elements) > 0){
1201                         foreach($this->elements as $element => $eParts){
1202                                 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1203                         }
1204                 }
1205                 // attributes
1206                 if(isset($this->attributes) && count($this->attributes) > 0){
1207                         foreach($this->attributes as $attr => $aParts){
1208                                 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1209                         }
1210                 }
1211                 // finish 'er up
1212                 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1213                 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1214                         $el .= " xmlns:$nsp=\"$ns\"\n";
1215                 }
1216                 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1217                 return $xml;
1218         }
1219
1220         /**
1221         * adds debug data to the clas level debug string
1222         *
1223         * @param    string $string debug data
1224         * @access   private
1225         */
1226         function xdebug($string){
1227                 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1228         }
1229
1230     /**
1231     * get the PHP type of a user defined type in the schema
1232     * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1233     * returns false if no type exists, or not w/ the given namespace
1234     * else returns a string that is either a native php type, or 'struct'
1235     *
1236     * @param string $type, name of defined type
1237     * @param string $ns, namespace of type
1238     * @return mixed
1239     * @access public
1240     */
1241         function getPHPType($type,$ns){
1242                 if(isset($this->typemap[$ns][$type])){
1243                         //print "found type '$type' and ns $ns in typemap<br>";
1244                         return $this->typemap[$ns][$type];
1245                 } elseif(isset($this->complexTypes[$type])){
1246                         //print "getting type '$type' and ns $ns from complexTypes array<br>";
1247                         return $this->complexTypes[$type]['phpType'];
1248                 }
1249                 return false;
1250         }
1251
1252         /**
1253     * returns an array of information about a given type
1254     * returns false if no type exists by the given name
1255     *
1256         *        typeDef = array(
1257         *        'elements' => array(), // refs to elements array
1258         *       'restrictionBase' => '',
1259         *       'phpType' => '',
1260         *       'order' => '(sequence|all)',
1261         *       'attrs' => array() // refs to attributes array
1262         *       )
1263     *
1264     * @param string
1265     * @return mixed
1266     * @access public
1267     */
1268         function getTypeDef($type){
1269                 //$this->debug("in getTypeDef for type $type");
1270                 if(isset($this->complexTypes[$type])){
1271                         $this->xdebug("in getTypeDef, found complexType $type");
1272                         return $this->complexTypes[$type];
1273                 } elseif(isset($this->simpleTypes[$type])){
1274                         $this->xdebug("in getTypeDef, found simpleType $type");
1275                         if (!isset($this->simpleTypes[$type]['phpType'])) {
1276                                 // get info for type to tack onto the simple type
1277                                 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1278                                 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1279                                 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1280                                 $etype = $this->getTypeDef($uqType);
1281                                 if ($etype) {
1282                                         if (isset($etype['phpType'])) {
1283                                                 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1284                                         }
1285                                         if (isset($etype['elements'])) {
1286                                                 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1287                                         }
1288                                 }
1289                         }
1290                         return $this->simpleTypes[$type];
1291                 } elseif(isset($this->elements[$type])){
1292                         $this->xdebug("in getTypeDef, found element $type");
1293                         if (!isset($this->elements[$type]['phpType'])) {
1294                                 // get info for type to tack onto the element
1295                                 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1296                                 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1297                                 $etype = $this->getTypeDef($uqType);
1298                                 if ($etype) {
1299                                         if (isset($etype['phpType'])) {
1300                                                 $this->elements[$type]['phpType'] = $etype['phpType'];
1301                                         }
1302                                         if (isset($etype['elements'])) {
1303                                                 $this->elements[$type]['elements'] = $etype['elements'];
1304                                         }
1305                                 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1306                                         $this->elements[$type]['phpType'] = 'scalar';
1307                                 }
1308                         }
1309                         return $this->elements[$type];
1310                 } elseif(isset($this->attributes[$type])){
1311                         $this->xdebug("in getTypeDef, found attribute $type");
1312                         return $this->attributes[$type];
1313                 }
1314                 $this->xdebug("in getTypeDef, did not find $type");
1315                 return false;
1316         }
1317
1318         /**
1319     * returns a sample serialization of a given type, or false if no type by the given name
1320     *
1321     * @param string $type, name of type
1322     * @return mixed
1323     * @access public
1324     */
1325     function serializeTypeDef($type){
1326         //print "in sTD() for type $type<br>";
1327         if($typeDef = $this->getTypeDef($type)){
1328                 $str .= '<'.$type;
1329             if(is_array($typeDef['attrs'])){
1330                 foreach($attrs as $attName => $data){
1331                     $str .= " $attName=\"{type = ".$data['type']."}\"";
1332                 }
1333             }
1334             $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1335             if(count($typeDef['elements']) > 0){
1336                 $str .= ">";
1337                 foreach($typeDef['elements'] as $element => $eData){
1338                     $str .= $this->serializeTypeDef($element);
1339                 }
1340                 $str .= "</$type>";
1341             } elseif($typeDef['typeClass'] == 'element') {
1342                 $str .= "></$type>";
1343             } else {
1344                 $str .= "/>";
1345             }
1346                         return $str;
1347         }
1348         return false;
1349     }
1350
1351     /**
1352     * returns HTML form elements that allow a user
1353     * to enter values for creating an instance of the given type.
1354     *
1355     * @param string $name, name for type instance
1356     * @param string $type, name of type
1357     * @return string
1358     * @access public
1359         */
1360         function typeToForm($name,$type){
1361                 // get typedef
1362                 if($typeDef = $this->getTypeDef($type)){
1363                         // if struct
1364                         if($typeDef['phpType'] == 'struct'){
1365                                 $buffer .= '<table>';
1366                                 foreach($typeDef['elements'] as $child => $childDef){
1367                                         $buffer .= "
1368                                         <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1369                                         <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1370                                 }
1371                                 $buffer .= '</table>';
1372                         // if array
1373                         } elseif($typeDef['phpType'] == 'array'){
1374                                 $buffer .= '<table>';
1375                                 for($i=0;$i < 3; $i++){
1376                                         $buffer .= "
1377                                         <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1378                                         <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1379                                 }
1380                                 $buffer .= '</table>';
1381                         // if scalar
1382                         } else {
1383                                 $buffer .= "<input type='text' name='parameters[$name]'>";
1384                         }
1385                 } else {
1386                         $buffer .= "<input type='text' name='parameters[$name]'>";
1387                 }
1388                 return $buffer;
1389         }
1390         
1391         /**
1392         * adds a complex type to the schema
1393         * 
1394         * example: array
1395         * 
1396         * addType(
1397         *       'ArrayOfstring',
1398         *       'complexType',
1399         *       'array',
1400         *       '',
1401         *       'SOAP-ENC:Array',
1402         *       array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1403         *       'xsd:string'
1404         * );
1405         * 
1406         * example: PHP associative array ( SOAP Struct )
1407         * 
1408         * addType(
1409         *       'SOAPStruct',
1410         *       'complexType',
1411         *       'struct',
1412         *       'all',
1413         *       array('myVar'=> array('name'=>'myVar','type'=>'string')
1414         * );
1415         * 
1416         * @param name
1417         * @param typeClass (complexType|simpleType|attribute)
1418         * @param phpType: currently supported are array and struct (php assoc array)
1419         * @param compositor (all|sequence|choice)
1420         * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1421         * @param elements = array ( name = array(name=>'',type=>'') )
1422         * @param attrs = array(
1423         *       array(
1424         *               'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1425         *               "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1426         *       )
1427         * )
1428         * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1429         *
1430         */
1431         function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1432                 $this->complexTypes[$name] = array(
1433             'name'              => $name,
1434             'typeClass' => $typeClass,
1435             'phpType'   => $phpType,
1436                 'compositor'=> $compositor,
1437             'restrictionBase' => $restrictionBase,
1438                 'elements'      => $elements,
1439             'attrs'             => $attrs,
1440             'arrayType' => $arrayType
1441                 );
1442                 
1443                 $this->xdebug("addComplexType $name: " . $this->varDump($this->complexTypes[$name]));
1444         }
1445         
1446         /**
1447         * adds a simple type to the schema
1448         *
1449         * @param name
1450         * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1451         * @param typeClass (simpleType)
1452         * @param phpType: (scalar)
1453         * @see xmlschema
1454         * 
1455         */
1456         function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
1457                 $this->simpleTypes[$name] = array(
1458             'name'              => $name,
1459             'typeClass' => $typeClass,
1460             'phpType'   => $phpType,
1461             'type' => $restrictionBase
1462                 );
1463                 
1464                 $this->xdebug("addSimpleType $name: " . $this->varDump($this->simpleTypes[$name]));
1465         }
1466 }
1467
1468
1469
1470 ?><?php
1471
1472
1473
1474 /**
1475 * for creating serializable abstractions of native PHP types
1476 * NOTE: this is only really used when WSDL is not available.
1477 *
1478 * @author   Dietrich Ayala <dietrich@ganx4.com>
1479 * @version  $Id$
1480 * @access   public
1481 */
1482 class soapval extends nusoap_base {
1483         /**
1484         * constructor
1485         *
1486         * @param    string $name optional name
1487         * @param    string $type optional type name
1488         * @param        mixed $value optional value
1489         * @param        string $namespace optional namespace of value
1490         * @param        string $type_namespace optional namespace of type
1491         * @param        array $attributes associative array of attributes to add to element serialization
1492         * @access   public
1493         */
1494         function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1495                 $this->name = $name;
1496                 $this->value = $value;
1497                 $this->type = $type;
1498                 $this->element_ns = $element_ns;
1499                 $this->type_ns = $type_ns;
1500                 $this->attributes = $attributes;
1501     }
1502
1503         /**
1504         * return serialized value
1505         *
1506         * @return       string XML data
1507         * @access   private
1508         */
1509         function serialize($use='encoded') {
1510                 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1511     }
1512
1513         /**
1514         * decodes a soapval object into a PHP native type
1515         *
1516         * @param        object $soapval optional SOAPx4 soapval object, else uses self
1517         * @return       mixed
1518         * @access   public
1519         */
1520         function decode(){
1521                 return $this->value;
1522         }
1523 }
1524
1525
1526
1527 ?><?php
1528
1529
1530
1531 /**
1532 * transport class for sending/receiving data via HTTP and HTTPS
1533 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1534 *
1535 * @author   Dietrich Ayala <dietrich@ganx4.com>
1536 * @version  $Id$
1537 * @access public
1538 */
1539 class soap_transport_http extends nusoap_base {
1540
1541         var $url = '';
1542         var $uri = '';
1543         var $scheme = '';
1544         var $host = '';
1545         var $port = '';
1546         var $path = '';
1547         var $request_method = 'POST';
1548         var $protocol_version = '1.0';
1549         var $encoding = '';
1550         var $outgoing_headers = array();
1551         var $incoming_headers = array();
1552         var $outgoing_payload = '';
1553         var $incoming_payload = '';
1554         var $useSOAPAction = true;
1555         var $persistentConnection = false;
1556         var $ch = false;        // cURL handle
1557         var $username;
1558         var $password;
1559         
1560         /**
1561         * constructor
1562         */
1563         function soap_transport_http($url){
1564                 $this->url = $url;
1565                 
1566                 $u = parse_url($url);
1567                 foreach($u as $k => $v){
1568                         $this->debug("$k = $v");
1569                         $this->$k = $v;
1570                 }
1571                 
1572                 // add any GET params to path
1573                 if(isset($u['query']) && $u['query'] != ''){
1574             $this->path .= '?' . $u['query'];
1575                 }
1576                 
1577                 // set default port
1578                 if(!isset($u['port'])){
1579                         if($u['scheme'] == 'https'){
1580                                 $this->port = 443;
1581                         } else {
1582                                 $this->port = 80;
1583                         }
1584                 }
1585                 
1586                 $this->uri = $this->path;
1587                 
1588                 // build headers
1589                 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
1590                 $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
1591                 if (!isset($u['port'])) {
1592                         $this->outgoing_headers['Host'] = $this->host;
1593                 } else {
1594                         $this->outgoing_headers['Host'] = $this->host.':'.$this->port;
1595                 }
1596                 
1597                 if (isset($u['user']) && $u['user'] != '') {
1598                         $this->setCredentials($u['user'], isset($u['pass']) ? $u['pass'] : '');
1599                 }
1600         }
1601         
1602         function connect($connection_timeout=0,$response_timeout=30){
1603                 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
1604                 // "regular" socket.
1605                 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
1606                 //       loaded), and until PHP5 stream_get_wrappers is not available.
1607 //              if ($this->scheme == 'https') {
1608 //                      if (version_compare(phpversion(), '4.3.0') >= 0) {
1609 //                              if (extension_loaded('openssl')) {
1610 //                                      $this->scheme = 'ssl';
1611 //                                      $this->debug('Using SSL over OpenSSL');
1612 //                              }
1613 //                      }
1614 //              }
1615           if ($this->scheme == 'http' || $this->scheme == 'ssl') {
1616                 // use persistent connection
1617                 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
1618                         if (!feof($this->fp)) {
1619                                 $this->debug('Re-use persistent connection');
1620                                 return true;
1621                         }
1622                         fclose($this->fp);
1623                         $this->debug('Closed persistent connection at EOF');
1624                 }
1625
1626                 // munge host if using OpenSSL
1627                 if ($this->scheme == 'ssl') {
1628                         $host = 'ssl://' . $this->host;
1629                 } else {
1630                         $host = $this->host;
1631                 }
1632                 $this->debug('calling fsockopen with host ' . $host);
1633
1634                 // open socket
1635                 if($connection_timeout > 0){
1636                         $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
1637                 } else {
1638                         $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
1639                 }
1640                 
1641                 // test pointer
1642                 if(!$this->fp) {
1643                         $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error ('.$this->errno.'): '.$this->error_str);
1644                         $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error ('.$this->errno.'): '.$this->error_str);
1645                         return false;
1646                 }
1647                 
1648                 // set response timeout
1649                 socket_set_timeout( $this->fp, $response_timeout);
1650
1651                 $this->debug('socket connected');
1652                 return true;
1653           } else if ($this->scheme == 'https') {
1654                 if (!extension_loaded('curl')) {
1655                         $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
1656                         return false;
1657                 }
1658                 $this->debug('connect using https');
1659                 // init CURL
1660                 $this->ch = curl_init();
1661                 // set url
1662                 $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
1663                 // add path
1664                 $hostURL .= $this->path;
1665                 curl_setopt($this->ch, CURLOPT_URL, $hostURL);
1666                 // ask for headers in the response output
1667                 curl_setopt($this->ch, CURLOPT_HEADER, 1);
1668                 // ask for the response output as the return value
1669                 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
1670                 // encode
1671                 // We manage this ourselves through headers and encoding
1672 //              if(function_exists('gzuncompress')){
1673 //                      curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
1674 //              }
1675                 // persistent connection
1676                 if ($this->persistentConnection) {
1677                         // The way we send data, we cannot use persistent connections, since
1678                         // there will be some "junk" at the end of our request.
1679                         //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
1680                         $this->persistentConnection = false;
1681                         $this->outgoing_headers['Connection'] = 'close';
1682                 }
1683                 // set timeout (NOTE: cURL does not have separate connection and response timeouts)
1684                 if ($connection_timeout != 0) {
1685                         curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
1686                 }
1687
1688                 // recent versions of cURL turn on peer/host checking by default,
1689                 // while PHP binaries are not compiled with a default location for the
1690                 // CA cert bundle, so disable peer/host checking.
1691 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');           
1692                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
1693                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
1694
1695                 /*
1696                         TODO: support client certificates (thanks Tobias Boes)
1697         curl_setopt($this->ch, CURLOPT_CAINFO, '$pathToPemFiles/rootca.pem');
1698         curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
1699         curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
1700         curl_setopt($this->ch, CURLOPT_SSLCERT, '$pathToPemFiles/mycert.pem');
1701         curl_setopt($this->ch, CURLOPT_SSLKEY, '$pathToPemFiles/mykey.pem');
1702                 */
1703                 $this->debug('cURL connection set up');
1704                 return true;
1705           } else {
1706                 $this->setError('Unknown scheme ' . $this->scheme);
1707                 $this->debug('Unknown scheme ' . $this->scheme);
1708                 return false;
1709           }
1710         }
1711         
1712         /**
1713         * send the SOAP message via HTTP
1714         *
1715         * @param    string $data message data
1716         * @param    integer $timeout set connection timeout in seconds
1717         * @param        integer $response_timeout set response timeout in seconds
1718         * @return       string data
1719         * @access   public
1720         */
1721         function send($data, $timeout=0, $response_timeout=30) {
1722                 
1723                 $this->debug('entered send() with data of length: '.strlen($data));
1724
1725                 $this->tryagain = true;
1726                 $tries = 0;
1727                 while ($this->tryagain) {
1728                         $this->tryagain = false;
1729                         if ($tries++ < 2) {
1730                                 // make connnection
1731                                 if (!$this->connect($timeout, $response_timeout)){
1732                                         return false;
1733                                 }
1734                                 
1735                                 // send request
1736                                 if (!$this->sendRequest($data)){
1737                                         return false;
1738                                 }
1739                                 
1740                                 // get response
1741                                 $respdata = $this->getResponse();
1742                         } else {
1743                                 $this->setError('Too many tries to get an OK response');
1744                         }
1745                 }               
1746                 $this->debug('end of send()');
1747                 return $respdata;
1748         }
1749
1750
1751         /**
1752         * send the SOAP message via HTTPS 1.0 using CURL
1753         *
1754         * @param    string $msg message data
1755         * @param    integer $timeout set connection timeout in seconds
1756         * @param        integer $response_timeout set response timeout in seconds
1757         * @return       string data
1758         * @access   public
1759         */
1760         function sendHTTPS($data, $timeout=0, $response_timeout=30) {
1761                 return $this->send($data, $timeout, $response_timeout);
1762         }
1763         
1764         /**
1765         * if authenticating, set user credentials here
1766         *
1767         * @param    string $username
1768         * @param    string $password
1769         * @param        string $authtype
1770         * @param        array $digestRequest
1771         * @access   public
1772         */
1773         function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array()) {
1774                 global $_SERVER;
1775
1776                 // cf. RFC 2617
1777                 if ($authtype == 'basic') {
1778                         $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode($username.':'.$password);
1779                 } elseif ($authtype == 'digest') {
1780                         if (isset($digestRequest['nonce'])) {
1781                                 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
1782                                 
1783                                 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
1784         
1785                                 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
1786                                 $A1 = $username. ':' . $digestRequest['realm'] . ':' . $password;
1787         
1788                                 // H(A1) = MD5(A1)
1789                                 $HA1 = md5($A1);
1790         
1791                                 // A2 = Method ":" digest-uri-value
1792                                 $A2 = 'POST:' . $this->uri;
1793         
1794                                 // H(A2)
1795                                 $HA2 =  md5($A2);
1796         
1797                                 // KD(secret, data) = H(concat(secret, ":", data))
1798                                 // if qop == auth:
1799                                 // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
1800                                 //                              ":" nc-value
1801                                 //                              ":" unq(cnonce-value)
1802                                 //                              ":" unq(qop-value)
1803                                 //                              ":" H(A2)
1804                                 //                            ) <">
1805                                 // if qop is missing,
1806                                 // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
1807         
1808                                 $unhashedDigest = '';
1809                                 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
1810                                 $cnonce = $nonce;
1811                                 if ($digestRequest['qop'] != '') {
1812                                         $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
1813                                 } else {
1814                                         $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
1815                                 }
1816         
1817                                 $hashedDigest = md5($unhashedDigest);
1818         
1819                                 $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
1820                         }
1821                 }
1822                 $this->username = $username;
1823                 $this->password = $password;
1824                 $this->authtype = $authtype;
1825                 $this->digestRequest = $digestRequest;
1826         }
1827         
1828         /**
1829         * set the soapaction value
1830         *
1831         * @param    string $soapaction
1832         * @access   public
1833         */
1834         function setSOAPAction($soapaction) {
1835                 $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
1836         }
1837         
1838         /**
1839         * use http encoding
1840         *
1841         * @param    string $enc encoding style. supported values: gzip, deflate, or both
1842         * @access   public
1843         */
1844         function setEncoding($enc='gzip, deflate'){
1845                 $this->protocol_version = '1.1';
1846                 $this->outgoing_headers['Accept-Encoding'] = $enc;
1847                 $this->outgoing_headers['Connection'] = 'close';
1848                 $this->persistentConnection = false;
1849                 set_magic_quotes_runtime(0);
1850                 // deprecated
1851                 $this->encoding = $enc;
1852         }
1853         
1854         /**
1855         * set proxy info here
1856         *
1857         * @param    string $proxyhost
1858         * @param    string $proxyport
1859         * @param        string $proxyusername
1860         * @param        string $proxypassword
1861         * @access   public
1862         */
1863         function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
1864                 $this->uri = $this->url;
1865                 $this->host = $proxyhost;
1866                 $this->port = $proxyport;
1867                 if ($proxyusername != '' && $proxypassword != '') {
1868                         $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
1869                 }
1870         }
1871         
1872         /**
1873         * decode a string that is encoded w/ "chunked' transfer encoding
1874         * as defined in RFC2068 19.4.6
1875         *
1876         * @param    string $buffer
1877         * @param    string $lb
1878         * @returns      string
1879         * @access   public
1880         */
1881         function decodeChunked($buffer, $lb){
1882                 // length := 0
1883                 $length = 0;
1884                 $new = '';
1885                 
1886                 // read chunk-size, chunk-extension (if any) and CRLF
1887                 // get the position of the linebreak
1888                 $chunkend = strpos($buffer, $lb);
1889                 if ($chunkend == FALSE) {
1890                         $this->debug('no linebreak found in decodeChunked');
1891                         return $new;
1892                 }
1893                 $temp = substr($buffer,0,$chunkend);
1894                 $chunk_size = hexdec( trim($temp) );
1895                 $chunkstart = $chunkend + strlen($lb);
1896                 // while (chunk-size > 0) {
1897                 while ($chunk_size > 0) {
1898                         $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
1899                         $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
1900                         
1901                         // Just in case we got a broken connection
1902                         if ($chunkend == FALSE) {
1903                             $chunk = substr($buffer,$chunkstart);
1904                                 // append chunk-data to entity-body
1905                         $new .= $chunk;
1906                             $length += strlen($chunk);
1907                             break;
1908                         }
1909                         
1910                         // read chunk-data and CRLF
1911                         $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1912                         // append chunk-data to entity-body
1913                         $new .= $chunk;
1914                         // length := length + chunk-size
1915                         $length += strlen($chunk);
1916                         // read chunk-size and CRLF
1917                         $chunkstart = $chunkend + strlen($lb);
1918                         
1919                         $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
1920                         if ($chunkend == FALSE) {
1921                                 break; //Just in case we got a broken connection
1922                         }
1923                         $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1924                         $chunk_size = hexdec( trim($temp) );
1925                         $chunkstart = $chunkend;
1926                 }
1927                 return $new;
1928         }
1929         
1930         /*
1931          *      Writes payload, including HTTP headers, to $this->outgoing_payload.
1932          */
1933         function buildPayload($data) {
1934                 // add content-length header
1935                 $this->outgoing_headers['Content-Length'] = strlen($data);
1936                 
1937                 // start building outgoing payload:
1938                 $this->outgoing_payload = "$this->request_method $this->uri HTTP/$this->protocol_version\r\n";
1939
1940                 // loop thru headers, serializing
1941                 foreach($this->outgoing_headers as $k => $v){
1942                         $this->outgoing_payload .= $k.': '.$v."\r\n";
1943                 }
1944                 
1945                 // header/body separator
1946                 $this->outgoing_payload .= "\r\n";
1947                 
1948                 // add data
1949                 $this->outgoing_payload .= $data;
1950         }
1951
1952         function sendRequest($data){
1953                 // build payload
1954                 $this->buildPayload($data);
1955
1956           if ($this->scheme == 'http' || $this->scheme == 'ssl') {
1957                 // send payload
1958                 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1959                         $this->setError('couldn\'t write message data to socket');
1960                         $this->debug('couldn\'t write message data to socket');
1961                         return false;
1962                 }
1963                 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
1964                 return true;
1965           } else if ($this->scheme == 'https') {
1966                 // set payload
1967                 // TODO: cURL does say this should only be the verb, and in fact it
1968                 // turns out that the URI and HTTP version are appended to this, which
1969                 // some servers refuse to work with
1970                 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1971                 foreach($this->outgoing_headers as $k => $v){
1972                         $curl_headers[] = "$k: $v";
1973                 }
1974                 curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
1975                 if ($this->request_method == "POST") {
1976                         curl_setopt($this->ch, CURLOPT_POST, 1);
1977                         curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
1978                 } else {
1979                 }
1980                 $this->debug('set cURL payload');
1981                 return true;
1982           }
1983         }
1984         
1985         function getResponse(){
1986                 $this->incoming_payload = '';
1987             
1988           if ($this->scheme == 'http' || $this->scheme == 'ssl') {
1989             // loop until headers have been retrieved
1990             $data = '';
1991             while (!isset($lb)){
1992
1993                         // We might EOF during header read.
1994                         if(feof($this->fp)) {
1995                                 $this->incoming_payload = $data;
1996                                 $this->debug('found no headers before EOF after length ' . strlen($data));
1997                                 $this->debug("received before EOF:\n" . $data);
1998                                 $this->setError('server failed to send headers');
1999                                 return false;
2000                         }
2001
2002                         $data .= fgets($this->fp, 256);
2003                         $pos = strpos($data,"\r\n\r\n");
2004                         if($pos > 1){
2005                                 $lb = "\r\n";
2006                         } else {
2007                                 $pos = strpos($data,"\n\n");
2008                                 if($pos > 1){
2009                                         $lb = "\n";
2010                                 }
2011                         }
2012                         // remove 100 header
2013                         if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2014                                 unset($lb);
2015                                 $data = '';
2016                         }//
2017                 }
2018                 // store header data
2019                 $this->incoming_payload .= $data;
2020                 $this->debug('found end of headers after length ' . strlen($data));
2021                 // process headers
2022                 $header_data = trim(substr($data,0,$pos));
2023                 $header_array = explode($lb,$header_data);
2024                 foreach($header_array as $header_line){
2025                         $arr = explode(':',$header_line, 2);
2026                         if(count($arr) > 1){
2027                                 $header_name = strtolower(trim($arr[0]));
2028                                 $this->incoming_headers[$header_name] = trim($arr[1]);
2029                         } else if (isset($header_name)) {
2030                                 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2031                         }
2032                 }
2033                 
2034                 // loop until msg has been received
2035                 // TODO: handle chunking in this loop to allow persistent connections with chunking
2036                 $content_length = isset($this->incoming_headers['content-length']) ? $this->incoming_headers['content-length'] : 2147483647;
2037                 $data = '';
2038                 $strlen = 0;
2039             while (($strlen < $content_length) && (!feof($this->fp))) {
2040                 $readlen = min(8192, $content_length - $strlen);
2041                         $tmp = fread($this->fp, $readlen);
2042                         $strlen += strlen($tmp);
2043                         $data .= $tmp;
2044                 }
2045                 $this->debug('read body of length ' . strlen($data));
2046                 $this->incoming_payload .= $data;
2047                 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2048                 
2049                 // close filepointer
2050                 if(
2051                         //(isset($this->incoming_headers['connection']) && $this->incoming_headers['connection'] == 'close') || 
2052                         (! $this->persistentConnection) || feof($this->fp)){
2053                         fclose($this->fp);
2054                         $this->fp = false;
2055                         $this->debug('closed socket');
2056                 }
2057                 
2058                 // connection was closed unexpectedly
2059                 if($this->incoming_payload == ''){
2060                         $this->setError('no response from server');
2061                         return false;
2062                 }
2063                 
2064                 // decode transfer-encoding
2065                 if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2066                         if(!$data = $this->decodeChunked($data, $lb)){
2067                                 $this->setError('Decoding of chunked data failed');
2068                                 return false;
2069                         }
2070                         //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2071                         // set decoded payload
2072                         $this->incoming_payload = $header_data.$lb.$lb.$data;
2073                 }
2074                 
2075           } else if ($this->scheme == 'https') {
2076                 // send and receive
2077                 $this->debug('send and receive with cURL');
2078                 $this->incoming_payload = curl_exec($this->ch);
2079                 $data = $this->incoming_payload;
2080
2081         $cErr = curl_error($this->ch);
2082                 if ($cErr != '') {
2083                 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
2084                         foreach(curl_getinfo($this->ch) as $k => $v){
2085                                 $err .= "$k: $v<br>";
2086                         }
2087                         $this->debug($err);
2088                         $this->setError($err);
2089                         curl_close($this->ch);
2090                 return false;
2091                 } else {
2092                         //echo '<pre>';
2093                         //var_dump(curl_getinfo($this->ch));
2094                         //echo '</pre>';
2095                 }
2096                 // close curl
2097                 $this->debug('No cURL error, closing cURL');
2098                 curl_close($this->ch);
2099                 
2100                 // remove 100 header
2101                 if (ereg('^HTTP/1.1 100',$data)) {
2102                         if ($pos = strpos($data,"\r\n\r\n")) {
2103                                 $data = ltrim(substr($data,$pos));
2104                         } elseif($pos = strpos($data,"\n\n") ) {
2105                                 $data = ltrim(substr($data,$pos));
2106                         }
2107                 }
2108                 
2109                 // separate content from HTTP headers
2110                 if ($pos = strpos($data,"\r\n\r\n")) {
2111                         $lb = "\r\n";
2112                 } elseif( $pos = strpos($data,"\n\n")) {
2113                         $lb = "\n";
2114                 } else {
2115                         $this->debug('no proper separation of headers and document');
2116                         $this->setError('no proper separation of headers and document');
2117                         return false;
2118                 }
2119                 $header_data = trim(substr($data,0,$pos));
2120                 $header_array = explode($lb,$header_data);
2121                 $data = ltrim(substr($data,$pos));
2122                 $this->debug('found proper separation of headers and document');
2123                 $this->debug('cleaned data, stringlen: '.strlen($data));
2124                 // clean headers
2125                 foreach ($header_array as $header_line) {
2126                         $arr = explode(':',$header_line,2);
2127                         if (count($arr) > 1) {
2128                                 $this->incoming_headers[strtolower(trim($arr[0]))] = trim($arr[1]);
2129                         }
2130                 }
2131           }
2132
2133                 // see if we need to resend the request with http digest authentication
2134                 if (isset($this->incoming_headers['www-authenticate']) && strstr($header_array[0], '401 Unauthorized')) {
2135                         if (substr("Digest ", $this->incoming_headers['www-authenticate'])) {
2136                                 // remove "Digest " from our elements
2137                                 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2138                                 
2139                                 // parse elements into array
2140                                 $digestElements = explode(', ', $digestString);
2141                                 while (list($key, $val) = each($digestElements)) {
2142                                         $tempElement = explode('=', $val);
2143                                         $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2144                                 }
2145
2146                                 // should have (at least) qop, realm, nonce
2147                                 if (isset($digestRequest['nonce'])) {
2148                                         $this->debug('found nonce in WWW-Authenticate: ' . $this->incoming_headers['www-authenticate']);
2149                                         $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2150                                         $this->tryagain = true;
2151                                         return false;
2152                                 }
2153                         }
2154                         $this->debug('HTTP authentication failed');
2155                         $this->setError('HTTP authentication failed');
2156                         return false;
2157                 }
2158                 
2159                 // decode content-encoding
2160                 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2161                         if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2162                         // if decoding works, use it. else assume data wasn't gzencoded
2163                         if(function_exists('gzuncompress')){
2164                                         //$timer->setMarker('starting decoding of gzip/deflated content');
2165                                         if($this->incoming_headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)){
2166                                         $data = $degzdata;
2167                                         } elseif($this->incoming_headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){    // do our best
2168                                                 $data = $degzdata;
2169                                         } else {
2170                                                 $this->setError('Errors occurred when trying to decode the data');
2171                                         }
2172                                         //$timer->setMarker('finished decoding of gzip/deflated content');
2173                                         //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2174                                         // set decoded payload
2175                                         $this->incoming_payload = $header_data.$lb.$lb.$data;
2176                         } else {
2177                                         $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2178                                 }
2179                         }
2180                 }
2181                 
2182                 if(strlen($data) == 0){
2183                         $this->debug('no data after headers!');
2184                         $this->setError('no data present after HTTP headers');
2185                         return false;
2186                 }
2187                 
2188                 return $data;
2189         }
2190
2191         function setContentType($type, $charset = false) {
2192                 $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2193         }
2194
2195         function usePersistentConnection(){
2196                 if (isset($this->outgoing_headers['Accept-Encoding'])) {
2197                         return false;
2198                 }
2199                 $this->protocol_version = '1.1';
2200                 $this->persistentConnection = true;
2201                 $this->outgoing_headers['Connection'] = 'Keep-Alive';
2202                 return true;
2203         }
2204 }
2205
2206 ?><?php
2207
2208
2209
2210 /**
2211 *
2212 * soap_server allows the user to create a SOAP server
2213 * that is capable of receiving messages and returning responses
2214 *
2215 * NOTE: WSDL functionality is experimental
2216 *
2217 * @author   Dietrich Ayala <dietrich@ganx4.com>
2218 * @version  $Id$
2219 * @access   public
2220 */
2221 class nusoap_server extends nusoap_base {
2222         var $headers = array();                 // HTTP headers of request
2223         var $request = '';                              // HTTP request
2224         var $requestHeaders = '';               // SOAP headers from request (incomplete namespace resolution) (text)
2225         var $document = '';                             // SOAP body request portion (incomplete namespace resolution) (text)
2226         var $requestSOAP = '';                  // SOAP payload for request (text)
2227         var $methodURI = '';                    // requested method namespace URI
2228         var $methodname = '';                   // name of method requested
2229         var $methodparams = array();    // method parameters from request
2230         var $xml_encoding = '';                 // character set encoding of incoming (request) messages
2231         var $SOAPAction = '';                   // SOAP Action from request
2232
2233         var $outgoing_headers = array();// HTTP headers of response
2234         var $response = '';                             // HTTP response
2235         var $responseHeaders = '';              // SOAP headers for response (text)
2236         var $responseSOAP = '';                 // SOAP payload for response (text)
2237         var $methodreturn = false;              // method return to place in response
2238         var $fault = false;                             // SOAP fault for response
2239         var $result = 'successful';             // text indication of result (for debugging)
2240
2241         var $operations = array();              // assoc array of operations => opData
2242         var $wsdl = false;                              // wsdl instance
2243         var $externalWSDLURL = false;   // URL for WSDL
2244         var $debug_flag = false;                // whether to append debug to response as XML comment
2245         
2246         /**
2247         * constructor
2248     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
2249         *
2250     * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
2251         * @access   public
2252         */
2253         function soap_server($wsdl=false){
2254
2255                 // turn on debugging?
2256                 global $debug;
2257                 global $_REQUEST;
2258                 global $_SERVER;
2259                 global $HTTP_SERVER_VARS;
2260
2261                 if (isset($debug)) {
2262                         $this->debug_flag = $debug;
2263                 } else if (isset($_REQUEST['debug'])) {
2264                         $this->debug_flag = $_REQUEST['debug'];
2265                 } else if (isset($_SERVER['QUERY_STRING'])) {
2266                         $qs = explode('&', $_SERVER['QUERY_STRING']);
2267                         foreach ($qs as $v) {
2268                                 if (substr($v, 0, 6) == 'debug=') {
2269                                         $this->debug_flag = substr($v, 6);
2270                                 }
2271                         }
2272                 } else if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
2273                         $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
2274                         foreach ($qs as $v) {
2275                                 if (substr($v, 0, 6) == 'debug=') {
2276                                         $this->debug_flag = substr($v, 6);
2277                                 }
2278                         }
2279                 }
2280
2281                 // wsdl
2282                 if($wsdl){
2283                         if (is_object($wsdl) && is_a($wsdl, 'wsdl')) {
2284                                 $this->wsdl = $wsdl;
2285                                 $this->externalWSDLURL = $this->wsdl->wsdl;
2286                                 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
2287                         } else {
2288                                 $this->debug('Create wsdl from ' . $wsdl);
2289                                 $this->wsdl = new wsdl($wsdl);
2290                                 $this->externalWSDLURL = $wsdl;
2291                         }
2292                         $this->debug("wsdl...\n" . $this->wsdl->debug_str);
2293                         $this->wsdl->debug_str = '';
2294                         if($err = $this->wsdl->getError()){
2295                                 die('WSDL ERROR: '.$err);
2296                         }
2297                 }
2298         }
2299
2300         /**
2301         * processes request and returns response
2302         *
2303         * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
2304         * @access   public
2305         */
2306         function service($data){
2307                 global $QUERY_STRING;
2308                 if(isset($_SERVER['QUERY_STRING'])){
2309                         $qs = $_SERVER['QUERY_STRING'];
2310                 } elseif(isset($GLOBALS['QUERY_STRING'])){
2311                         $qs = $GLOBALS['QUERY_STRING'];
2312                 } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
2313                         $qs = $QUERY_STRING;
2314                 }
2315
2316                 if(isset($qs) && ereg('wsdl', $qs) ){
2317                         // This is a request for WSDL
2318                         if($this->externalWSDLURL){
2319               if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
2320                                 header('Location: '.$this->externalWSDLURL);
2321               } else { // assume file
2322                 header("Content-Type: text/xml\r\n");
2323                 $fp = fopen($this->externalWSDLURL, 'r');
2324                 fpassthru($fp);
2325               }
2326                         } else {
2327                                 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
2328                                 print $this->wsdl->serialize();
2329                         }
2330                 } elseif($data == '' && $this->wsdl){
2331                         // print web interface
2332                         print $this->webDescription();
2333                 } else {
2334                         // handle the request
2335                         $this->parse_request($data);
2336                         if (! $this->fault) {
2337                                 $this->invoke_method();
2338                         }
2339                         if (! $this->fault) {
2340                                 $this->serialize_return();
2341                         }
2342                         $this->send_response();
2343                 }
2344         }
2345
2346         /**
2347         * parses HTTP request headers.
2348         *
2349         * The following fields are set by this function (when successful)
2350         *
2351         * headers
2352         * request
2353         * xml_encoding
2354         * SOAPAction
2355         *
2356         * @access   private
2357         */
2358         function parse_http_headers() {
2359                 global $HTTP_SERVER_VARS;
2360                 global $_SERVER;
2361
2362                 $this->request = '';
2363                 if(function_exists('getallheaders')){
2364                         $this->headers = getallheaders();
2365                         foreach($this->headers as $k=>$v){
2366                                 $this->request .= "$k: $v\r\n";
2367                                 $this->debug("$k: $v");
2368                         }
2369                         // get SOAPAction header
2370                         if(isset($this->headers['SOAPAction'])){
2371                                 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
2372                         }
2373                         // get the character encoding of the incoming request
2374                         if(strpos($this->headers['Content-Type'],'=')){
2375                                 $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
2376                                 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
2377                                         $this->xml_encoding = strtoupper($enc);
2378                                 } else {
2379                                         $this->xml_encoding = 'US-ASCII';
2380                                 }
2381                         } else {
2382                                 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2383                                 $this->xml_encoding = 'UTF-8';
2384                         }
2385                 } elseif(isset($_SERVER) && is_array($_SERVER)){
2386                         foreach ($_SERVER as $k => $v) {
2387                                 if (substr($k, 0, 5) == 'HTTP_') {
2388                                         $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2389                                 } else {
2390                                         $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k))));
2391                                 }
2392                                 if ($k == 'Soapaction') {
2393                                         // get SOAPAction header
2394                                         $k = 'SOAPAction';
2395                                         $v = str_replace('"', '', $v);
2396                                         $v = str_replace('\\', '', $v);
2397                                         $this->SOAPAction = $v;
2398                                 } else if ($k == 'Content-Type') {
2399                                         // get the character encoding of the incoming request
2400                                         if (strpos($v, '=')) {
2401                                                 $enc = substr(strstr($v, '='), 1);
2402                                                 $enc = str_replace('"', '', $enc);
2403                                                 $enc = str_replace('\\', '', $enc);
2404                                                 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
2405                                                         $this->xml_encoding = strtoupper($enc);
2406                                                 } else {
2407                                                         $this->xml_encoding = 'US-ASCII';
2408                                                 }
2409                                         } else {
2410                                                 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2411                                                 $this->xml_encoding = 'UTF-8';
2412                                         }
2413                                 }
2414                                 $this->headers[$k] = $v;
2415                                 $this->request .= "$k: $v\r\n";
2416                                 $this->debug("$k: $v");
2417                         }
2418                 } elseif (is_array($HTTP_SERVER_VARS)) {
2419                         foreach ($HTTP_SERVER_VARS as $k => $v) {
2420                                 if (substr($k, 0, 5) == 'HTTP_') {
2421                                         $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2422                                         if ($k == 'Soapaction') {
2423                                                 // get SOAPAction header
2424                                                 $k = 'SOAPAction';
2425                                                 $v = str_replace('"', '', $v);
2426                                                 $v = str_replace('\\', '', $v);
2427                                                 $this->SOAPAction = $v;
2428                                         } else if ($k == 'Content-Type') {
2429                                                 // get the character encoding of the incoming request
2430                                                 if (strpos($v, '=')) {
2431                                                         $enc = substr(strstr($v, '='), 1);
2432                                                         $enc = str_replace('"', '', $enc);
2433                                                         $enc = str_replace('\\', '', $enc);
2434                                                         if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
2435                                                                 $this->xml_encoding = strtoupper($enc);
2436                                                         } else {
2437                                                                 $this->xml_encoding = 'US-ASCII';
2438                                                         }
2439                                                 } else {
2440                                                         // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2441                                                         $this->xml_encoding = 'UTF-8';
2442                                                 }
2443                                         }
2444                                         $this->headers[$k] = $v;
2445                                         $this->request .= "$k: $v\r\n";
2446                                         $this->debug("$k: $v");
2447                                 }
2448                         }
2449                 }
2450         }
2451
2452         /**
2453         * parses a request
2454         *
2455         * The following fields are set by this function (when successful)
2456         *
2457         * headers
2458         * request
2459         * xml_encoding
2460         * SOAPAction
2461         * request
2462         * requestSOAP
2463         * methodURI
2464         * methodname
2465         * methodparams
2466         * requestHeaders
2467         * document
2468         *
2469         * This sets the fault field on error
2470         *
2471         * @param    string $data XML string
2472         * @access   private
2473         */
2474         function parse_request($data='') {
2475                 $this->debug('entering parse_request() on '.date('H:i Y-m-d'));
2476                 $this->parse_http_headers();
2477                 $this->debug('got character encoding: '.$this->xml_encoding);
2478                 // uncompress if necessary
2479                 if (isset($this->headers['Content-Encoding']) && $this->headers['Content-Encoding'] != '') {
2480                         $this->debug('got content encoding: ' . $this->headers['Content-Encoding']);
2481                         if ($this->headers['Content-Encoding'] == 'deflate' || $this->headers['Content-Encoding'] == 'gzip') {
2482                         // if decoding works, use it. else assume data wasn't gzencoded
2483                                 if (function_exists('gzuncompress')) {
2484                                         if ($this->headers['Content-Encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
2485                                                 $data = $degzdata;
2486                                         } elseif ($this->headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
2487                                                 $data = $degzdata;
2488                                         } else {
2489                                                 $this->fault('Server', 'Errors occurred when trying to decode the data');
2490                                                 return;
2491                                         }
2492                                 } else {
2493                                         $this->fault('Server', 'This Server does not support compressed data');
2494                                         return;
2495                                 }
2496                         }
2497                 }
2498                 $this->request .= "\r\n".$data;
2499                 $this->requestSOAP = $data;
2500                 // parse response, get soap parser obj
2501                 $parser = new soap_parser($data,$this->xml_encoding);
2502                 // parser debug
2503                 $this->debug("parser debug: \n".$parser->debug_str);
2504                 // if fault occurred during message parsing
2505                 if($err = $parser->getError()){
2506                         $this->result = 'fault: error in msg parsing: '.$err;
2507                         $this->fault('Server',"error in msg parsing:\n".$err);
2508                 // else successfully parsed request into soapval object
2509                 } else {
2510                         // get/set methodname
2511                         $this->methodURI = $parser->root_struct_namespace;
2512                         $this->methodname = $parser->root_struct_name;
2513                         $this->debug('method name: '.$this->methodname);
2514                         $this->debug('calling parser->get_response()');
2515                         $this->methodparams = $parser->get_response();
2516                         // get SOAP headers
2517                         $this->requestHeaders = $parser->getHeaders();
2518             // add document for doclit support
2519             $this->document = $parser->document;
2520                 }
2521                 $this->debug('leaving parse_request() on '.date('H:i Y-m-d'));
2522         }
2523
2524         /**
2525         * invokes a PHP function for the requested SOAP method
2526         *
2527         * The following fields are set by this function (when successful)
2528         *
2529         * methodreturn
2530         *
2531         * Note that the PHP function that is called may also set the following
2532         * fields to affect the response sent to the client
2533         *
2534         * responseHeaders
2535         * outgoing_headers
2536         *
2537         * This sets the fault field on error
2538         *
2539         * @access   private
2540         */
2541         function invoke_method() {
2542                 $this->debug('entering invoke_method');
2543                 // does method exist?
2544                 if(!function_exists($this->methodname)){
2545                         // "method not found" fault here
2546                         $this->debug("method '$this->methodname' not found!");
2547                         $this->result = 'fault: method not found';
2548                         $this->fault('Server',"method '$this->methodname' not defined in service");
2549                         return;
2550                 }
2551                 if($this->wsdl){
2552                         if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
2553                         //if(
2554                         $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
2555                                 return;
2556                     }
2557                     $this->debug('opData is ' . $this->varDump($this->opData));
2558                 }
2559                 $this->debug("method '$this->methodname' exists");
2560                 // evaluate message, getting back parameters
2561                 // verify that request parameters match the method's signature
2562                 if(! $this->verify_method($this->methodname,$this->methodparams)){
2563                         // debug
2564                         $this->debug('ERROR: request not verified against method signature');
2565                         $this->result = 'fault: request failed validation against method signature';
2566                         // return fault
2567                         $this->fault('Server',"Operation '$this->methodname' not defined in service.");
2568                         return;
2569                 }
2570
2571                 // if there are parameters to pass
2572         $this->debug('params var dump '.$this->varDump($this->methodparams));
2573                 if($this->methodparams){
2574                         $this->debug("calling '$this->methodname' with params");
2575                         if (! function_exists('call_user_func_array')) {
2576                                 $this->debug('calling method using eval()');
2577                                 $funcCall = $this->methodname.'(';
2578                                 foreach($this->methodparams as $param) {
2579                                         $funcCall .= "\"$param\",";
2580                                 }
2581                                 $funcCall = substr($funcCall, 0, -1).')';
2582                                 $this->debug('function call:<br>'.$funcCall);
2583                                 @eval("\$this->methodreturn = $funcCall;");
2584                         } else {
2585                                 $this->debug('calling method using call_user_func_array()');
2586                                 $this->methodreturn = call_user_func_array("$this->methodname",$this->methodparams);
2587                         }
2588             $this->debug('response var dump'.$this->varDump($this->methodreturn));
2589                 } else {
2590                         // call method w/ no parameters
2591                         $this->debug("calling $this->methodname w/ no params");
2592                         $m = $this->methodname;
2593                         $this->methodreturn = @$m();
2594                 }
2595                 $this->debug("leaving invoke_method: called method $this->methodname, received $this->methodreturn of type".gettype($this->methodreturn));
2596         }
2597
2598         /**
2599         * serializes the return value from a PHP function into a full SOAP Envelope
2600         *
2601         * The following fields are set by this function (when successful)
2602         *
2603         * responseSOAP
2604         *
2605         * This sets the fault field on error
2606         *
2607         * @access   private
2608         */
2609         function serialize_return() {
2610                 $this->debug("Entering serialize_return");
2611                 // if we got nothing back. this might be ok (echoVoid)
2612                 if(isset($this->methodreturn) && ($this->methodreturn != '' || is_bool($this->methodreturn))) {
2613                         // if fault
2614                         if(get_class($this->methodreturn) == 'soap_fault'){
2615                                 $this->debug('got a fault object from method');
2616                                 $this->fault = $this->methodreturn;
2617                                 return;
2618                         // if return val is soapval object
2619                         } elseif(get_class($this->methodreturn) == 'soapval'){
2620                                 $this->debug('got a soapval object from method');
2621                                 $return_val = $this->methodreturn->serialize();
2622                         // returned other
2623                         } else {
2624                                 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
2625                                 $this->debug('serializing return value');
2626                                 if($this->wsdl){
2627                                         // weak attempt at supporting multiple output params
2628                                         if(sizeof($this->opData['output']['parts']) > 1){
2629                                         $opParams = $this->methodreturn;
2630                                     } else {
2631                                         $opParams = array($this->methodreturn);
2632                                     }
2633                                     $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2634                                         if($errstr = $this->wsdl->getError()){
2635                                                 $this->debug('got wsdl error: '.$errstr);
2636                                                 $this->fault('Server', 'got wsdl error: '.$errstr);
2637                                                 return;
2638                                         }
2639                                 } else {
2640                                     $return_val = $this->serialize_val($this->methodreturn, 'return');
2641                                 }
2642                         }
2643                         $this->debug('return val: '.$this->varDump($return_val));
2644                 } else {
2645                         $return_val = '';
2646                         $this->debug('got no response from method');
2647                 }
2648                 $this->debug('serializing response');
2649                 if ($this->wsdl) {
2650                         if ($this->opData['style'] == 'rpc') {
2651                                 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
2652                         } else {
2653                                 $payload = $return_val;
2654                         }
2655                 } else {
2656                         $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
2657                 }
2658                 $this->result = 'successful';
2659                 if($this->wsdl){
2660                         //if($this->debug_flag){
2661                 $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
2662             //  }
2663                         // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2664                         $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
2665                 } else {
2666                         $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
2667                 }
2668                 $this->debug("Leaving serialize_return");
2669         }
2670
2671         /**
2672         * sends an HTTP response
2673         *
2674         * The following fields are set by this function (when successful)
2675         *
2676         * outgoing_headers
2677         * response
2678         *
2679         * @access   private
2680         */
2681         function send_response() {
2682                 $this->debug('Enter send_response');
2683                 if ($this->fault) {
2684                         $payload = $this->fault->serialize();
2685                         $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
2686                         $this->outgoing_headers[] = "Status: 500 Internal Server Error";
2687                 } else {
2688                         $payload = $this->responseSOAP;
2689                         // Some combinations of PHP+Web server allow the Status
2690                         // to come through as a header.  Since OK is the default
2691                         // just do nothing.
2692                         // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
2693                         // $this->outgoing_headers[] = "Status: 200 OK";
2694                 }
2695         // add debug data if in debug mode
2696                 if(isset($this->debug_flag) && $this->debug_flag){
2697                         while (strpos($this->debug_str, '--')) {
2698                                 $this->debug_str = str_replace('--', '- -', $this->debug_str);
2699                         }
2700                 $payload .= "<!--\n" . $this->debug_str . "\n-->";
2701         }
2702                 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
2703                 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2704                 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
2705                 // Let the Web server decide about this
2706                 //$this->outgoing_headers[] = "Connection: Close\r\n";
2707                 $this->outgoing_headers[] = "Content-Type: text/xml; charset=$this->soap_defencoding";
2708                 //begin code to compress payload - by John
2709                 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['Accept-Encoding'])) {     
2710                    if (strstr($this->headers['Accept-Encoding'], 'deflate')) {
2711                                 if (function_exists('gzcompress')) {
2712                                         if (isset($this->debug_flag) && $this->debug_flag) {
2713                                                 $payload .= "<!-- Content being deflated -->";
2714                                         }
2715                                         $this->outgoing_headers[] = "Content-Encoding: deflate";
2716                                         $payload = gzcompress($payload);
2717                                 } else {
2718                                         if (isset($this->debug_flag) && $this->debug_flag) {
2719                                                 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
2720                                         }
2721                                 }
2722                    } else if (strstr($this->headers['Accept-Encoding'], 'gzip')) {
2723                                 if (function_exists('gzencode')) {
2724                                         if (isset($this->debug_flag) && $this->debug_flag) {
2725                                                 $payload .= "<!-- Content being gzipped -->";
2726                                         }
2727                                         $this->outgoing_headers[] = "Content-Encoding: gzip";
2728                                         $payload = gzencode($payload);
2729                                 } else {
2730                                         if (isset($this->debug_flag) && $this->debug_flag) {
2731                                                 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
2732                                         }
2733                                 }
2734                         }
2735                 }
2736                 //end code
2737                 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
2738                 reset($this->outgoing_headers);
2739                 foreach($this->outgoing_headers as $hdr){
2740                         header($hdr, false);
2741                 }
2742                 $this->response = join("\r\n",$this->outgoing_headers)."\r\n".$payload;
2743                 print $payload;
2744         }
2745
2746         /**
2747         * takes the value that was created by parsing the request
2748         * and compares to the method's signature, if available.
2749         *
2750         * @param        mixed
2751         * @return       boolean
2752         * @access   private
2753         */
2754         function verify_method($operation,$request){
2755                 if(isset($this->wsdl) && is_object($this->wsdl)){
2756                         if($this->wsdl->getOperationData($operation)){
2757                                 return true;
2758                         }
2759             } elseif(isset($this->operations[$operation])){
2760                         return true;
2761                 }
2762                 return false;
2763         }
2764
2765         /**
2766         * add a method to the dispatch map
2767         *
2768         * @param    string $methodname
2769         * @param    string $in array of input values
2770         * @param    string $out array of output values
2771         * @access   public
2772         */
2773         function add_to_map($methodname,$in,$out){
2774                         $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2775         }
2776
2777         /**
2778         * register a service with the server
2779         *
2780         * @param    string $methodname
2781         * @param    string $in assoc array of input values: key = param name, value = param type
2782         * @param    string $out assoc array of output values: key = param name, value = param type
2783         * @param        string $namespace
2784         * @param        string $soapaction
2785         * @param        string $style optional (rpc|document)
2786         * @param        string $use optional (encoded|literal)
2787         * @param        string $documentation optional Description to include in WSDL
2788         * @access   public
2789         */
2790         function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false,$documentation=''){
2791                 if($this->externalWSDLURL){
2792                         die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2793                 }
2794             if(false == $in) {
2795                 }
2796                 if(false == $out) {
2797                 }
2798                 if(false == $namespace) {
2799                 }
2800                 if(false == $soapaction) {
2801                         $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2802                         $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2803                         $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
2804                 }
2805                 if(false == $style) {
2806                         $style = "rpc";
2807                 }
2808                 if(false == $use) {
2809                         $use = "encoded";
2810                 }
2811                 
2812                 $this->operations[$name] = array(
2813             'name' => $name,
2814             'in' => $in,
2815             'out' => $out,
2816             'namespace' => $namespace,
2817             'soapaction' => $soapaction,
2818             'style' => $style);
2819         if($this->wsdl){
2820                 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation);
2821             }
2822                 return true;
2823         }
2824
2825         /**
2826         * create a fault. this also acts as a flag to the server that a fault has occured.
2827         *
2828         * @param        string faultcode
2829         * @param        string faultstring
2830         * @param        string faultactor
2831         * @param        string faultdetail
2832         * @access   public
2833         */
2834         function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
2835                 $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2836         }
2837
2838     /**
2839     * prints html description of services
2840     *
2841     * @access private
2842     */
2843     function webDescription(){
2844                 $b = '
2845                 <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
2846                 <style type="text/css">
2847                     body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2848                     p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2849                     pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2850                     ul      { margin-top: 10px; margin-left: 20px; }
2851                     li      { list-style-type: none; margin-top: 10px; color: #000000; }
2852                     .content{
2853                         margin-left: 0px; padding-bottom: 2em; }
2854                     .nav {
2855                         padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2856                         margin-top: 10px; margin-left: 0px; color: #000000;
2857                         background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2858                     .title {
2859                         font-family: arial; font-size: 26px; color: #ffffff;
2860                         background-color: #999999; width: 105%; margin-left: 0px;
2861                         padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2862                     .hidden {
2863                         position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2864                         font-family: arial; overflow: hidden; width: 600;
2865                         padding: 20px; font-size: 10px; background-color: #999999;
2866                         layer-background-color:#FFFFFF; }
2867                     a,a:active  { color: charcoal; font-weight: bold; }
2868                     a:visited   { color: #666666; font-weight: bold; }
2869                     a:hover     { color: cc3300; font-weight: bold; }
2870                 </style>
2871                 <script language="JavaScript" type="text/javascript">
2872                 <!--
2873                 // POP-UP CAPTIONS...
2874                 function lib_bwcheck(){ //Browsercheck (needed)
2875                     this.ver=navigator.appVersion
2876                     this.agent=navigator.userAgent
2877                     this.dom=document.getElementById?1:0
2878                     this.opera5=this.agent.indexOf("Opera 5")>-1
2879                     this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2880                     this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2881                     this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2882                     this.ie=this.ie4||this.ie5||this.ie6
2883                     this.mac=this.agent.indexOf("Mac")>-1
2884                     this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2885                     this.ns4=(document.layers && !this.dom)?1:0;
2886                     this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2887                     return this
2888                 }
2889                 var bw = new lib_bwcheck()
2890                 //Makes crossbrowser object.
2891                 function makeObj(obj){
2892                     this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2893                     if(!this.evnt) return false
2894                     this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2895                     this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2896                     this.writeIt=b_writeIt;
2897                     return this
2898                 }
2899                 // A unit of measure that will be added when setting the position of a layer.
2900                 //var px = bw.ns4||window.opera?"":"px";
2901                 function b_writeIt(text){
2902                     if (bw.ns4){this.wref.write(text);this.wref.close()}
2903                     else this.wref.innerHTML = text
2904                 }
2905                 //Shows the messages
2906                 var oDesc;
2907                 function popup(divid){
2908                     if(oDesc = new makeObj(divid)){
2909                         oDesc.css.visibility = "visible"
2910                     }
2911                 }
2912                 function popout(){ // Hides message
2913                     if(oDesc) oDesc.css.visibility = "hidden"
2914                 }
2915                 //-->
2916                 </script>
2917                 </head>
2918                 <body>
2919                 <div class=content>
2920                         <br><br>
2921                         <div class=title>'.$this->wsdl->serviceName.'</div>
2922                         <div class=nav>
2923                                 <p>View the <a href="'.(isset($GLOBALS['PHP_SELF']) ? $GLOBALS['PHP_SELF'] : $_SERVER['PHP_SELF']).'?wsdl">WSDL</a> for the service.
2924                                 Click on an operation name to view it&apos;s details.</p>
2925                                 <ul>';
2926                                 foreach($this->wsdl->getOperations() as $op => $data){
2927                                     $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2928                                     // create hidden div
2929                                     $b .= "<div id='$op' class='hidden'>
2930                                     <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2931                                     foreach($data as $donnie => $marie){ // loop through opdata
2932                                                 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
2933                                                     $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
2934                                                     foreach($marie as $captain => $tenille){ // loop through data
2935                                                                 if($captain == 'parts'){ // loop thru parts
2936                                                                     $b .= "&nbsp;&nbsp;$captain:<br>";
2937                                                         //if(is_array($tenille)){
2938                                                                         foreach($tenille as $joanie => $chachi){
2939                                                                                         $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
2940                                                                         }
2941                                                                 //}
2942                                                                 } else {
2943                                                                     $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
2944                                                                 }
2945                                                     }
2946                                                 } else {
2947                                                     $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
2948                                                 }
2949                                     }
2950                                         $b .= '</div>';
2951                                 }
2952                                 $b .= '
2953                                 <ul>
2954                         </div>
2955                 </div></body></html>';
2956                 return $b;
2957     }
2958
2959     /**
2960     * sets up wsdl object
2961     * this acts as a flag to enable internal WSDL generation
2962     *
2963     * @param string $serviceName, name of the service
2964     * @param string $namespace optional tns namespace
2965     * @param string $endpoint optional URL of service endpoint
2966     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
2967     * @param string $transport optional SOAP transport
2968     * @param string $schemaTargetNamespace optional targetNamespace for service schema
2969     */
2970     function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
2971     {
2972                 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2973                 $SERVER_PORT = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $GLOBALS['SERVER_PORT'];
2974                 if ($SERVER_PORT == 80) {
2975                         $SERVER_PORT = '';
2976                 } else {
2977                         $SERVER_PORT = ':' . $SERVER_PORT;
2978                 }
2979                 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2980         if(false == $namespace) {
2981             $namespace = "http://$SERVER_NAME/soap/$serviceName";
2982         }
2983         
2984         if(false == $endpoint) {
2985                 if (isset($_SERVER['HTTPS'])) {
2986                         $HTTPS = $_SERVER['HTTPS'];
2987                 } elseif (isset($GLOBALS['HTTPS'])) {
2988                         $HTTPS = $GLOBALS['HTTPS'];
2989                 } else {
2990                         $HTTPS = '0';
2991                 }
2992                 if ($HTTPS == '1' || $HTTPS == 'on') {
2993                         $SCHEME = 'https';
2994                 } else {
2995                         $SCHEME = 'http';
2996                 }
2997             $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
2998         }
2999         
3000         if(false == $schemaTargetNamespace) {
3001             $schemaTargetNamespace = $namespace;
3002         }
3003         
3004                 $this->wsdl = new wsdl;
3005                 $this->wsdl->serviceName = $serviceName;
3006         $this->wsdl->endpoint = $endpoint;
3007                 $this->wsdl->namespaces['tns'] = $namespace;
3008                 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
3009                 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
3010                 if ($schemaTargetNamespace != $namespace) {
3011                         $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
3012                 }
3013         $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
3014         $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
3015         $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
3016         $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
3017         $this->wsdl->bindings[$serviceName.'Binding'] = array(
3018                 'name'=>$serviceName.'Binding',
3019             'style'=>$style,
3020             'transport'=>$transport,
3021             'portType'=>$serviceName.'PortType');
3022         $this->wsdl->ports[$serviceName.'Port'] = array(
3023                 'binding'=>$serviceName.'Binding',
3024             'location'=>$endpoint,
3025             'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
3026     }
3027 }
3028
3029
3030
3031 ?><?php
3032
3033
3034
3035 /**
3036 * parses a WSDL file, allows access to it's data, other utility methods
3037
3038 * @author   Dietrich Ayala <dietrich@ganx4.com>
3039 * @version  $Id$
3040 * @access public 
3041 */
3042 class wsdl extends nusoap_base {
3043         // URL or filename of the root of this WSDL
3044     var $wsdl; 
3045     // define internal arrays of bindings, ports, operations, messages, etc.
3046     var $schemas = array();
3047     var $currentSchema;
3048     var $message = array();
3049     var $complexTypes = array();
3050     var $messages = array();
3051     var $currentMessage;
3052     var $currentOperation;
3053     var $portTypes = array();
3054     var $currentPortType;
3055     var $bindings = array();
3056     var $currentBinding;
3057     var $ports = array();
3058     var $currentPort;
3059     var $opData = array();
3060     var $status = '';
3061     var $documentation = false;
3062     var $endpoint = ''; 
3063     // array of wsdl docs to import
3064     var $import = array(); 
3065     // parser vars
3066     var $parser;
3067     var $position = 0;
3068     var $depth = 0;
3069     var $depth_array = array();
3070         // for getting wsdl
3071         var $proxyhost = '';
3072     var $proxyport = '';
3073         var $proxyusername = '';
3074         var $proxypassword = '';
3075         var $timeout = 0;
3076         var $response_timeout = 30;
3077
3078     /**
3079      * constructor
3080      * 
3081      * @param string $wsdl WSDL document URL
3082          * @param string $proxyhost
3083          * @param string $proxyport
3084          * @param string $proxyusername
3085          * @param string $proxypassword
3086          * @param integer $timeout set the connection timeout
3087          * @param integer $response_timeout set the response timeout
3088      * @access public 
3089      */
3090     function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
3091         $this->wsdl = $wsdl;
3092         $this->proxyhost = $proxyhost;
3093         $this->proxyport = $proxyport;
3094                 $this->proxyusername = $proxyusername;
3095                 $this->proxypassword = $proxypassword;
3096                 $this->timeout = $timeout;
3097                 $this->response_timeout = $response_timeout;
3098         
3099         // parse wsdl file
3100         if ($wsdl != "") {
3101             $this->debug('initial wsdl URL: ' . $wsdl);
3102             $this->parseWSDL($wsdl);
3103         }
3104         // imports
3105         // TODO: handle imports more properly, grabbing them in-line and nesting them
3106                 $imported_urls = array();
3107                 $imported = 1;
3108                 while ($imported > 0) {
3109                         $imported = 0;
3110                         // Schema imports
3111                         foreach ($this->schemas as $ns => $list) {
3112                                 foreach ($list as $xs) {
3113                                                 $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
3114                                     foreach ($xs->imports as $ns2 => $list2) {
3115                                         for ($ii = 0; $ii < count($list2); $ii++) {
3116                                                 if (! $list2[$ii]['loaded']) {
3117                                                         $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
3118                                                         $url = $list2[$ii]['location'];
3119                                                                         if ($url != '') {
3120                                                                                 $urlparts = parse_url($url);
3121                                                                                 if (!isset($urlparts['host'])) {
3122                                                                                         $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . 
3123                                                                                                         substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
3124                                                                                 }
3125                                                                                 if (! in_array($url, $imported_urls)) {
3126                                                                         $this->parseWSDL($url);
3127                                                                         $imported++;
3128                                                                         $imported_urls[] = $url;
3129                                                                 }
3130                                                                         } else {
3131                                                                                 $this->debug("Unexpected scenario: empty URL for unloaded import");
3132                                                                         }
3133                                                                 }
3134                                                         }
3135                                     } 
3136                                 }
3137                         }
3138                         // WSDL imports
3139                                 $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
3140                     foreach ($this->import as $ns => $list) {
3141                         for ($ii = 0; $ii < count($list); $ii++) {
3142                                 if (! $list[$ii]['loaded']) {
3143                                         $this->import[$ns][$ii]['loaded'] = true;
3144                                         $url = $list[$ii]['location'];
3145                                                         if ($url != '') {
3146                                                                 $urlparts = parse_url($url);
3147                                                                 if (!isset($urlparts['host'])) {
3148                                                                         $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . 
3149                                                                                         substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
3150                                                                 }
3151                                                                 if (! in_array($url, $imported_urls)) {
3152                                                         $this->parseWSDL($url);
3153                                                         $imported++;
3154                                                         $imported_urls[] = $url;
3155                                                 }
3156                                                         } else {
3157                                                                 $this->debug("Unexpected scenario: empty URL for unloaded import");
3158                                                         }
3159                                                 }
3160                                         }
3161                     } 
3162                         }
3163         // add new data to operation data
3164         foreach($this->bindings as $binding => $bindingData) {
3165             if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
3166                 foreach($bindingData['operations'] as $operation => $data) {
3167                     $this->debug('post-parse data gathering for ' . $operation);
3168                     $this->bindings[$binding]['operations'][$operation]['input'] = 
3169                                                 isset($this->bindings[$binding]['operations'][$operation]['input']) ? 
3170                                                 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
3171                                                 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
3172                     $this->bindings[$binding]['operations'][$operation]['output'] = 
3173                                                 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
3174                                                 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
3175                                                 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
3176                     if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
3177                                                 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
3178                                         }
3179                                         if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
3180                                 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
3181                     }
3182                                         if (isset($bindingData['style'])) {
3183                         $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
3184                     }
3185                     $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
3186                     $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
3187                     $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
3188                 } 
3189             } 
3190         }
3191     }
3192
3193     /**
3194      * parses the wsdl document
3195      * 
3196      * @param string $wsdl path or URL
3197      * @access private 
3198      */
3199     function parseWSDL($wsdl = '')
3200     {
3201         if ($wsdl == '') {
3202             $this->debug('no wsdl passed to parseWSDL()!!');
3203             $this->setError('no wsdl passed to parseWSDL()!!');
3204             return false;
3205         }
3206         
3207         // parse $wsdl for url format
3208         $wsdl_props = parse_url($wsdl);
3209
3210         if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
3211             $this->debug('getting WSDL http(s) URL ' . $wsdl);
3212                 // get wsdl
3213                 $tr = new soap_transport_http($wsdl);
3214                         $tr->request_method = 'GET';
3215                         $tr->useSOAPAction = false;
3216                         if($this->proxyhost && $this->proxyport){
3217                                 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
3218                         }
3219                         if (isset($wsdl_props['user'])) {
3220                 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
3221             }
3222                         $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
3223                         //$this->debug("WSDL request\n" . $tr->outgoing_payload);
3224                         //$this->debug("WSDL response\n" . $tr->incoming_payload);
3225                         $this->debug("transport debug data...\n" . $tr->debug_str);
3226                         // catch errors
3227                         if($err = $tr->getError() ){
3228                                 $errstr = 'HTTP ERROR: '.$err;
3229                                 $this->debug($errstr);
3230                     $this->setError($errstr);
3231                                 unset($tr);
3232                     return false;
3233                         }
3234                         unset($tr);
3235         } else {
3236             // $wsdl is not http(s), so treat it as a file URL or plain file path
3237                 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
3238                         $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
3239                 } else {
3240                         $path = $wsdl;
3241                 }
3242             $this->debug('getting WSDL file ' . $path);
3243             if ($fp = @fopen($path, 'r')) {
3244                 $wsdl_string = '';
3245                 while ($data = fread($fp, 32768)) {
3246                     $wsdl_string .= $data;
3247                 } 
3248                 fclose($fp);
3249             } else {
3250                 $errstr = "Bad path to WSDL file $path";
3251                 $this->debug($errstr);
3252                 $this->setError($errstr);
3253                 return false;
3254             } 
3255         }
3256         // end new code added
3257         // Create an XML parser.
3258         $this->parser = xml_parser_create(); 
3259         // Set the options for parsing the XML data.
3260         // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3261         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
3262         // Set the object for the parser.
3263         xml_set_object($this->parser, $this); 
3264         // Set the element handlers for the parser.
3265         xml_set_element_handler($this->parser, 'start_element', 'end_element');
3266         xml_set_character_data_handler($this->parser, 'character_data');
3267         // Parse the XML file.
3268         if (!xml_parse($this->parser, $wsdl_string, true)) {
3269             // Display an error message.
3270             $errstr = sprintf(
3271                                 'XML error parsing WSDL from %s on line %d: %s',
3272                                 $wsdl,
3273                 xml_get_current_line_number($this->parser),
3274                 xml_error_string(xml_get_error_code($this->parser))
3275                 );
3276             $this->debug($errstr);
3277                         $this->debug("XML payload:\n" . $wsdl_string);
3278             $this->setError($errstr);
3279             return false;
3280         } 
3281                 // free the parser
3282         xml_parser_free($this->parser);
3283                 // catch wsdl parse errors
3284                 if($this->getError()){
3285                         return false;
3286                 }
3287         return true;
3288     } 
3289
3290     /**
3291      * start-element handler
3292      * 
3293      * @param string $parser XML parser object
3294      * @param string $name element name
3295      * @param string $attrs associative array of attributes
3296      * @access private 
3297      */
3298     function start_element($parser, $name, $attrs)
3299     {
3300         if ($this->status == 'schema') {
3301             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
3302             $this->debug_str .= $this->currentSchema->debug_str;
3303             $this->currentSchema->debug_str = '';
3304         } elseif (ereg('schema$', $name)) {
3305             // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
3306             $this->status = 'schema';
3307             $this->currentSchema = new xmlschema('', '', $this->namespaces);
3308             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
3309             $this->debug_str .= $this->currentSchema->debug_str;
3310             $this->currentSchema->debug_str = '';
3311         } else {
3312             // position in the total number of elements, starting from 0
3313             $pos = $this->position++;
3314             $depth = $this->depth++; 
3315             // set self as current value for this depth
3316             $this->depth_array[$depth] = $pos;
3317             $this->message[$pos] = array('cdata' => ''); 
3318             // get element prefix
3319             if (ereg(':', $name)) {
3320                 // get ns prefix
3321                 $prefix = substr($name, 0, strpos($name, ':')); 
3322                 // get ns
3323                 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; 
3324                 // get unqualified name
3325                 $name = substr(strstr($name, ':'), 1);
3326             } 
3327
3328             if (count($attrs) > 0) {
3329                 foreach($attrs as $k => $v) {
3330                     // if ns declarations, add to class level array of valid namespaces
3331                     if (ereg("^xmlns", $k)) {
3332                         if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
3333                             $this->namespaces[$ns_prefix] = $v;
3334                         } else {
3335                             $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
3336                         } 
3337                         if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
3338                             $this->XMLSchemaVersion = $v;
3339                             $this->namespaces['xsi'] = $v . '-instance';
3340                         } 
3341                     } //  
3342                     // expand each attribute
3343                     $k = strpos($k, ':') ? $this->expandQname($k) : $k;
3344                     if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
3345                         $v = strpos($v, ':') ? $this->expandQname($v) : $v;
3346                     } 
3347                     $eAttrs[$k] = $v;
3348                 } 
3349                 $attrs = $eAttrs;
3350             } else {
3351                 $attrs = array();
3352             } 
3353             // find status, register data
3354             switch ($this->status) {
3355                 case 'message':
3356                     if ($name == 'part') {
3357                         if (isset($attrs['type'])) {
3358                                     $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
3359                                     $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
3360                                 } 
3361                                     if (isset($attrs['element'])) {
3362                                         $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
3363                                     } 
3364                                 } 
3365                                 break;
3366                             case 'portType':
3367                                 switch ($name) {
3368                                     case 'operation':
3369                                         $this->currentPortOperation = $attrs['name'];
3370                                         $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
3371                                         if (isset($attrs['parameterOrder'])) {
3372                                                 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
3373                                                 } 
3374                                                 break;
3375                                             case 'documentation':
3376                                                 $this->documentation = true;
3377                                                 break; 
3378                                             // merge input/output data
3379                                             default:
3380                                                 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
3381                                                 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
3382                                                 break;
3383                                         } 
3384                                 break;
3385                                 case 'binding':
3386                                     switch ($name) {
3387                                         case 'binding': 
3388                                             // get ns prefix
3389                                             if (isset($attrs['style'])) {
3390                                             $this->bindings[$this->currentBinding]['prefix'] = $prefix;
3391                                                 } 
3392                                                 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
3393                                                 break;
3394                                                 case 'header':
3395                                                     $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
3396                                                     break;
3397                                                 case 'operation':
3398                                                     if (isset($attrs['soapAction'])) {
3399                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
3400                                                     } 
3401                                                     if (isset($attrs['style'])) {
3402                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
3403                                                     } 
3404                                                     if (isset($attrs['name'])) {
3405                                                         $this->currentOperation = $attrs['name'];
3406                                                         $this->debug("current binding operation: $this->currentOperation");
3407                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
3408                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
3409                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
3410                                                     } 
3411                                                     break;
3412                                                 case 'input':
3413                                                     $this->opStatus = 'input';
3414                                                     break;
3415                                                 case 'output':
3416                                                     $this->opStatus = 'output';
3417                                                     break;
3418                                                 case 'body':
3419                                                     if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
3420                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
3421                                                     } else {
3422                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
3423                                                     } 
3424                                                     break;
3425                                         } 
3426                                         break;
3427                                 case 'service':
3428                                         switch ($name) {
3429                                             case 'port':
3430                                                 $this->currentPort = $attrs['name'];
3431                                                 $this->debug('current port: ' . $this->currentPort);
3432                                                 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
3433                                         
3434                                                 break;
3435                                             case 'address':
3436                                                 $this->ports[$this->currentPort]['location'] = $attrs['location'];
3437                                                 $this->ports[$this->currentPort]['bindingType'] = $namespace;
3438                                                 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
3439                                                 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
3440                                                 break;
3441                                         } 
3442                                         break;
3443                         } 
3444                 // set status
3445                 switch ($name) {
3446                         case 'import':
3447                             if (isset($attrs['location'])) {
3448                     $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
3449                     $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
3450                                 } else {
3451                     $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
3452                                         if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
3453                                                 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
3454                                         }
3455                     $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
3456                                 }
3457                                 break;
3458                         //wait for schema
3459                         //case 'types':
3460                         //      $this->status = 'schema';
3461                         //      break;
3462                         case 'message':
3463                                 $this->status = 'message';
3464                                 $this->messages[$attrs['name']] = array();
3465                                 $this->currentMessage = $attrs['name'];
3466                                 break;
3467                         case 'portType':
3468                                 $this->status = 'portType';
3469                                 $this->portTypes[$attrs['name']] = array();
3470                                 $this->currentPortType = $attrs['name'];
3471                                 break;
3472                         case "binding":
3473                                 if (isset($attrs['name'])) {
3474                                 // get binding name
3475                                         if (strpos($attrs['name'], ':')) {
3476                                         $this->currentBinding = $this->getLocalPart($attrs['name']);
3477                                         } else {
3478                                         $this->currentBinding = $attrs['name'];
3479                                         } 
3480                                         $this->status = 'binding';
3481                                         $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
3482                                         $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
3483                                 } 
3484                                 break;
3485                         case 'service':
3486                                 $this->serviceName = $attrs['name'];
3487                                 $this->status = 'service';
3488                                 $this->debug('current service: ' . $this->serviceName);
3489                                 break;
3490                         case 'definitions':
3491                                 foreach ($attrs as $name => $value) {
3492                                         $this->wsdl_info[$name] = $value;
3493                                 } 
3494                                 break;
3495                         } 
3496                 } 
3497         } 
3498
3499         /**
3500         * end-element handler
3501         * 
3502         * @param string $parser XML parser object
3503         * @param string $name element name
3504         * @access private 
3505         */
3506         function end_element($parser, $name){ 
3507                 // unset schema status
3508                 if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
3509                         $this->status = "";
3510                         $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
3511                 } 
3512                 if ($this->status == 'schema') {
3513                         $this->currentSchema->schemaEndElement($parser, $name);
3514                 } else {
3515                         // bring depth down a notch
3516                         $this->depth--;
3517                 } 
3518                 // end documentation
3519                 if ($this->documentation) {
3520                         //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
3521                         //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
3522                         $this->documentation = false;
3523                 } 
3524         } 
3525
3526         /**
3527          * element content handler
3528          * 
3529          * @param string $parser XML parser object
3530          * @param string $data element content
3531          * @access private 
3532          */
3533         function character_data($parser, $data)
3534         {
3535                 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
3536                 if (isset($this->message[$pos]['cdata'])) {
3537                         $this->message[$pos]['cdata'] .= $data;
3538                 } 
3539                 if ($this->documentation) {
3540                         $this->documentation .= $data;
3541                 } 
3542         } 
3543         
3544         function getBindingData($binding)
3545         {
3546                 if (is_array($this->bindings[$binding])) {
3547                         return $this->bindings[$binding];
3548                 } 
3549         }
3550         
3551         /**
3552          * returns an assoc array of operation names => operation data
3553          * 
3554          * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
3555          * @return array 
3556          * @access public 
3557          */
3558         function getOperations($bindingType = 'soap')
3559         {
3560                 $ops = array();
3561                 if ($bindingType == 'soap') {
3562                         $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
3563                 }
3564                 // loop thru ports
3565                 foreach($this->ports as $port => $portData) {
3566                         // binding type of port matches parameter
3567                         if ($portData['bindingType'] == $bindingType) {
3568                                 //$this->debug("getOperations for port $port");
3569                                 //$this->debug("port data: " . $this->varDump($portData));
3570                                 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
3571                                 // merge bindings
3572                                 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
3573                                         $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
3574                                 }
3575                         }
3576                 } 
3577                 return $ops;
3578         } 
3579         
3580         /**
3581          * returns an associative array of data necessary for calling an operation
3582          * 
3583          * @param string $operation , name of operation
3584          * @param string $bindingType , type of binding eg: soap
3585          * @return array 
3586          * @access public 
3587          */
3588         function getOperationData($operation, $bindingType = 'soap')
3589         {
3590                 if ($bindingType == 'soap') {
3591                         $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
3592                 }
3593                 // loop thru ports
3594                 foreach($this->ports as $port => $portData) {
3595                         // binding type of port matches parameter
3596                         if ($portData['bindingType'] == $bindingType) {
3597                                 // get binding
3598                                 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
3599                                 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
3600                                         if ($operation == $bOperation) {
3601                                                 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
3602                                             return $opData;
3603                                         } 
3604                                 } 
3605                         }
3606                 } 
3607         }
3608         
3609         /**
3610     * returns an array of information about a given type
3611     * returns false if no type exists by the given name
3612     *
3613         *        typeDef = array(
3614         *        'elements' => array(), // refs to elements array
3615         *       'restrictionBase' => '',
3616         *       'phpType' => '',
3617         *       'order' => '(sequence|all)',
3618         *       'attrs' => array() // refs to attributes array
3619         *       )
3620     *
3621     * @param $type string
3622     * @param $ns string
3623     * @return mixed
3624     * @access public
3625     * @see xmlschema
3626     */
3627         function getTypeDef($type, $ns) {
3628                 if ((! $ns) && isset($this->namespaces['tns'])) {
3629                         $ns = $this->namespaces['tns'];
3630                 }
3631                 if (isset($this->schemas[$ns])) {
3632                         foreach ($this->schemas[$ns] as $xs) {
3633                                 $t = $xs->getTypeDef($type);
3634                                 $this->debug_str .= $xs->debug_str;
3635                                 $xs->debug_str = '';
3636                                 if ($t) {
3637                                         return $t;
3638                                 }
3639                         }
3640                 }
3641                 return false;
3642         }
3643
3644         /**
3645         * serialize the parsed wsdl
3646         * 
3647         * @return string , serialization of WSDL
3648         * @access public 
3649         */
3650         function serialize()
3651         {
3652                 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?><definitions';
3653                 foreach($this->namespaces as $k => $v) {
3654                         $xml .= " xmlns:$k=\"$v\"";
3655                 } 
3656                 // 10.9.02 - add poulter fix for wsdl and tns declarations
3657                 if (isset($this->namespaces['wsdl'])) {
3658                         $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
3659                 } 
3660                 if (isset($this->namespaces['tns'])) {
3661                         $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
3662                 } 
3663                 $xml .= '>'; 
3664                 // imports
3665                 if (sizeof($this->import) > 0) {
3666                         foreach($this->import as $ns => $list) {
3667                                 foreach ($list as $ii) {
3668                                         if ($ii['location'] != '') {
3669                                                 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
3670                                         } else {
3671                                                 $xml .= '<import namespace="' . $ns . '" />';
3672                                         }
3673                                 }
3674                         } 
3675                 } 
3676                 // types
3677                 if (count($this->schemas)>=1) {
3678                         $xml .= '<types>';
3679                         foreach ($this->schemas as $ns => $list) {
3680                                 foreach ($list as $xs) {
3681                                         $xml .= $xs->serializeSchema();
3682                                 }
3683                         }
3684                         $xml .= '</types>';
3685                 } 
3686                 // messages
3687                 if (count($this->messages) >= 1) {
3688                         foreach($this->messages as $msgName => $msgParts) {
3689                                 $xml .= '<message name="' . $msgName . '">';
3690                                 if(is_array($msgParts)){
3691                                         foreach($msgParts as $partName => $partType) {
3692                                                 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
3693                                                 if (strpos($partType, ':')) {
3694                                                     $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
3695                                                 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
3696                                                     // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
3697                                                     $typePrefix = 'xsd';
3698                                                 } else {
3699                                                     foreach($this->typemap as $ns => $types) {
3700                                                         if (isset($types[$partType])) {
3701                                                             $typePrefix = $this->getPrefixFromNamespace($ns);
3702                                                         } 
3703                                                     } 
3704                                                     if (!isset($typePrefix)) {
3705                                                         die("$partType has no namespace!");
3706                                                     } 
3707                                                 } 
3708                                                 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
3709                                         }
3710                                 }
3711                                 $xml .= '</message>';
3712                         } 
3713                 } 
3714                 // bindings & porttypes
3715                 if (count($this->bindings) >= 1) {
3716                         $binding_xml = '';
3717                         $portType_xml = '';
3718                         foreach($this->bindings as $bindingName => $attrs) {
3719                                 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
3720                                 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
3721                                 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
3722                                 foreach($attrs['operations'] as $opName => $opParts) {
3723                                         $binding_xml .= '<operation name="' . $opName . '">';
3724                                         $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
3725                                         if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
3726                                                 $enc_style = '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
3727                                         } else {
3728                                                 $enc_style = '';
3729                                         }
3730                                         $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . $enc_style . '/></input>';
3731                                         if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
3732                                                 $enc_style = '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
3733                                         } else {
3734                                                 $enc_style = '';
3735                                         }
3736                                         $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . $enc_style . '/></output>';
3737                                         $binding_xml .= '</operation>';
3738                                         $portType_xml .= '<operation name="' . $opParts['name'] . '"';
3739                                         if (isset($opParts['parameterOrder'])) {
3740                                             $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
3741                                         } 
3742                                         $portType_xml .= '>';
3743                                         if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
3744                                                 $portType_xml .= '<documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
3745                                         }
3746                                         $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
3747                                         $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
3748                                         $portType_xml .= '</operation>';
3749                                 } 
3750                                 $portType_xml .= '</portType>';
3751                                 $binding_xml .= '</binding>';
3752                         } 
3753                         $xml .= $portType_xml . $binding_xml;
3754                 } 
3755                 // services
3756                 $xml .= '<service name="' . $this->serviceName . '">';
3757                 if (count($this->ports) >= 1) {
3758                         foreach($this->ports as $pName => $attrs) {
3759                                 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
3760                                 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
3761                                 $xml .= '</port>';
3762                         } 
3763                 } 
3764                 $xml .= '</service>';
3765                 return $xml . '</definitions>';
3766         } 
3767         
3768         /**
3769          * serialize a PHP value according to a WSDL message definition
3770          * 
3771          * TODO
3772          * - multi-ref serialization
3773          * - validate PHP values against type definitions, return errors if invalid
3774          * 
3775          * @param string $ type name
3776          * @param mixed $ param value
3777          * @return mixed new param or false if initial value didn't validate
3778          */
3779         function serializeRPCParameters($operation, $direction, $parameters)
3780         {
3781                 $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); 
3782                 
3783                 if ($direction != 'input' && $direction != 'output') {
3784                         $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
3785                         $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
3786                         return false;
3787                 } 
3788                 if (!$opData = $this->getOperationData($operation)) {
3789                         $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
3790                         $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
3791                         return false;
3792                 }
3793                 $this->debug($this->varDump($opData));
3794
3795                 // Get encoding style for output and set to current
3796                 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3797                 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
3798                         $encodingStyle = $opData['output']['encodingStyle'];
3799                         $enc_style = $encodingStyle;
3800                 }
3801
3802                 // set input params
3803                 $xml = '';
3804                 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3805                         
3806                         $use = $opData[$direction]['use'];
3807                         $this->debug("use=$use");
3808                         $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3809                         if (is_array($parameters)) {
3810                                 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
3811                                 $this->debug('have ' . $parametersArrayType . ' parameters');
3812                                 foreach($opData[$direction]['parts'] as $name => $type) {
3813                                         $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
3814                                         // Track encoding style
3815                                         if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
3816                                                 $encodingStyle = $opData[$direction]['encodingStyle'];                  
3817                                                 $enc_style = $encodingStyle;
3818                                         } else {
3819                                                 $enc_style = false;
3820                                         }
3821                                         // NOTE: add error handling here
3822                                         // if serializeType returns false, then catch global error and fault
3823                                         if ($parametersArrayType == 'arraySimple') {
3824                                                 $p = array_shift($parameters);
3825                                                 $this->debug('calling serializeType w/indexed param');
3826                                                 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
3827                                         } elseif (isset($parameters[$name])) {
3828                                                 $this->debug('calling serializeType w/named param');
3829                                                 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
3830                                         } else {
3831                                                 // TODO: only send nillable
3832                                                 $this->debug('calling serializeType w/null param');
3833                                                 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
3834                                         }
3835                                 }
3836                         } else {
3837                                 $this->debug('no parameters passed.');
3838                         }
3839                 }
3840                 return $xml;
3841         } 
3842         
3843         /**
3844          * serialize a PHP value according to a WSDL message definition
3845          * 
3846          * TODO
3847          * - multi-ref serialization
3848          * - validate PHP values against type definitions, return errors if invalid
3849          * 
3850          * @param string $ type name
3851          * @param mixed $ param value
3852          * @return mixed new param or false if initial value didn't validate
3853          */
3854         function serializeParameters($operation, $direction, $parameters)
3855         {
3856                 $this->debug('in serializeParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); 
3857                 
3858                 if ($direction != 'input' && $direction != 'output') {
3859                         $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
3860                         $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
3861                         return false;
3862                 } 
3863                 if (!$opData = $this->getOperationData($operation)) {
3864                         $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
3865                         $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
3866                         return false;
3867                 }
3868                 $this->debug($this->varDump($opData));
3869                 
3870                 // Get encoding style for output and set to current
3871                 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3872                 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
3873                         $encodingStyle = $opData['output']['encodingStyle'];
3874                         $enc_style = $encodingStyle;
3875                 }
3876                 
3877                 // set input params
3878                 $xml = '';
3879                 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3880                         
3881                         $use = $opData[$direction]['use'];
3882                         $this->debug("use=$use");
3883                         $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3884                         if (is_array($parameters)) {
3885                                 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
3886                                 $this->debug('have ' . $parametersArrayType . ' parameters');
3887                                 foreach($opData[$direction]['parts'] as $name => $type) {
3888                                         $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
3889                                         // Track encoding style
3890                                         if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
3891                                                 $encodingStyle = $opData[$direction]['encodingStyle'];                  
3892                                                 $enc_style = $encodingStyle;
3893                                         } else {
3894                                                 $enc_style = false;
3895                                         }
3896                                         // NOTE: add error handling here
3897                                         // if serializeType returns false, then catch global error and fault
3898                                         if ($parametersArrayType == 'arraySimple') {
3899                                                 $p = array_shift($parameters);
3900                                                 $this->debug('calling serializeType w/indexed param');
3901                                                 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
3902                                         } elseif (isset($parameters[$name])) {
3903                                                 $this->debug('calling serializeType w/named param');
3904                                                 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
3905                                         } else {
3906                                                 // TODO: only send nillable
3907                                                 $this->debug('calling serializeType w/null param');
3908                                                 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
3909                                         }
3910                                 }
3911                         } else {
3912                                 $this->debug('no parameters passed.');
3913                         }
3914                 }
3915                 return $xml;
3916         } 
3917         
3918         /**
3919          * serializes a PHP value according a given type definition
3920          * 
3921          * @param string $name , name of type (part)
3922          * @param string $type , type of type, heh (type or element)
3923          * @param mixed $value , a native PHP value (parameter value)
3924          * @param string $use , use for part (encoded|literal)
3925          * @param string $encodingStyle , use to add encoding changes to serialisation
3926          * @return string serialization
3927          * @access public 
3928          */
3929         function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false)
3930         {
3931                 $this->debug("in serializeType: $name, $type, $value, $use, $encodingStyle");
3932                 if($use == 'encoded' && $encodingStyle) {
3933                         $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
3934                 }
3935
3936                 // if a soap_val has been supplied, let its type override the WSDL
3937         if (is_object($value) && get_class($value) == 'soapval') {
3938                 // TODO: get attributes from soapval?
3939                 if ($value->type_ns) {
3940                         $type = $value->type_ns . ':' . $value->type;
3941                 } else {
3942                         $type = $value->type;
3943                 }
3944                 $value = $value->value;
3945                 $forceType = true;
3946                 $this->debug("in serializeType: soapval overrides type to $type, value to $value");
3947         } else {
3948                 $forceType = false;
3949         }
3950
3951                 $xml = '';
3952                 if (strpos($type, ':')) {
3953                         $uqType = substr($type, strrpos($type, ':') + 1);
3954                         $ns = substr($type, 0, strrpos($type, ':'));
3955                         $this->debug("got a prefixed type: $uqType, $ns");
3956                         if ($this->getNamespaceFromPrefix($ns)) {
3957                                 $ns = $this->getNamespaceFromPrefix($ns);
3958                                 $this->debug("expanded prefixed type: $uqType, $ns");
3959                         }
3960
3961                         if($ns == $this->XMLSchemaVersion){
3962                                 
3963                                 if (is_null($value)) {
3964                                         if ($use == 'literal') {
3965                                                 // TODO: depends on nillable
3966                                                 return "<$name/>";
3967                                         } else {
3968                                                 return "<$name xsi:nil=\"true\"/>";
3969                                         }
3970                                 }
3971                         if ($uqType == 'boolean' && !$value) {
3972                                         $value = 'false';
3973                                 } elseif ($uqType == 'boolean') {
3974                                         $value = 'true';
3975                                 } 
3976                                 if ($uqType == 'string' && gettype($value) == 'string') {
3977                                         $value = $this->expandEntities($value);
3978                                 } 
3979                                 // it's a scalar
3980                                 // TODO: what about null/nil values?
3981                                 // check type isn't a custom type extending xmlschema namespace
3982                                 if (!$this->getTypeDef($uqType, $ns)) {
3983                                         if ($use == 'literal') {
3984                                                 if ($forceType) {
3985                                                         return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3986                                                 } else {
3987                                                         return "<$name>$value</$name>";
3988                                                 }
3989                                         } else {
3990                                                 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\"$encodingStyle>$value</$name>";
3991                                         }
3992                                 }
3993                         } else if ($ns == 'http://xml.apache.org/xml-soap') {
3994                                 if ($uqType == 'Map') {
3995                                         $contents = '';
3996                                         foreach($value as $k => $v) {
3997                                                 $this->debug("serializing map element: key $k, value $v");
3998                                                 $contents .= '<item>';
3999                                                 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
4000                                                 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
4001                                                 $contents .= '</item>';
4002                                         }
4003                                         if ($use == 'literal') {
4004                                                 if ($forceType) {
4005                                                 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\">$contents</$name>";
4006                                                 } else {
4007                                                         return "<$name>$contents</$name>";
4008                                                 }
4009                                         } else {
4010                                                 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\"$encodingStyle>$contents</$name>";
4011                                         }
4012                                 }
4013                         }
4014                 } else {
4015                         $this->debug("No namespace for type $type");
4016                         $ns = '';
4017                         $uqType = $type;
4018                 }
4019                 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
4020                         $this->setError("$type ($uqType) is not a supported type.");
4021                         $this->debug("$type ($uqType) is not a supported type.");
4022                         return false;
4023                 } else {
4024                         foreach($typeDef as $k => $v) {
4025                                 $this->debug("typedef, $k: $v");
4026                         }
4027                 }
4028                 $phpType = $typeDef['phpType'];
4029                 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); 
4030                 // if php type == struct, map value to the <all> element names
4031                 if ($phpType == 'struct') {
4032                         if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
4033                                 $elementName = $uqType;
4034                                 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4035                                         $elementNS = " xmlns=\"$ns\"";
4036                                 }
4037                         } else {
4038                                 $elementName = $name;
4039                                 $elementNS = '';
4040                         }
4041                         if (is_null($value)) {
4042                                 if ($use == 'literal') {
4043                                         // TODO: depends on nillable
4044                                         return "<$elementName$elementNS/>";
4045                                 } else {
4046                                         return "<$elementName$elementNS xsi:nil=\"true\"/>";
4047                                 }
4048                         }
4049                         if ($use == 'literal') {
4050                                 if ($forceType) {
4051                                         $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
4052                                 } else {
4053                                         $xml = "<$elementName$elementNS>";
4054                                 }
4055                         } else {
4056                                 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
4057                         }
4058                         
4059                         if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
4060                         
4061                                 // toggle whether all elements are present - ideally should validate against schema
4062                                 if(count($typeDef['elements']) != count($value)){
4063                                         $optionals = true;
4064                                 }
4065                                 foreach($typeDef['elements'] as $eName => $attrs) {
4066                                         // if user took advantage of a minOccurs=0, then only serialize named parameters
4067                                         if(isset($optionals) && !isset($value[$eName])){
4068                                                 // do nothing
4069                                         } else {
4070                                                 // TODO: if maxOccurs > 1, then allow serialization of an array
4071                                                 // get value
4072                                                 if (isset($value[$eName])) {
4073                                                     $v = $value[$eName];
4074                                                 } else {
4075                                                     $v = null;
4076                                                 }
4077                                                 if (isset($attrs['maxOccurs']) && $attrs['maxOccurs'] == 'unbounded' && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
4078                                                         $vv = $v;
4079                                                         foreach ($vv as $k => $v) {
4080                                                                 if (isset($attrs['type'])) {
4081                                                                         // serialize schema-defined type
4082                                                                     $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle);
4083                                                                 } else {
4084                                                                         // serialize generic type
4085                                                                     $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
4086                                                                     $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
4087                                                                 }
4088                                                         }
4089                                                 } else {
4090                                                         if (isset($attrs['type'])) {
4091                                                                 // serialize schema-defined type
4092                                                             $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle);
4093                                                         } else {
4094                                                                 // serialize generic type
4095                                                             $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
4096                                                             $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
4097                                                         }
4098                                                 }
4099                                         }
4100                                 } 
4101                         } else {
4102                                 //echo 'got here';
4103                         }
4104                         $xml .= "</$elementName>";
4105                 } elseif ($phpType == 'array') {
4106                         if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4107                                 $elementNS = " xmlns=\"$ns\"";
4108                         } else {
4109                                 $elementNS = '';
4110                         }
4111                         if (is_null($value)) {
4112                                 if ($use == 'literal') {
4113                                         // TODO: depends on nillable
4114                                         return "<$name$elementNS/>";
4115                                 } else {
4116                                         return "<$name$elementNS xsi:nil=\"true\"/>";
4117                                 }
4118                         }
4119                         if (isset($typeDef['multidimensional'])) {
4120                                 $nv = array();
4121                                 foreach($value as $v) {
4122                                         $cols = ',' . sizeof($v);
4123                                         $nv = array_merge($nv, $v);
4124                                 } 
4125                                 $value = $nv;
4126                         } else {
4127                                 $cols = '';
4128                         } 
4129                         if (is_array($value) && sizeof($value) >= 1) {
4130                                 $rows = sizeof($value);
4131                                 $contents = '';
4132                                 foreach($value as $k => $v) {
4133                                         $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
4134                                         //if (strpos($typeDef['arrayType'], ':') ) {
4135                                         if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
4136                                             $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
4137                                         } else {
4138                                             $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
4139                                         } 
4140                                 }
4141                                 $this->debug('contents: '.$this->varDump($contents));
4142                         } else {
4143                                 $rows = 0;
4144                                 $contents = null;
4145                         }
4146                         // TODO: for now, an empty value will be serialized as a zero element
4147                         // array.  Revisit this when coding the handling of null/nil values.
4148                         if ($use == 'literal') {
4149                                 $xml = "<$name$elementNS>"
4150                                         .$contents
4151                                         ."</$name>";
4152                         } else {
4153                                 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
4154                                         $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
4155                                         .':arrayType="'
4156                                         .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
4157                                         .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
4158                                         .$contents
4159                                         ."</$name>";
4160                         }
4161                 } elseif ($phpType == 'scalar') {
4162                         if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4163                                 $elementNS = " xmlns=\"$ns\"";
4164                         } else {
4165                                 $elementNS = '';
4166                         }
4167                         if ($use == 'literal') {
4168                                 if ($forceType) {
4169                                         return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
4170                                 } else {
4171                                         return "<$name$elementNS>$value</$name>";
4172                                 }
4173                         } else {
4174                                 return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
4175                         }
4176                 }
4177                 $this->debug('returning: '.$this->varDump($xml));
4178                 return $xml;
4179         }
4180         
4181         /**
4182         * adds an XML Schema complex type to the WSDL types
4183         *
4184         * @param name
4185         * @param typeClass (complexType|simpleType|attribute)
4186         * @param phpType: currently supported are array and struct (php assoc array)
4187         * @param compositor (all|sequence|choice)
4188         * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
4189         * @param elements = array ( name = array(name=>'',type=>'') )
4190         * @param attrs = array(
4191         *       array(
4192         *               'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
4193         *               "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
4194         *       )
4195         * )
4196         * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
4197         * @see xmlschema
4198         * 
4199         */
4200         function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
4201                 if (count($elements) > 0) {
4202                 foreach($elements as $n => $e){
4203                     // expand each element
4204                     foreach ($e as $k => $v) {
4205                             $k = strpos($k,':') ? $this->expandQname($k) : $k;
4206                             $v = strpos($v,':') ? $this->expandQname($v) : $v;
4207                             $ee[$k] = $v;
4208                         }
4209                         $eElements[$n] = $ee;
4210                 }
4211                 $elements = $eElements;
4212                 }
4213                 
4214                 if (count($attrs) > 0) {
4215                 foreach($attrs as $n => $a){
4216                     // expand each attribute
4217                     foreach ($a as $k => $v) {
4218                             $k = strpos($k,':') ? $this->expandQname($k) : $k;
4219                             $v = strpos($v,':') ? $this->expandQname($v) : $v;
4220                             $aa[$k] = $v;
4221                         }
4222                         $eAttrs[$n] = $aa;
4223                 }
4224                 $attrs = $eAttrs;
4225                 }
4226
4227                 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
4228                 $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
4229
4230                 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
4231                 $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
4232         }
4233
4234         /**
4235         * adds an XML Schema simple type to the WSDL types
4236         *
4237         * @param name
4238         * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
4239         * @param typeClass (simpleType)
4240         * @param phpType: (scalar)
4241         * @see xmlschema
4242         * 
4243         */
4244         function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
4245                 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
4246
4247                 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
4248                 $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType);
4249         }
4250
4251         /**
4252         * register a service with the server
4253         * 
4254         * @param string $methodname 
4255         * @param string $in assoc array of input values: key = param name, value = param type
4256         * @param string $out assoc array of output values: key = param name, value = param type
4257         * @param string $namespace optional The namespace for the operation
4258         * @param string $soapaction optional The soapaction for the operation
4259         * @param string $style (rpc|document) optional The style for the operation
4260         * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
4261         * @param string $documentation optional The description to include in the WSDL
4262         * @access public 
4263         */
4264         function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = ''){
4265                 if ($style == 'rpc' && $use == 'encoded') {
4266                         $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4267                 } else {
4268                         $encodingStyle = '';
4269                 } 
4270                 // get binding
4271                 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
4272                 array(
4273                 'name' => $name,
4274                 'binding' => $this->serviceName . 'Binding',
4275                 'endpoint' => $this->endpoint,
4276                 'soapAction' => $soapaction,
4277                 'style' => $style,
4278                 'input' => array(
4279                         'use' => $use,
4280                         'namespace' => $namespace,
4281                         'encodingStyle' => $encodingStyle,
4282                         'message' => $name . 'Request',
4283                         'parts' => $in),
4284                 'output' => array(
4285                         'use' => $use,
4286                         'namespace' => $namespace,
4287                         'encodingStyle' => $encodingStyle,
4288                         'message' => $name . 'Response',
4289                         'parts' => $out),
4290                 'namespace' => $namespace,
4291                 'transport' => 'http://schemas.xmlsoap.org/soap/http',
4292                 'documentation' => $documentation); 
4293                 // add portTypes
4294                 // add messages
4295                 if($in)
4296                 {
4297                         foreach($in as $pName => $pType)
4298                         {
4299                                 if(strpos($pType,':')) {
4300                                         $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
4301                                 }
4302                                 $this->messages[$name.'Request'][$pName] = $pType;
4303                         }
4304                 } else {
4305             $this->messages[$name.'Request']= '0';
4306         }
4307                 if($out)
4308                 {
4309                         foreach($out as $pName => $pType)
4310                         {
4311                                 if(strpos($pType,':')) {
4312                                         $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
4313                                 }
4314                                 $this->messages[$name.'Response'][$pName] = $pType;
4315                         }
4316                 } else {
4317             $this->messages[$name.'Response']= '0';
4318         }
4319                 return true;
4320         } 
4321 }
4322 ?><?php
4323
4324
4325
4326 /**
4327 *
4328 * soap_parser class parses SOAP XML messages into native PHP values
4329 *
4330 * @author   Dietrich Ayala <dietrich@ganx4.com>
4331 * @version  $Id$
4332 * @access   public
4333 */
4334 class soap_parser extends nusoap_base {
4335
4336         var $xml = '';
4337         var $xml_encoding = '';
4338         var $method = '';
4339         var $root_struct = '';
4340         var $root_struct_name = '';
4341         var $root_struct_namespace = '';
4342         var $root_header = '';
4343     var $document = '';                 // incoming SOAP body (text)
4344         // determines where in the message we are (envelope,header,body,method)
4345         var $status = '';
4346         var $position = 0;
4347         var $depth = 0;
4348         var $default_namespace = '';
4349         var $namespaces = array();
4350         var $message = array();
4351     var $parent = '';
4352         var $fault = false;
4353         var $fault_code = '';
4354         var $fault_str = '';
4355         var $fault_detail = '';
4356         var $depth_array = array();
4357         var $debug_flag = true;
4358         var $soapresponse = NULL;
4359         var $responseHeaders = '';      // incoming SOAP headers (text)
4360         var $body_position = 0;
4361         // for multiref parsing:
4362         // array of id => pos
4363         var $ids = array();
4364         // array of id => hrefs => pos
4365         var $multirefs = array();
4366         // toggle for auto-decoding element content
4367         var $decode_utf8 = true;
4368
4369         /**
4370         * constructor
4371         *
4372         * @param    string $xml SOAP message
4373         * @param    string $encoding character encoding scheme of message
4374         * @param    string $method
4375         * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
4376         * @access   public
4377         */
4378         function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
4379                 $this->xml = $xml;
4380                 $this->xml_encoding = $encoding;
4381                 $this->method = $method;
4382                 $this->decode_utf8 = $decode_utf8;
4383
4384                 // Check whether content has been read.
4385                 if(!empty($xml)){
4386                         $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
4387                         // Create an XML parser - why not xml_parser_create_ns?
4388                         $this->parser = xml_parser_create($this->xml_encoding);
4389                         // Set the options for parsing the XML data.
4390                         //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4391                         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4392                         xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
4393                         // Set the object for the parser.
4394                         xml_set_object($this->parser, $this);
4395                         // Set the element handlers for the parser.
4396                         xml_set_element_handler($this->parser, 'start_element','end_element');
4397                         xml_set_character_data_handler($this->parser,'character_data');
4398
4399                         // Parse the XML file.
4400                         if(!xml_parse($this->parser,$xml,true)){
4401                             // Display an error message.
4402                             $err = sprintf('XML error parsing SOAP payload on line %d: %s',
4403                             xml_get_current_line_number($this->parser),
4404                             xml_error_string(xml_get_error_code($this->parser)));
4405                                 $this->debug($err);
4406                                 $this->debug("XML payload:\n" . $xml);
4407                                 $this->setError($err);
4408                         } else {
4409                                 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
4410                                 // get final value
4411                                 $this->soapresponse = $this->message[$this->root_struct]['result'];
4412                                 // get header value: no, because this is documented as XML string
4413 //                              if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
4414 //                                      $this->responseHeaders = $this->message[$this->root_header]['result'];
4415 //                              }
4416                                 // resolve hrefs/ids
4417                                 if(sizeof($this->multirefs) > 0){
4418                                         foreach($this->multirefs as $id => $hrefs){
4419                                                 $this->debug('resolving multirefs for id: '.$id);
4420                                                 $idVal = $this->buildVal($this->ids[$id]);
4421                                                 foreach($hrefs as $refPos => $ref){
4422                                                         $this->debug('resolving href at pos '.$refPos);
4423                                                         $this->multirefs[$id][$refPos] = $idVal;
4424                                                 }
4425                                         }
4426                                 }
4427                         }
4428                         xml_parser_free($this->parser);
4429                 } else {
4430                         $this->debug('xml was empty, didn\'t parse!');
4431                         $this->setError('xml was empty, didn\'t parse!');
4432                 }
4433         }
4434
4435         /**
4436         * start-element handler
4437         *
4438         * @param    string $parser XML parser object
4439         * @param    string $name element name
4440         * @param    string $attrs associative array of attributes
4441         * @access   private
4442         */
4443         function start_element($parser, $name, $attrs) {
4444                 // position in a total number of elements, starting from 0
4445                 // update class level pos
4446                 $pos = $this->position++;
4447                 // and set mine
4448                 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
4449                 // depth = how many levels removed from root?
4450                 // set mine as current global depth and increment global depth value
4451                 $this->message[$pos]['depth'] = $this->depth++;
4452
4453                 // else add self as child to whoever the current parent is
4454                 if($pos != 0){
4455                         $this->message[$this->parent]['children'] .= '|'.$pos;
4456                 }
4457                 // set my parent
4458                 $this->message[$pos]['parent'] = $this->parent;
4459                 // set self as current parent
4460                 $this->parent = $pos;
4461                 // set self as current value for this depth
4462                 $this->depth_array[$this->depth] = $pos;
4463                 // get element prefix
4464                 if(strpos($name,':')){
4465                         // get ns prefix
4466                         $prefix = substr($name,0,strpos($name,':'));
4467                         // get unqualified name
4468                         $name = substr(strstr($name,':'),1);
4469                 }
4470                 // set status
4471                 if($name == 'Envelope'){
4472                         $this->status = 'envelope';
4473                 } elseif($name == 'Header'){
4474                         $this->root_header = $pos;
4475                         $this->status = 'header';
4476                 } elseif($name == 'Body'){
4477                         $this->status = 'body';
4478                         $this->body_position = $pos;
4479                 // set method
4480                 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
4481                         $this->status = 'method';
4482                         $this->root_struct_name = $name;
4483                         $this->root_struct = $pos;
4484                         $this->message[$pos]['type'] = 'struct';
4485                         $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
4486                 }
4487                 // set my status
4488                 $this->message[$pos]['status'] = $this->status;
4489                 // set name
4490                 $this->message[$pos]['name'] = htmlspecialchars($name);
4491                 // set attrs
4492                 $this->message[$pos]['attrs'] = $attrs;
4493
4494                 // loop through atts, logging ns and type declarations
4495         $attstr = '';
4496                 foreach($attrs as $key => $value){
4497                 $key_prefix = $this->getPrefix($key);
4498                         $key_localpart = $this->getLocalPart($key);
4499                         // if ns declarations, add to class level array of valid namespaces
4500             if($key_prefix == 'xmlns'){
4501                                 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
4502                                         $this->XMLSchemaVersion = $value;
4503                                         $this->namespaces['xsd'] = $this->XMLSchemaVersion;
4504                                         $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
4505                                 }
4506                 $this->namespaces[$key_localpart] = $value;
4507                                 // set method namespace
4508                                 if($name == $this->root_struct_name){
4509                                         $this->methodNamespace = $value;
4510                                 }
4511                         // if it's a type declaration, set type
4512             } elseif($key_localpart == 'type'){
4513                 $value_prefix = $this->getPrefix($value);
4514                 $value_localpart = $this->getLocalPart($value);
4515                                 $this->message[$pos]['type'] = $value_localpart;
4516                                 $this->message[$pos]['typePrefix'] = $value_prefix;
4517                 if(isset($this->namespaces[$value_prefix])){
4518                         $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
4519                 } else if(isset($attrs['xmlns:'.$value_prefix])) {
4520                                         $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
4521                 }
4522                                 // should do something here with the namespace of specified type?
4523                         } elseif($key_localpart == 'arrayType'){
4524                                 $this->message[$pos]['type'] = 'array';
4525                                 /* do arrayType ereg here
4526                                 [1]    arrayTypeValue    ::=    atype asize
4527                                 [2]    atype    ::=    QName rank*
4528                                 [3]    rank    ::=    '[' (',')* ']'
4529                                 [4]    asize    ::=    '[' length~ ']'
4530                                 [5]    length    ::=    nextDimension* Digit+
4531                                 [6]    nextDimension    ::=    Digit+ ','
4532                                 */
4533                                 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
4534                                 if(ereg($expr,$value,$regs)){
4535                                         $this->message[$pos]['typePrefix'] = $regs[1];
4536                                         $this->message[$pos]['arrayTypePrefix'] = $regs[1];
4537                         if (isset($this->namespaces[$regs[1]])) {
4538                                 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
4539                         } else if (isset($attrs['xmlns:'.$regs[1]])) {
4540                                                 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
4541                         }
4542                                         $this->message[$pos]['arrayType'] = $regs[2];
4543                                         $this->message[$pos]['arraySize'] = $regs[3];
4544                                         $this->message[$pos]['arrayCols'] = $regs[4];
4545                                 }
4546                         }
4547                         // log id
4548                         if($key == 'id'){
4549                                 $this->ids[$value] = $pos;
4550                         }
4551                         // root
4552                         if($key_localpart == 'root' && $value == 1){
4553                                 $this->status = 'method';
4554                                 $this->root_struct_name = $name;
4555                                 $this->root_struct = $pos;
4556                                 $this->debug("found root struct $this->root_struct_name, pos $pos");
4557                         }
4558             // for doclit
4559             $attstr .= " $key=\"$value\"";
4560                 }
4561         // get namespace - must be done after namespace atts are processed
4562                 if(isset($prefix)){
4563                         $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
4564                         $this->default_namespace = $this->namespaces[$prefix];
4565                 } else {
4566                         $this->message[$pos]['namespace'] = $this->default_namespace;
4567                 }
4568         if($this->status == 'header'){
4569                 if ($this->root_header != $pos) {
4570                         $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
4571                 }
4572         } elseif($this->root_struct_name != ''){
4573                 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
4574         }
4575         }
4576
4577         /**
4578         * end-element handler
4579         *
4580         * @param    string $parser XML parser object
4581         * @param    string $name element name
4582         * @access   private
4583         */
4584         function end_element($parser, $name) {
4585                 // position of current element is equal to the last value left in depth_array for my depth
4586                 $pos = $this->depth_array[$this->depth--];
4587
4588         // get element prefix
4589                 if(strpos($name,':')){
4590                         // get ns prefix
4591                         $prefix = substr($name,0,strpos($name,':'));
4592                         // get unqualified name
4593                         $name = substr(strstr($name,':'),1);
4594                 }
4595                 
4596                 // build to native type
4597                 if(isset($this->body_position) && $pos > $this->body_position){
4598                         // deal w/ multirefs
4599                         if(isset($this->message[$pos]['attrs']['href'])){
4600                                 // get id
4601                                 $id = substr($this->message[$pos]['attrs']['href'],1);
4602                                 // add placeholder to href array
4603                                 $this->multirefs[$id][$pos] = 'placeholder';
4604                                 // add set a reference to it as the result value
4605                                 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
4606             // build complex values
4607                         } elseif($this->message[$pos]['children'] != ''){
4608                         
4609                                 // if result has already been generated (struct/array
4610                                 if(!isset($this->message[$pos]['result'])){
4611                                         $this->message[$pos]['result'] = $this->buildVal($pos);
4612                                 }
4613                                 
4614                         // set value of simple type
4615                         } else {
4616                 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
4617                 if (isset($this->message[$pos]['type'])) {
4618                                         $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'] : '');
4619                                 } else {
4620                                         $parent = $this->message[$pos]['parent'];
4621                                         if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
4622                                                 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
4623                                         } else {
4624                                                 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
4625                                         }
4626                                 }
4627
4628                                 /* add value to parent's result, if parent is struct/array
4629                                 $parent = $this->message[$pos]['parent'];
4630                                 if($this->message[$parent]['type'] != 'map'){
4631                                         if(strtolower($this->message[$parent]['type']) == 'array'){
4632                                                 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
4633                                         } else {
4634                                                 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
4635                                         }
4636                                 }
4637                                 */
4638                         }
4639                 }
4640                 
4641         // for doclit
4642         if($this->status == 'header'){
4643                 if ($this->root_header != $pos) {
4644                         $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
4645                 }
4646         } elseif($pos >= $this->root_struct){
4647                 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
4648         }
4649                 // switch status
4650                 if($pos == $this->root_struct){
4651                         $this->status = 'body';
4652                         $this->root_struct_namespace = $this->message[$pos]['namespace'];
4653                 } elseif($name == 'Body'){
4654                         $this->status = 'envelope';
4655                  } elseif($name == 'Header'){
4656                         $this->status = 'envelope';
4657                 } elseif($name == 'Envelope'){
4658                         //
4659                 }
4660                 // set parent back to my parent
4661                 $this->parent = $this->message[$pos]['parent'];
4662         }
4663
4664         /**
4665         * element content handler
4666         *
4667         * @param    string $parser XML parser object
4668         * @param    string $data element content
4669         * @access   private
4670         */
4671         function character_data($parser, $data){
4672                 $pos = $this->depth_array[$this->depth];
4673                 if ($this->xml_encoding=='UTF-8'){
4674                         // TODO: add an option to disable this for folks who want
4675                         // raw UTF-8 that, e.g., might not map to iso-8859-1
4676                         // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
4677                         if($this->decode_utf8){
4678                                 $data = utf8_decode($data);
4679                         }
4680                 }
4681         $this->message[$pos]['cdata'] .= $data;
4682         // for doclit
4683         if($this->status == 'header'){
4684                 $this->responseHeaders .= $data;
4685         } else {
4686                 $this->document .= $data;
4687         }
4688         }
4689
4690         /**
4691         * get the parsed message
4692         *
4693         * @return       mixed
4694         * @access   public
4695         */
4696         function get_response(){
4697                 return $this->soapresponse;
4698         }
4699
4700         /**
4701         * get the parsed headers
4702         *
4703         * @return       string XML or empty if no headers
4704         * @access   public
4705         */
4706         function getHeaders(){
4707             return $this->responseHeaders;
4708         }
4709
4710         /**
4711         * decodes entities
4712         *
4713         * @param    string $text string to translate
4714         * @access   private
4715         */
4716         function decode_entities($text){
4717                 foreach($this->entities as $entity => $encoded){
4718                         $text = str_replace($encoded,$entity,$text);
4719                 }
4720                 return $text;
4721         }
4722
4723         /**
4724         * decodes simple types into PHP variables
4725         *
4726         * @param    string $value value to decode
4727         * @param    string $type XML type to decode
4728         * @param    string $typens XML type namespace to decode
4729         * @access   private
4730         */
4731         function decodeSimple($value, $type, $typens) {
4732                 // TODO: use the namespace!
4733                 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
4734                         return (string) $value;
4735                 }
4736                 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
4737                         return (int) $value;
4738                 }
4739                 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
4740                         return (double) $value;
4741                 }
4742                 if ($type == 'boolean') {
4743                         if (strtolower($value) == 'false' || strtolower($value) == 'f') {
4744                                 return false;
4745                         }
4746                         return (boolean) $value;
4747                 }
4748                 if ($type == 'base64' || $type == 'base64Binary') {
4749                         return base64_decode($value);
4750                 }
4751                 // obscure numeric types
4752                 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
4753                         || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
4754                         || $type == 'unsignedInt'
4755                         || $type == 'unsignedShort' || $type == 'unsignedByte') {
4756                         return (int) $value;
4757                 }
4758                 // everything else
4759                 return (string) $value;
4760         }
4761
4762         /**
4763         * builds response structures for compound values (arrays/structs)
4764         *
4765         * @param    string $pos position in node tree
4766         * @access   private
4767         */
4768         function buildVal($pos){
4769                 if(!isset($this->message[$pos]['type'])){
4770                         $this->message[$pos]['type'] = '';
4771                 }
4772                 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
4773                 // if there are children...
4774                 if($this->message[$pos]['children'] != ''){
4775                         $children = explode('|',$this->message[$pos]['children']);
4776                         array_shift($children); // knock off empty
4777                         // md array
4778                         if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
4779                 $r=0; // rowcount
4780                 $c=0; // colcount
4781                 foreach($children as $child_pos){
4782                                         $this->debug("got an MD array element: $r, $c");
4783                                         $params[$r][] = $this->message[$child_pos]['result'];
4784                                     $c++;
4785                                     if($c == $this->message[$pos]['arrayCols']){
4786                                         $c = 0;
4787                                                 $r++;
4788                                     }
4789                 }
4790             // array
4791                         } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
4792                 $this->debug('adding array '.$this->message[$pos]['name']);
4793                 foreach($children as $child_pos){
4794                         $params[] = &$this->message[$child_pos]['result'];
4795                 }
4796             // apache Map type: java hashtable
4797             } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
4798                 foreach($children as $child_pos){
4799                         $kv = explode("|",$this->message[$child_pos]['children']);
4800                         $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
4801                 }
4802             // generic compound type
4803             //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
4804                     } else {
4805                         // Apache Vector type: treat as an array
4806                                 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
4807                                         $notstruct = 1;
4808                                 } else {
4809                         // is array or struct?
4810                         foreach($children as $child_pos){
4811                                 if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
4812                                         $notstruct = 1;
4813                                         break;
4814                                 }
4815                                 $keys[$this->message[$child_pos]['name']] = 1;
4816                         }
4817                     }
4818                 //
4819                 foreach($children as $child_pos){
4820                         if(isset($notstruct)){
4821                                 $params[] = &$this->message[$child_pos]['result'];
4822                         } else {
4823                                 if (isset($params[$this->message[$child_pos]['name']])) {
4824                                         // de-serialize repeated element name into an array
4825                                         if (!is_array($params[$this->message[$child_pos]['name']])) {
4826                                                 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
4827                                         }
4828                                         $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
4829                                 } else {
4830                                                 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
4831                                             }
4832                         }
4833                 }
4834                         }
4835                         return is_array($params) ? $params : array();
4836                 } else {
4837                 $this->debug('no children');
4838             if(strpos($this->message[$pos]['cdata'],'&')){
4839                         return  strtr($this->message[$pos]['cdata'],array_flip($this->entities));
4840             } else {
4841                 return $this->message[$pos]['cdata'];
4842             }
4843                 }
4844         }
4845 }
4846
4847
4848
4849 ?><?php
4850
4851
4852
4853 /**
4854 *
4855 * nusoapclient higher level class for easy usage.
4856 *
4857 * usage:
4858 *
4859 * // instantiate client with server info
4860 * $soapclient = new nusoapclient( string path [ ,boolean wsdl] );
4861 *
4862 * // call method, get results
4863 * echo $soapclient->call( string methodname [ ,array parameters] );
4864 *
4865 * // bye bye client
4866 * unset($soapclient);
4867 *
4868 * @author   Dietrich Ayala <dietrich@ganx4.com>
4869 * @version  $Id$
4870 * @access   public
4871 */
4872 class nusoapclient extends nusoap_base  {
4873
4874         var $username = '';
4875         var $password = '';
4876         var $requestHeaders = false;    // SOAP headers in request (text)
4877         var $responseHeaders = '';              // SOAP headers from response (incomplete namespace resolution) (text)
4878         var $document = '';                             // SOAP body response portion (incomplete namespace resolution) (text)
4879         var $endpoint;
4880         var $error_str = false;
4881     var $proxyhost = '';
4882     var $proxyport = '';
4883         var $proxyusername = '';
4884         var $proxypassword = '';
4885     var $xml_encoding = '';                     // character set encoding of incoming (response) messages
4886         var $http_encoding = false;
4887         var $timeout = 0;
4888         var $response_timeout = 30;
4889         var $endpointType = '';
4890         var $persistentConnection = false;
4891         var $defaultRpcParams = false;
4892         var $request = '';                              // HTTP request
4893         var $response = '';                             // HTTP response
4894         var $responseData = '';                 // SOAP payload of response
4895         // toggles whether the parser decodes element content w/ utf8_decode()
4896     var $decode_utf8 = true;
4897         
4898         /**
4899         * fault related variables
4900         *
4901         * @var      fault
4902         * @var      faultcode
4903         * @var      faultstring
4904         * @var      faultdetail
4905         * @access   public
4906         */
4907         var $fault, $faultcode, $faultstring, $faultdetail;
4908
4909         /**
4910         * constructor
4911         *
4912         * @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
4913         * @param    bool $wsdl optional, set to true if using WSDL
4914         * @param        int $portName optional portName in WSDL document
4915         * @param    string $proxyhost
4916         * @param    string $proxyport
4917         * @param        string $proxyusername
4918         * @param        string $proxypassword
4919         * @param        integer $timeout set the connection timeout
4920         * @param        integer $response_timeout set the response timeout
4921         * @access   public
4922         */
4923         function nusoapclient($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
4924                 $this->endpoint = $endpoint;
4925                 $this->proxyhost = $proxyhost;
4926                 $this->proxyport = $proxyport;
4927                 $this->proxyusername = $proxyusername;
4928                 $this->proxypassword = $proxypassword;
4929                 $this->timeout = $timeout;
4930                 $this->response_timeout = $response_timeout;
4931
4932                 // make values
4933                 if($wsdl){
4934                         $this->endpointType = 'wsdl';
4935                         if (is_object($endpoint) && is_a($endpoint, 'wsdl')) {
4936                                 $this->wsdl = $endpoint;
4937                                 $this->endpoint = $this->wsdl->wsdl;
4938                                 $this->wsdlFile = $this->endpoint;
4939                                 $this->debug('existing wsdl instance created from ' . $this->endpoint);
4940                         } else {
4941                                 $this->wsdlFile = $this->endpoint;
4942                                 
4943                                 // instantiate wsdl object and parse wsdl file
4944                                 $this->debug('instantiating wsdl class with doc: '.$endpoint);
4945                                 $this->wsdl = new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout);
4946                         }
4947                         $this->debug("wsdl debug...\n".$this->wsdl->debug_str);
4948                         $this->wsdl->debug_str = '';
4949                         // catch errors
4950                         if($errstr = $this->wsdl->getError()){
4951                                 $this->debug('got wsdl error: '.$errstr);
4952                                 $this->setError('wsdl error: '.$errstr);
4953                         } elseif($this->operations = $this->wsdl->getOperations()){
4954                                 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
4955                         } else {
4956                                 $this->debug( 'getOperations returned false');
4957                                 $this->setError('no operations defined in the WSDL document!');
4958                         }
4959                 }
4960         }
4961
4962         /**
4963         * calls method, returns PHP native type
4964         *
4965         * @param    string $method SOAP server URL or path
4966         * @param    array $params For RPC, array of parameters, can be associative or not.
4967         *                         For literal, either the stringized XML for the body,
4968         *                         or an array of parameters like the RPC case.  The
4969         *                         $rpcParams parameter controls this treatment, or
4970         *                         the $defaultRpcParams field if $rpcParams is not
4971         *                         specified.  IMPORTANT: most services with literal
4972         *                         parameters have document style, in which case there
4973         *                         is really one parameter, the root of the fragment
4974         *                         used in the call, which encloses what programmers
4975         *                         normally think of parameters.  A parameter array
4976         *                         *must* include the wrapper.
4977         * @param        string $namespace optional method namespace (WSDL can override)
4978         * @param        string $soapAction optional SOAPAction value (WSDL can override)
4979         * @param        boolean $headers optional array of soapval objects for headers
4980         * @param        boolean $rpcParams optional treat params as RPC for use="literal"
4981         *                   This can be used on a per-call basis to overrider defaultRpcParams.
4982         * @param        string  $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
4983         * @param        string  $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
4984         * @return       mixed
4985         * @access   public
4986         */
4987         function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
4988                 $this->operation = $operation;
4989                 $this->fault = false;
4990                 $this->error_str = '';
4991                 $this->request = '';
4992                 $this->response = '';
4993                 $this->responseData = '';
4994                 $this->faultstring = '';
4995                 $this->faultcode = '';
4996                 $this->opData = array();
4997                 
4998                 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
4999                 $this->debug("endpointType: $this->endpointType");
5000                 if ($headers) {
5001                         $this->requestHeaders = $headers;
5002                 }
5003                 // if wsdl, get operation data and process parameters
5004                 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
5005
5006                         $this->opData = $opData;
5007                         foreach($opData as $key => $value){
5008                                 $this->debug("$key -> $value");
5009                         }
5010                         if (isset($opData['soapAction'])) {
5011                                 $soapAction = $opData['soapAction'];
5012                         }
5013                         $this->endpoint = $opData['endpoint'];
5014                         $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] :     ($namespace != '' ? $namespace : 'http://testuri.org');
5015                         $style = $opData['style'];
5016                         // add ns to ns array
5017                         if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
5018                                 $this->wsdl->namespaces['nu'] = $namespace;
5019             }
5020                         // serialize payload
5021                         
5022                         if($opData['input']['use'] == 'literal') {
5023                                 if (is_null($rpcParams)) {
5024                                         $rpcParams = $this->defaultRpcParams;
5025                                 }
5026                                 if ($rpcParams) {
5027                                         $this->debug("serializing literal params for operation $operation");
5028                                         $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
5029                                         $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
5030                                         //$this->debug($this->varDump($params));
5031                                 } else {
5032                                         // TODO: what?  We want to treat $params as a scalar....
5033                                         $this->debug("serializing literal document for operation $operation");
5034                                         //$payload = is_array($params) ? array_shift($params) : $params;
5035                                         $payload = $this->wsdl->serializeParameters($operation,'input',$params);
5036                                 }
5037                         } else {
5038                                 $this->debug("serializing encoded params for operation $operation");
5039                         
5040                                 // Partial fix for multiple encoding styles in the same function call
5041                                 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5042                                 $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation";
5043                                 if(isset($opData['output']['encodingStyle']) && $encodingStyle != $opData['output']['encodingStyle']) {
5044                                         $payload .= (' SOAP-ENV:encodingStyle="' . $opData['output']['encodingStyle'] . '"');
5045                                 }                                                                               
5046                                 $payload .= ('>' . $this->wsdl->serializeRPCParameters($operation,'input',$params).
5047                                                          '</'.$this->wsdl->getPrefixFromNamespace($namespace).":$operation>");
5048                         }
5049                         $this->debug('payload size: '.strlen($payload));
5050                         // serialize envelope
5051                         $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style);
5052                         $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
5053                         $this->wsdl->debug_str = '';
5054                         if ($errstr = $this->wsdl->getError()) {
5055                                 $this->debug('got wsdl error: '.$errstr);
5056                                 $this->setError('wsdl error: '.$errstr);
5057                                 return false;
5058                         }
5059                 } elseif($this->endpointType == 'wsdl') {
5060                         $this->setError( 'operation '.$operation.' not present.');
5061                         $this->debug("operation '$operation' not present.");
5062                         $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
5063                         $this->wsdl->debug_str = '';
5064                         return false;
5065                 // no wsdl
5066                 } else {
5067                         // make message
5068             if($namespace == ''){
5069                 $namespace = 'http://testuri.org';
5070                 $this->wsdl->namespaces['ns1'] = $namespace;
5071             }
5072                         // serialize envelope
5073                         // note: 
5074                         $payload = '';
5075                         if(is_array($params)){
5076                                 foreach($params as $k => $v){
5077                                         $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
5078                                 }
5079                         }
5080                         $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">".$payload."</ns1:$operation>";
5081                         $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,array(),$style,$use);
5082                 }
5083                 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace, style: $style");
5084                 // send
5085                 $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'...");
5086                 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
5087                 if($errstr = $this->getError()){
5088                         $this->debug('Error: '.$errstr);
5089                         return false;
5090                 } else {
5091                         $this->return = $return;
5092                         $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
5093                         
5094                         // fault?
5095                         if(is_array($return) && isset($return['faultcode'])){
5096                                 $this->debug('got fault');
5097                                 $this->setError($return['faultcode'].': '.$return['faultstring']);
5098                                 $this->fault = true;
5099                                 foreach($return as $k => $v){
5100                                         $this->$k = $v;
5101                                         $this->debug("$k = $v<br>");
5102                                 }
5103                                 return $return;
5104                         } else {
5105                                 // array of return values
5106                                 if(is_array($return)){
5107                                         // multiple 'out' parameters
5108                                         if(sizeof($return) > 1){
5109                                                 return $return;
5110                                         }
5111                                         // single 'out' parameter
5112                                         return array_shift($return);
5113                                 // nothing returned (ie, echoVoid)
5114                                 } else {
5115                                         return "";
5116                                 }
5117                         }
5118                 }
5119         }
5120
5121         /**
5122         * get available data pertaining to an operation
5123         *
5124         * @param    string $operation operation name
5125         * @return       array array of data pertaining to the operation
5126         * @access   public
5127         */
5128         function getOperationData($operation){
5129                 if(isset($this->operations[$operation])){
5130                         return $this->operations[$operation];
5131                 }
5132                 $this->debug("No data for operation: $operation");
5133         }
5134
5135     /**
5136     * send the SOAP message
5137     *
5138     * Note: if the operation has multiple return values
5139     * the return value of this method will be an array
5140     * of those values.
5141     *
5142         * @param    string $msg a SOAPx4 soapmsg object
5143         * @param    string $soapaction SOAPAction value
5144         * @param    integer $timeout set connection timeout in seconds
5145         * @param        integer $response_timeout set response timeout in seconds
5146         * @return       mixed native PHP types.
5147         * @access   private
5148         */
5149         function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
5150                 // detect transport
5151                 switch(true){
5152                         // http(s)
5153                         case ereg('^http',$this->endpoint):
5154                                 $this->debug('transporting via HTTP');
5155                                 if($this->persistentConnection == true && is_object($this->persistentConnection)){
5156                                         $http =& $this->persistentConnection;
5157                                 } else {
5158                                         $http = new soap_transport_http($this->endpoint);
5159                                         if ($this->persistentConnection) {
5160                                                 $http->usePersistentConnection();
5161                                         }
5162                                 }
5163                                 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
5164                                 $http->setSOAPAction($soapaction);
5165                                 if($this->proxyhost && $this->proxyport){
5166                                         $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
5167                                 }
5168                 if($this->username != '' && $this->password != '') {
5169                                         $http->setCredentials($this->username,$this->password);
5170                                 }
5171                                 if($this->http_encoding != ''){
5172                                         $http->setEncoding($this->http_encoding);
5173                                 }
5174                                 $this->debug('sending message, length: '.strlen($msg));
5175                                 if(ereg('^http:',$this->endpoint)){
5176                                 //if(strpos($this->endpoint,'http:')){
5177                                         $this->responseData = $http->send($msg,$timeout,$response_timeout);
5178                                 } elseif(ereg('^https',$this->endpoint)){
5179                                 //} elseif(strpos($this->endpoint,'https:')){
5180                                         //if(phpversion() == '4.3.0-dev'){
5181                                                 //$response = $http->send($msg,$timeout,$response_timeout);
5182                                 //$this->request = $http->outgoing_payload;
5183                                                 //$this->response = $http->incoming_payload;
5184                                         //} else
5185                                         if (extension_loaded('curl')) {
5186                                                 $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout);
5187                                         } else {
5188                                                 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
5189                                         }                                                               
5190                                 } else {
5191                                         $this->setError('no http/s in endpoint url');
5192                                 }
5193                                 $this->request = $http->outgoing_payload;
5194                                 $this->response = $http->incoming_payload;
5195                                 $this->debug("transport debug data...\n".$http->debug_str);
5196                                 
5197                                 // save transport object if using persistent connections
5198                                 if ($this->persistentConnection) {
5199                                         $http->debug_str = '';
5200                                         if (!is_object($this->persistentConnection)) {
5201                                                 $this->persistentConnection = $http;
5202                                         }
5203                                 }
5204                                 
5205                                 if($err = $http->getError()){
5206                                         $this->setError('HTTP Error: '.$err);
5207                                         return false;
5208                                 } elseif($this->getError()){
5209                                         return false;
5210                                 } else {
5211                                         $this->debug('got response, length: '. strlen($this->responseData).' type: '.$http->incoming_headers['content-type']);
5212                                         return $this->parseResponse($http->incoming_headers, $this->responseData);
5213                                 }
5214                         break;
5215                         default:
5216                                 $this->setError('no transport found, or selected transport is not yet supported!');
5217                         return false;
5218                         break;
5219                 }
5220         }
5221
5222         /**
5223         * processes SOAP message returned from server
5224         *
5225         * @param        array   $headers        The HTTP headers
5226         * @param        string  $data           unprocessed response data from server
5227         * @return       mixed   value of the message, decoded into a PHP type
5228         * @access   protected
5229         */
5230     function parseResponse($headers, $data) {
5231                 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
5232                 if (!strstr($headers['content-type'], 'text/xml')) {
5233                         $this->setError('Response not of type text/xml');
5234                         return false;
5235                 }
5236                 if (strpos($headers['content-type'], '=')) {
5237                         $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
5238                         $this->debug('Got response encoding: ' . $enc);
5239                         if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
5240                                 $this->xml_encoding = strtoupper($enc);
5241                         } else {
5242                                 $this->xml_encoding = 'US-ASCII';
5243                         }
5244                 } else {
5245                         // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
5246                         $this->xml_encoding = 'UTF-8';
5247                 }
5248                 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
5249                 $parser = new soap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
5250                 // add parser debug data to our debug
5251                 $this->debug($parser->debug_str);
5252                 // if parse errors
5253                 if($errstr = $parser->getError()){
5254                         $this->setError( $errstr);
5255                         // destroy the parser object
5256                         unset($parser);
5257                         return false;
5258                 } else {
5259                         // get SOAP headers
5260                         $this->responseHeaders = $parser->getHeaders();
5261                         // get decoded message
5262                         $return = $parser->get_response();
5263             // add document for doclit support
5264             $this->document = $parser->document;
5265                         // destroy the parser object
5266                         unset($parser);
5267                         // return decode message
5268                         return $return;
5269                 }
5270          }
5271
5272         /**
5273         * set the SOAP headers
5274         *
5275         * @param        $headers string XML
5276         * @access   public
5277         */
5278         function setHeaders($headers){
5279                 $this->requestHeaders = $headers;
5280         }
5281
5282         /**
5283         * get the response headers
5284         *
5285         * @return       mixed object SOAPx4 soapval object or empty if no headers
5286         * @access   public
5287         */
5288         function getHeaders(){
5289             if($this->responseHeaders != '') {
5290                         return $this->responseHeaders;
5291             }
5292         }
5293
5294         /**
5295         * set proxy info here
5296         *
5297         * @param    string $proxyhost
5298         * @param    string $proxyport
5299         * @param        string $proxyusername
5300         * @param        string $proxypassword
5301         * @access   public
5302         */
5303         function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
5304                 $this->proxyhost = $proxyhost;
5305                 $this->proxyport = $proxyport;
5306                 $this->proxyusername = $proxyusername;
5307                 $this->proxypassword = $proxypassword;
5308         }
5309
5310         /**
5311         * if authenticating, set user credentials here
5312         *
5313         * @param    string $username
5314         * @param    string $password
5315         * @access   public
5316         */
5317         function setCredentials($username, $password) {
5318                 $this->username = $username;
5319                 $this->password = $password;
5320         }
5321         
5322         /**
5323         * use HTTP encoding
5324         *
5325         * @param    string $enc
5326         * @access   public
5327         */
5328         function setHTTPEncoding($enc='gzip, deflate'){
5329                 $this->http_encoding = $enc;
5330         }
5331         
5332         /**
5333         * use HTTP persistent connections if possible
5334         *
5335         * @access   public
5336         */
5337         function useHTTPPersistentConnection(){
5338                 $this->persistentConnection = true;
5339         }
5340         
5341         /**
5342         * gets the default RPC parameter setting.
5343         * If true, default is that call params are like RPC even for document style.
5344         * Each call() can override this value.
5345         *
5346         * @access public
5347         */
5348         function getDefaultRpcParams() {
5349                 return $this->defaultRpcParams;
5350         }
5351
5352         /**
5353         * sets the default RPC parameter setting.
5354         * If true, default is that call params are like RPC even for document style
5355         * Each call() can override this value.
5356         *
5357         * @param    boolean $rpcParams
5358         * @access public
5359         */
5360         function setDefaultRpcParams($rpcParams) {
5361                 $this->defaultRpcParams = $rpcParams;
5362         }
5363         
5364         /**
5365         * dynamically creates proxy class, allowing user to directly call methods from wsdl
5366         *
5367         * @return   object soap_proxy object
5368         * @access   public
5369         */
5370         function getProxy(){
5371                 $evalStr = '';
5372                 foreach($this->operations as $operation => $opData){
5373                         if($operation != ''){
5374                                 // create param string
5375                                 $paramStr = '';
5376                                 if(sizeof($opData['input']['parts']) > 0){
5377                                         foreach($opData['input']['parts'] as $name => $type){
5378                                                 $paramStr .= "\$$name,";
5379                                         }
5380                                         $paramStr = substr($paramStr,0,strlen($paramStr)-1);
5381                                 }
5382                                 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
5383                                 $evalStr .= "function $operation ($paramStr){
5384                                         // load params into array
5385                                         \$params = array($paramStr);
5386                                         return \$this->call('$operation',\$params,'".$opData['namespace']."','".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
5387                                 }";
5388                                 unset($paramStr);
5389                         }
5390                 }
5391                 $r = rand();
5392                 $evalStr = 'class soap_proxy_'.$r.' extends nusoapclient {
5393                                 '.$evalStr.'
5394                         }';
5395                 //print "proxy class:<pre>$evalStr</pre>";
5396                 // eval the class
5397                 eval($evalStr);
5398                 // instantiate proxy object
5399                 eval("\$proxy = new soap_proxy_$r('');");
5400                 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
5401                 $proxy->endpointType = 'wsdl';
5402                 $proxy->wsdlFile = $this->wsdlFile;
5403                 $proxy->wsdl = $this->wsdl;
5404                 $proxy->operations = $this->operations;
5405                 $proxy->defaultRpcParams = $this->defaultRpcParams;
5406                 // transfer other state
5407                 $proxy->username = $this->username;
5408                 $proxy->password = $this->password;
5409                 $proxy->proxyhost = $this->proxyhost;
5410                 $proxy->proxyport = $this->proxyport;
5411                 $proxy->proxyusername = $this->proxyusername;
5412                 $proxy->proxypassword = $this->proxypassword;
5413                 $proxy->timeout = $this->timeout;
5414                 $proxy->response_timeout = $this->response_timeout;
5415                 $proxy->http_encoding = $this->http_encoding;
5416                 $proxy->persistentConnection = $this->persistentConnection;
5417                 return $proxy;
5418         }
5419
5420         /**
5421         * gets the HTTP body for the current request.
5422         *
5423         * @param string $soapmsg The SOAP payload
5424         * @return string The HTTP body, which includes the SOAP payload
5425         * @access protected
5426         */
5427         function getHTTPBody($soapmsg) {
5428                 return $soapmsg;
5429         }
5430         
5431         /**
5432         * gets the HTTP content type for the current request.
5433         *
5434         * Note: getHTTPBody must be called before this.
5435         *
5436         * @return string the HTTP content type for the current request.
5437         * @access protected
5438         */
5439         function getHTTPContentType() {
5440                 return 'text/xml';
5441         }
5442         
5443         /**
5444         * gets the HTTP content type charset for the current request.
5445         * returns false for non-text content types.
5446         *
5447         * Note: getHTTPBody must be called before this.
5448         *
5449         * @return string the HTTP content type charset for the current request.
5450         * @access protected
5451         */
5452         function getHTTPContentTypeCharset() {
5453                 return $this->soap_defencoding;
5454         }
5455
5456         /*
5457         * whether or not parser should decode utf8 element content
5458     *
5459     * @return   always returns true
5460     * @access   public
5461     */
5462     function decodeUTF8($bool){
5463                 $this->decode_utf8 = $bool;
5464                 return true;
5465     }
5466 }
5467 ?>