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