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 | |
8 | NuSOAP - Web Services Toolkit for PHP |
9 | |
10 | Copyright (c) 2002 NuSphere Corporation |
11 | |
12 | This library is free software; you can redistribute it and/or |
13 | modify it under the terms of the GNU Lesser General Public |
14 | License as published by the Free Software Foundation; either |
15 | version 2.1 of the License, or (at your option) any later version. |
16 | |
17 | This library is distributed in the hope that it will be useful, |
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
20 | Lesser General Public License for more details. |
21 | |
22 | You should have received a copy of the GNU Lesser General Public |
23 | License along with this library; if not, write to the Free Software |
24 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
25 | |
26 | If you have any questions or comments, please email: |
27 | |
28 | Dietrich Ayala |
29 | dietrich@ganx4.com |
30 | http://dietrich.ganx4.com/nusoap |
31 | |
32 | NuSphere Corporation |
33 | http://www.nusphere.com |
34 | |
35 | */ |
36 | |
37 | /* load classes |
38 | |
39 | // necessary classes |
40 | require_once('class.soapclient.php'); |
41 | require_once('class.soap_val.php'); |
42 | require_once('class.soap_parser.php'); |
43 | require_once('class.soap_fault.php'); |
44 | |
45 | // transport classes |
46 | require_once('class.soap_transport_http.php'); |
47 | |
48 | // optional add-on classes |
49 | require_once('class.xmlschema.php'); |
50 | require_once('class.wsdl.php'); |
51 | |
52 | // server class |
53 | require_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 | */ |
63 | class 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 '<'. |
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('&', '&', $val); |
212 | $val = str_replace("'", ''', $val); |
213 | $val = str_replace('"', '"', $val); |
214 | $val = str_replace('<', '<', $val); |
215 | $val = str_replace('>', '>', $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 | */ |
661 | function 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 | */ |
689 | function 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 | |
719 | function 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 | */ |
744 | class 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 | */ |
809 | class 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 | */ |
1579 | class 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 | */ |
1636 | class 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 | */ |
2473 | class 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 | */ |
3227 | class 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 | { |
|