Fixed bug #15580: Add calls to logDeprecatedFunction() for more deprecated functions...
[Packages/TYPO3.CMS.git] / typo3 / sysext / em / mod1 / class.nusoap.php
1 <?php
2 /*
3 $Id$
4
5 NuSOAP - Web Services Toolkit for PHP
6
7 Copyright (c) 2002 NuSphere Corporation
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 §§§§§
23 If you have any questions or comments, please email:
24
25 Dietrich Ayala
26 dietrich@ganx4.com
27 http://dietrich.ganx4.com/nusoap
28
29 NuSphere Corporation
30 http://www.nusphere.com
31
32 */
33
34 /* load classes
35
36 // necessary classes
37 require_once('class.soapclient.php');
38 require_once('class.soap_val.php');
39 require_once('class.soap_parser.php');
40 require_once('class.soap_fault.php');
41
42 // transport classes
43 require_once('class.soap_transport_http.php');
44
45 // optional add-on classes
46 require_once('class.xmlschema.php');
47 require_once('class.wsdl.php');
48 */
49
50 // class variable emulation
51 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
52 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
53
54 /**
55 *
56 * nusoap_base
57 *
58 * @author Dietrich Ayala <dietrich@ganx4.com>
59 * @version $Id$
60 * @access public
61 */
62 class nusoap_base {
63 /**
64 * Identification for HTTP headers.
65 *
66 * @var string
67 * @access private
68 */
69 var $title = 'NuSOAP';
70 /**
71 * Version for HTTP headers.
72 *
73 * @var string
74 * @access private
75 */
76 var $version = '0.7.2';
77 /**
78 * CVS revision for HTTP headers.
79 *
80 * @var string
81 * @access private
82 */
83 var $revision = '$Revision$';
84 /**
85 * Current error string (manipulated by getError/setError)
86 *
87 * @var string
88 * @access private
89 */
90 var $error_str = '';
91 /**
92 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
93 *
94 * @var string
95 * @access private
96 */
97 var $debug_str = '';
98 /**
99 * toggles automatic encoding of special characters as entities
100 * (should always be true, I think)
101 *
102 * @var boolean
103 * @access private
104 */
105 var $charencoding = true;
106 /**
107 * the debug level for this instance
108 *
109 * @var integer
110 * @access private
111 */
112 var $debugLevel;
113
114 /**
115 * set schema version
116 *
117 * @var string
118 * @access public
119 */
120 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
121
122 /**
123 * charset encoding for outgoing messages
124 *
125 * @var string
126 * @access public
127 */
128 var $soap_defencoding = 'ISO-8859-1';
129 //var $soap_defencoding = 'UTF-8';
130
131 /**
132 * namespaces in an array of prefix => uri
133 *
134 * this is "seeded" by a set of constants, but it may be altered by code
135 *
136 * @var array
137 * @access public
138 */
139 var $namespaces = array(
140 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
141 'xsd' => 'http://www.w3.org/2001/XMLSchema',
142 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
143 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
144 );
145
146 /**
147 * namespaces used in the current context, e.g. during serialization
148 *
149 * @var array
150 * @access private
151 */
152 var $usedNamespaces = array();
153
154 /**
155 * XML Schema types in an array of uri => (array of xml type => php type)
156 * is this legacy yet?
157 * no, this is used by the xmlschema class to verify type => namespace mappings.
158 * @var array
159 * @access public
160 */
161 var $typemap = array(
162 'http://www.w3.org/2001/XMLSchema' => array(
163 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
164 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
165 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
166 // abstract "any" types
167 'anyType'=>'string','anySimpleType'=>'string',
168 // derived datatypes
169 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
170 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
171 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
172 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
173 'http://www.w3.org/2000/10/XMLSchema' => array(
174 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
175 'float'=>'double','dateTime'=>'string',
176 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
177 'http://www.w3.org/1999/XMLSchema' => array(
178 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
179 'float'=>'double','dateTime'=>'string',
180 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
181 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
182 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
183 'http://xml.apache.org/xml-soap' => array('Map')
184 );
185
186 /**
187 * XML entities to convert
188 *
189 * @var array
190 * @access public
191 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.5.
192 * @see expandEntities
193 */
194 var $xmlEntities = array('quot' => '"','amp' => '&',
195 'lt' => '<','gt' => '>','apos' => "'");
196
197 /**
198 * constructor
199 *
200 * @access public
201 */
202 function nusoap_base() {
203 $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
204 }
205
206 /**
207 * gets the global debug level, which applies to future instances
208 *
209 * @return integer Debug level 0-9, where 0 turns off
210 * @access public
211 */
212 function getGlobalDebugLevel() {
213 return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
214 }
215
216 /**
217 * sets the global debug level, which applies to future instances
218 *
219 * @param int $level Debug level 0-9, where 0 turns off
220 * @access public
221 */
222 function setGlobalDebugLevel($level) {
223 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
224 }
225
226 /**
227 * gets the debug level for this instance
228 *
229 * @return int Debug level 0-9, where 0 turns off
230 * @access public
231 */
232 function getDebugLevel() {
233 return $this->debugLevel;
234 }
235
236 /**
237 * sets the debug level for this instance
238 *
239 * @param int $level Debug level 0-9, where 0 turns off
240 * @access public
241 */
242 function setDebugLevel($level) {
243 $this->debugLevel = $level;
244 }
245
246 /**
247 * adds debug data to the instance debug string with formatting
248 *
249 * @param string $string debug data
250 * @access private
251 */
252 function debug($string){
253 if ($this->debugLevel > 0) {
254 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
255 }
256 }
257
258 /**
259 * adds debug data to the instance debug string without formatting
260 *
261 * @param string $string debug data
262 * @access public
263 */
264 function appendDebug($string){
265 if ($this->debugLevel > 0) {
266 // it would be nice to use a memory stream here to use
267 // memory more efficiently
268 $this->debug_str .= $string;
269 }
270 }
271
272 /**
273 * clears the current debug data for this instance
274 *
275 * @access public
276 */
277 function clearDebug() {
278 // it would be nice to use a memory stream here to use
279 // memory more efficiently
280 $this->debug_str = '';
281 }
282
283 /**
284 * gets the current debug data for this instance
285 *
286 * @return debug data
287 * @access public
288 */
289 function &getDebug() {
290 // it would be nice to use a memory stream here to use
291 // memory more efficiently
292 return $this->debug_str;
293 }
294
295 /**
296 * gets the current debug data for this instance as an XML comment
297 * this may change the contents of the debug data
298 *
299 * @return debug data as an XML comment
300 * @access public
301 */
302 function &getDebugAsXMLComment() {
303 // it would be nice to use a memory stream here to use
304 // memory more efficiently
305 while (strpos($this->debug_str, '--')) {
306 $this->debug_str = str_replace('--', '- -', $this->debug_str);
307 }
308 return "<!--\n" . $this->debug_str . "\n-->";
309 }
310
311 /**
312 * expands entities, e.g. changes '<' to '&lt;'.
313 *
314 * @param string $val The string in which to expand entities.
315 * @access private
316 */
317 function expandEntities($val) {
318 if ($this->charencoding) {
319 $val = str_replace('&', '&amp;', $val);
320 $val = str_replace("'", '&apos;', $val);
321 $val = str_replace('"', '&quot;', $val);
322 $val = str_replace('<', '&lt;', $val);
323 $val = str_replace('>', '&gt;', $val);
324 }
325 return $val;
326 }
327
328 /**
329 * returns error string if present
330 *
331 * @return mixed error string or false
332 * @access public
333 */
334 function getError(){
335 if($this->error_str != ''){
336 return $this->error_str;
337 }
338 return false;
339 }
340
341 /**
342 * sets error string
343 *
344 * @return boolean $string error string
345 * @access private
346 */
347 function setError($str){
348 $this->error_str = $str;
349 }
350
351 /**
352 * detect if array is a simple array or a struct (associative array)
353 *
354 * @param mixed $val The PHP array
355 * @return string (arraySimple|arrayStruct)
356 * @access private
357 */
358 function isArraySimpleOrStruct($val) {
359 $keyList = array_keys($val);
360 foreach ($keyList as $keyListValue) {
361 if (!is_int($keyListValue)) {
362 return 'arrayStruct';
363 }
364 }
365 return 'arraySimple';
366 }
367
368 /**
369 * serializes PHP values in accordance w/ section 5. Type information is
370 * not serialized if $use == 'literal'.
371 *
372 * @param mixed $val The value to serialize
373 * @param string $name The name (local part) of the XML element
374 * @param string $type The XML schema type (local part) for the element
375 * @param string $name_ns The namespace for the name of the XML element
376 * @param string $type_ns The namespace for the type of the element
377 * @param array $attributes The attributes to serialize as name=>value pairs
378 * @param string $use The WSDL "use" (encoded|literal)
379 * @return string The serialized element, possibly with child elements
380 * @access public
381 */
382 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
383 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
384 $this->appendDebug('value=' . $this->varDump($val));
385 $this->appendDebug('attributes=' . $this->varDump($attributes));
386
387 if(is_object($val) && get_class($val) == 'soapval'){
388 return $val->serialize($use);
389 }
390 // force valid name if necessary
391 if (is_numeric($name)) {
392 $name = '__numeric_' . $name;
393 } elseif (! $name) {
394 $name = 'noname';
395 }
396 // if name has ns, add ns prefix to name
397 $xmlns = '';
398 if($name_ns){
399 $prefix = 'nu'.rand(1000,9999);
400 $name = $prefix.':'.$name;
401 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
402 }
403 // if type is prefixed, create type prefix
404 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
405 // need to fix this. shouldn't default to xsd if no ns specified
406 // w/o checking against typemap
407 $type_prefix = 'xsd';
408 } elseif($type_ns){
409 $type_prefix = 'ns'.rand(1000,9999);
410 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
411 }
412 // serialize attributes if present
413 $atts = '';
414 if($attributes){
415 foreach($attributes as $k => $v){
416 $atts .= " $k=\"".$this->expandEntities($v).'"';
417 }
418 }
419 // serialize null value
420 if (is_null($val)) {
421 if ($use == 'literal') {
422 // TODO: depends on minOccurs
423 return "<$name$xmlns $atts/>";
424 } else {
425 if (isset($type) && isset($type_prefix)) {
426 $type_str = " xsi:type=\"$type_prefix:$type\"";
427 } else {
428 $type_str = '';
429 }
430 return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
431 }
432 }
433 // serialize if an xsd built-in primitive type
434 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
435 if (is_bool($val)) {
436 if ($type == 'boolean') {
437 $val = $val ? 'true' : 'false';
438 } elseif (! $val) {
439 $val = 0;
440 }
441 } else if (is_string($val)) {
442 $val = $this->expandEntities($val);
443 }
444 if ($use == 'literal') {
445 return "<$name$xmlns $atts>$val</$name>";
446 } else {
447 return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
448 }
449 }
450 // detect type and serialize
451 $xml = '';
452 switch(true) {
453 case (is_bool($val) || $type == 'boolean'):
454 if ($type == 'boolean') {
455 $val = $val ? 'true' : 'false';
456 } elseif (! $val) {
457 $val = 0;
458 }
459 if ($use == 'literal') {
460 $xml .= "<$name$xmlns $atts>$val</$name>";
461 } else {
462 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
463 }
464 break;
465 case (is_int($val) || is_long($val) || $type == 'int'):
466 if ($use == 'literal') {
467 $xml .= "<$name$xmlns $atts>$val</$name>";
468 } else {
469 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
470 }
471 break;
472 case (is_float($val)|| is_double($val) || $type == 'float'):
473 if ($use == 'literal') {
474 $xml .= "<$name$xmlns $atts>$val</$name>";
475 } else {
476 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
477 }
478 break;
479 case (is_string($val) || $type == 'string'):
480 $val = $this->expandEntities($val);
481 if ($use == 'literal') {
482 $xml .= "<$name$xmlns $atts>$val</$name>";
483 } else {
484 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
485 }
486 break;
487 case is_object($val):
488 if (! $name) {
489 $name = get_class($val);
490 $this->debug("In serialize_val, used class name $name as element name");
491 } else {
492 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
493 }
494 foreach(get_object_vars($val) as $k => $v){
495 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
496 }
497 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
498 break;
499 break;
500 case (is_array($val) || $type):
501 // detect if struct or array
502 $valueType = $this->isArraySimpleOrStruct($val);
503 if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
504 $i = 0;
505 if(is_array($val) && count($val)> 0){
506 foreach($val as $v){
507 if(is_object($v) && get_class($v) == 'soapval'){
508 $tt_ns = $v->type_ns;
509 $tt = $v->type;
510 } elseif (is_array($v)) {
511 $tt = $this->isArraySimpleOrStruct($v);
512 } else {
513 $tt = gettype($v);
514 }
515 $array_types[$tt] = 1;
516 // TODO: for literal, the name should be $name
517 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
518 ++$i;
519 }
520 if(count($array_types) > 1){
521 $array_typename = 'xsd:anyType';
522 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
523 if ($tt == 'integer') {
524 $tt = 'int';
525 }
526 $array_typename = 'xsd:'.$tt;
527 } elseif(isset($tt) && $tt == 'arraySimple'){
528 $array_typename = 'SOAP-ENC:Array';
529 } elseif(isset($tt) && $tt == 'arrayStruct'){
530 $array_typename = 'unnamed_struct_use_soapval';
531 } else {
532 // if type is prefixed, create type prefix
533 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
534 $array_typename = 'xsd:' . $tt;
535 } elseif ($tt_ns) {
536 $tt_prefix = 'ns' . rand(1000, 9999);
537 $array_typename = "$tt_prefix:$tt";
538 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
539 } else {
540 $array_typename = $tt;
541 }
542 }
543 $array_type = $i;
544 if ($use == 'literal') {
545 $type_str = '';
546 } else if (isset($type) && isset($type_prefix)) {
547 $type_str = " xsi:type=\"$type_prefix:$type\"";
548 } else {
549 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
550 }
551 // empty array
552 } else {
553 if ($use == 'literal') {
554 $type_str = '';
555 } else if (isset($type) && isset($type_prefix)) {
556 $type_str = " xsi:type=\"$type_prefix:$type\"";
557 } else {
558 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
559 }
560 }
561 // TODO: for array in literal, there is no wrapper here
562 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
563 } else {
564 // got a struct
565 if(isset($type) && isset($type_prefix)){
566 $type_str = " xsi:type=\"$type_prefix:$type\"";
567 } else {
568 $type_str = '';
569 }
570 if ($use == 'literal') {
571 $xml .= "<$name$xmlns $atts>";
572 } else {
573 $xml .= "<$name$xmlns$type_str$atts>";
574 }
575 foreach($val as $k => $v){
576 // Apache Map
577 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
578 $xml .= '<item>';
579 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
580 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
581 $xml .= '</item>';
582 } else {
583 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
584 }
585 }
586 $xml .= "</$name>";
587 }
588 break;
589 default:
590 $xml .= 'not detected, got '.gettype($val).' for '.$val;
591 break;
592 }
593 return $xml;
594 }
595
596 /**
597 * serializes a message
598 *
599 * @param string $body the XML of the SOAP body
600 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
601 * @param array $namespaces optional the namespaces used in generating the body and headers
602 * @param string $style optional (rpc|document)
603 * @param string $use optional (encoded|literal)
604 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
605 * @return string the message
606 * @access public
607 */
608 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
609 // TODO: add an option to automatically run utf8_encode on $body and $headers
610 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
611 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
612
613 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
614 $this->debug("headers:");
615 $this->appendDebug($this->varDump($headers));
616 $this->debug("namespaces:");
617 $this->appendDebug($this->varDump($namespaces));
618
619 // serialize namespaces
620 $ns_string = '';
621 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
622 $ns_string .= " xmlns:$k=\"$v\"";
623 }
624 if($encodingStyle) {
625 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
626 }
627
628 // serialize headers
629 if($headers){
630 if (is_array($headers)) {
631 $xml = '';
632 foreach ($headers as $header) {
633 $xml .= $this->serialize_val($header, false, false, false, false, false, $use);
634 }
635 $headers = $xml;
636 $this->debug("In serializeEnvelope, serialzied array of headers to $headers");
637 }
638 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
639 }
640 // serialize envelope
641 return
642 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
643 '<SOAP-ENV:Envelope'.$ns_string.">".
644 $headers.
645 "<SOAP-ENV:Body>".
646 $body.
647 "</SOAP-ENV:Body>".
648 "</SOAP-ENV:Envelope>";
649 }
650
651 /**
652 * formats a string to be inserted into an HTML stream
653 *
654 * @param string $str The string to format
655 * @return string The formatted string
656 * @access public
657 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6
658 */
659 function formatDump($str){
660 t3lib_div::logDeprecatedFunction();
661
662 $str = htmlspecialchars($str);
663 return nl2br($str);
664 }
665
666 /**
667 * contracts (changes namespace to prefix) a qualified name
668 *
669 * @param string $qname qname
670 * @return string contracted qname
671 * @access private
672 */
673 function contractQname($qname){
674 // get element namespace
675 //$this->xdebug("Contract $qname");
676 if (strrpos($qname, ':')) {
677 // get unqualified name
678 $name = substr($qname, strrpos($qname, ':') + 1);
679 // get ns
680 $ns = substr($qname, 0, strrpos($qname, ':'));
681 $p = $this->getPrefixFromNamespace($ns);
682 if ($p) {
683 return $p . ':' . $name;
684 }
685 return $qname;
686 } else {
687 return $qname;
688 }
689 }
690
691 /**
692 * expands (changes prefix to namespace) a qualified name
693 *
694 * @param string $string qname
695 * @return string expanded qname
696 * @access private
697 */
698 function expandQname($qname){
699 // get element prefix
700 if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
701 // get unqualified name
702 $name = substr(strstr($qname,':'),1);
703 // get ns prefix
704 $prefix = substr($qname,0,strpos($qname,':'));
705 if(isset($this->namespaces[$prefix])){
706 return $this->namespaces[$prefix].':'.$name;
707 } else {
708 return $qname;
709 }
710 } else {
711 return $qname;
712 }
713 }
714
715 /**
716 * returns the local part of a prefixed string
717 * returns the original string, if not prefixed
718 *
719 * @param string $str The prefixed string
720 * @return string The local part
721 * @access public
722 */
723 function getLocalPart($str){
724 if($sstr = strrchr($str,':')){
725 // get unqualified name
726 return substr( $sstr, 1 );
727 } else {
728 return $str;
729 }
730 }
731
732 /**
733 * returns the prefix part of a prefixed string
734 * returns false, if not prefixed
735 *
736 * @param string $str The prefixed string
737 * @return mixed The prefix or false if there is no prefix
738 * @access public
739 */
740 function getPrefix($str){
741 if($pos = strrpos($str,':')){
742 // get prefix
743 return substr($str,0,$pos);
744 }
745 return false;
746 }
747
748 /**
749 * pass it a prefix, it returns a namespace
750 *
751 * @param string $prefix The prefix
752 * @return mixed The namespace, false if no namespace has the specified prefix
753 * @access public
754 */
755 function getNamespaceFromPrefix($prefix){
756 if (isset($this->namespaces[$prefix])) {
757 return $this->namespaces[$prefix];
758 }
759 //$this->setError("No namespace registered for prefix '$prefix'");
760 return false;
761 }
762
763 /**
764 * returns the prefix for a given namespace (or prefix)
765 * or false if no prefixes registered for the given namespace
766 *
767 * @param string $ns The namespace
768 * @return mixed The prefix, false if the namespace has no prefixes
769 * @access public
770 */
771 function getPrefixFromNamespace($ns) {
772 foreach ($this->namespaces as $p => $n) {
773 if ($ns == $n || $ns == $p) {
774 $this->usedNamespaces[$p] = $n;
775 return $p;
776 }
777 }
778 return false;
779 }
780
781 /**
782 * returns the time in ODBC canonical form with microseconds
783 *
784 * @return string The time in ODBC canonical form with microseconds
785 * @access public
786 */
787 function getmicrotime() {
788 if (function_exists('gettimeofday')) {
789 $tod = gettimeofday();
790 $sec = $tod['sec'];
791 $usec = $tod['usec'];
792 } else {
793 $sec = time();
794 $usec = 0;
795 }
796 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
797 }
798
799 /**
800 * Returns a string with the output of var_dump
801 *
802 * @param mixed $data The variable to var_dump
803 * @return string The output of var_dump
804 * @access public
805 */
806 function varDump($data) {
807 ob_start();
808 var_dump($data);
809 $ret_val = ob_get_contents();
810 ob_end_clean();
811 return $ret_val;
812 }
813 }
814
815 // XML Schema Datatype Helper Functions
816
817 //xsd:dateTime helpers
818
819 /**
820 * convert unix timestamp to ISO 8601 compliant date string
821 *
822 * @param string $timestamp Unix time stamp
823 * @access public
824 */
825 function timestamp_to_iso8601($timestamp,$utc=true){
826 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
827 if($utc){
828 $pattern = '/'.
829 '([0-9]{4})-'. // centuries & years CCYY-
830 '([0-9]{2})-'. // months MM-
831 '([0-9]{2})'. // days DD
832 'T'. // separator T
833 '([0-9]{2}):'. // hours hh:
834 '([0-9]{2}):'. // minutes mm:
835 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
836 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
837 '/';
838
839 if(preg_match($pattern,$datestr,$regs)){
840 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
841 }
842 return false;
843 } else {
844 return $datestr;
845 }
846 }
847
848 /**
849 * convert ISO 8601 compliant date string to unix timestamp
850 *
851 * @param string $datestr ISO 8601 compliant date string
852 * @access public
853 */
854 function iso8601_to_timestamp($datestr){
855 $pattern = '/'.
856 '([0-9]{4})-'. // centuries & years CCYY-
857 '([0-9]{2})-'. // months MM-
858 '([0-9]{2})'. // days DD
859 'T'. // separator T
860 '([0-9]{2}):'. // hours hh:
861 '([0-9]{2}):'. // minutes mm:
862 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
863 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
864 '/';
865
866 if(preg_match($pattern,$datestr,$regs)){
867 // not utc
868 if($regs[8] != 'Z'){
869 $op = substr($regs[8],0,1);
870 $h = substr($regs[8],1,2);
871 $m = substr($regs[8],strlen($regs[8])-2,2);
872 if($op == '-'){
873 $regs[4] = $regs[4] + $h;
874 $regs[5] = $regs[5] + $m;
875 } elseif($op == '+'){
876 $regs[4] = $regs[4] - $h;
877 $regs[5] = $regs[5] - $m;
878 }
879 }
880 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
881 } else {
882 return false;
883 }
884 }
885
886 /**
887 * sleeps some number of microseconds
888 *
889 * @param string $usec the number of microseconds to sleep
890 * @access public
891 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6.
892 */
893 function usleepWindows($usec)
894 {
895 t3lib_div::logDeprecatedFunction();
896
897 $start = gettimeofday();
898
899 do
900 {
901 $stop = gettimeofday();
902 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
903 + $stop['usec'] - $start['usec'];
904 }
905 while ($timePassed < $usec);
906 }
907
908
909
910 /**
911 * Contains information for a SOAP fault.
912 * Mainly used for returning faults from deployed functions
913 * in a server instance.
914 * @author Dietrich Ayala <dietrich@ganx4.com>
915 * @version $Id$
916 * @access public
917 */
918 class soap_fault extends nusoap_base {
919 /**
920 * The fault code (client|server)
921 * @var string
922 * @access private
923 */
924 var $faultcode;
925 /**
926 * The fault actor
927 * @var string
928 * @access private
929 */
930 var $faultactor;
931 /**
932 * The fault string, a description of the fault
933 * @var string
934 * @access private
935 */
936 var $faultstring;
937 /**
938 * The fault detail, typically a string or array of string
939 * @var mixed
940 * @access private
941 */
942 var $faultdetail;
943
944 /**
945 * constructor
946 *
947 * @param string $faultcode (client | server)
948 * @param string $faultactor only used when msg routed between multiple actors
949 * @param string $faultstring human readable error message
950 * @param mixed $faultdetail detail, typically a string or array of string
951 */
952 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
953 parent::nusoap_base();
954 $this->faultcode = $faultcode;
955 $this->faultactor = $faultactor;
956 $this->faultstring = $faultstring;
957 $this->faultdetail = $faultdetail;
958 }
959
960 /**
961 * serialize a fault
962 *
963 * @return string The serialization of the fault instance.
964 * @access public
965 */
966 function serialize(){
967 $ns_string = '';
968 foreach($this->namespaces as $k => $v){
969 $ns_string .= "\n xmlns:$k=\"$v\"";
970 }
971 $return_msg =
972 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
973 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
974 '<SOAP-ENV:Body>'.
975 '<SOAP-ENV:Fault>'.
976 $this->serialize_val($this->faultcode, 'faultcode').
977 $this->serialize_val($this->faultactor, 'faultactor').
978 $this->serialize_val($this->faultstring, 'faultstring').
979 $this->serialize_val($this->faultdetail, 'detail').
980 '</SOAP-ENV:Fault>'.
981 '</SOAP-ENV:Body>'.
982 '</SOAP-ENV:Envelope>';
983 return $return_msg;
984 }
985 }
986
987
988
989 /**
990 * parses an XML Schema, allows access to it's data, other utility methods
991 * no validation... yet.
992 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
993 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
994 * tutorials I refer to :)
995 *
996 * @author Dietrich Ayala <dietrich@ganx4.com>
997 * @version $Id$
998 * @access public
999 */
1000 class XMLSchema extends nusoap_base {
1001
1002 // files
1003 var $schema = '';
1004 var $xml = '';
1005 // namespaces
1006 var $enclosingNamespaces;
1007 // schema info
1008 var $schemaInfo = array();
1009 var $schemaTargetNamespace = '';
1010 // types, elements, attributes defined by the schema
1011 var $attributes = array();
1012 var $complexTypes = array();
1013 var $complexTypeStack = array();
1014 var $currentComplexType = null;
1015 var $elements = array();
1016 var $elementStack = array();
1017 var $currentElement = null;
1018 var $simpleTypes = array();
1019 var $simpleTypeStack = array();
1020 var $currentSimpleType = null;
1021 // imports
1022 var $imports = array();
1023 // parser vars
1024 var $parser;
1025 var $position = 0;
1026 var $depth = 0;
1027 var $depth_array = array();
1028 var $message = array();
1029 var $defaultNamespace = array();
1030
1031 /**
1032 * constructor
1033 *
1034 * @param string $schema schema document URI
1035 * @param string $xml xml document URI
1036 * @param string $namespaces namespaces defined in enclosing XML
1037 * @access public
1038 */
1039 function XMLSchema($schema='',$xml='',$namespaces=array()){
1040 parent::nusoap_base();
1041 $this->debug('xmlschema class instantiated, inside constructor');
1042 // files
1043 $this->schema = $schema;
1044 $this->xml = $xml;
1045
1046 // namespaces
1047 $this->enclosingNamespaces = $namespaces;
1048 $this->namespaces = array_merge($this->namespaces, $namespaces);
1049
1050 // parse schema file
1051 if($schema != ''){
1052 $this->debug('initial schema file: '.$schema);
1053 $this->parseFile($schema, 'schema');
1054 }
1055
1056 // parse xml file
1057 if($xml != ''){
1058 $this->debug('initial xml file: '.$xml);
1059 $this->parseFile($xml, 'xml');
1060 }
1061
1062 }
1063
1064 /**
1065 * parse an XML file
1066 *
1067 * @param string $xml, path/URL to XML file
1068 * @param string $type, (schema | xml)
1069 * @return boolean
1070 * @access public
1071 */
1072 function parseFile($xml,$type){
1073 // parse xml file
1074 if($xml != ""){
1075 $xmlStr = @join("",@file($xml));
1076 if($xmlStr == ""){
1077 $msg = 'Error reading XML from '.$xml;
1078 $this->setError($msg);
1079 $this->debug($msg);
1080 return false;
1081 } else {
1082 $this->debug("parsing $xml");
1083 $this->parseString($xmlStr,$type);
1084 $this->debug("done parsing $xml");
1085 return true;
1086 }
1087 }
1088 return false;
1089 }
1090
1091 /**
1092 * parse an XML string
1093 *
1094 * @param string $xml path or URL
1095 * @param string $type, (schema|xml)
1096 * @access private
1097 */
1098 function parseString($xml,$type){
1099 // parse xml string
1100 if($xml != ""){
1101
1102 // Create an XML parser.
1103 $this->parser = xml_parser_create();
1104 // Set the options for parsing the XML data.
1105 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1106
1107 // Set the object for the parser.
1108 xml_set_object($this->parser, $this);
1109
1110 // Set the element handlers for the parser.
1111 if($type == "schema"){
1112 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1113 xml_set_character_data_handler($this->parser,'schemaCharacterData');
1114 } elseif($type == "xml"){
1115 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1116 xml_set_character_data_handler($this->parser,'xmlCharacterData');
1117 }
1118
1119 // Parse the XML file.
1120 if(!xml_parse($this->parser,$xml,true)){
1121 // Display an error message.
1122 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1123 xml_get_current_line_number($this->parser),
1124 xml_error_string(xml_get_error_code($this->parser))
1125 );
1126 $this->debug($errstr);
1127 $this->debug("XML payload:\n" . $xml);
1128 $this->setError($errstr);
1129 }
1130
1131 xml_parser_free($this->parser);
1132 } else{
1133 $this->debug('no xml passed to parseString()!!');
1134 $this->setError('no xml passed to parseString()!!');
1135 }
1136 }
1137
1138 /**
1139 * start-element handler
1140 *
1141 * @param string $parser XML parser object
1142 * @param string $name element name
1143 * @param string $attrs associative array of attributes
1144 * @access private
1145 */
1146 function schemaStartElement($parser, $name, $attrs) {
1147
1148 // position in the total number of elements, starting from 0
1149 $pos = $this->position++;
1150 $depth = $this->depth++;
1151 // set self as current value for this depth
1152 $this->depth_array[$depth] = $pos;
1153 $this->message[$pos] = array('cdata' => '');
1154 if ($depth > 0) {
1155 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1156 } else {
1157 $this->defaultNamespace[$pos] = false;
1158 }
1159
1160 // get element prefix
1161 if($prefix = $this->getPrefix($name)){
1162 // get unqualified name
1163 $name = $this->getLocalPart($name);
1164 } else {
1165 $prefix = '';
1166 }
1167
1168 // loop thru attributes, expanding, and registering namespace declarations
1169 if(count($attrs) > 0){
1170 foreach($attrs as $k => $v){
1171 // if ns declarations, add to class level array of valid namespaces
1172 if(preg_match('/^xmlns/',$k)){
1173 //$this->xdebug("$k: $v");
1174 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1175 if($ns_prefix = substr(strrchr($k,':'),1)){
1176 //$this->xdebug("Add namespace[$ns_prefix] = $v");
1177 $this->namespaces[$ns_prefix] = $v;
1178 } else {
1179 $this->defaultNamespace[$pos] = $v;
1180 if (! $this->getPrefixFromNamespace($v)) {
1181 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1182 }
1183 }
1184 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1185 $this->XMLSchemaVersion = $v;
1186 $this->namespaces['xsi'] = $v.'-instance';
1187 }
1188 }
1189 }
1190 foreach($attrs as $k => $v){
1191 // expand each attribute
1192 $k = strpos($k,':') ? $this->expandQname($k) : $k;
1193 $v = strpos($v,':') ? $this->expandQname($v) : $v;
1194 $eAttrs[$k] = $v;
1195 }
1196 $attrs = $eAttrs;
1197 } else {
1198 $attrs = array();
1199 }
1200 // find status, register data
1201 switch($name){
1202 case 'all': // (optional) compositor content for a complexType
1203 case 'choice':
1204 case 'group':
1205 case 'sequence':
1206 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1207 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1208 //if($name == 'all' || $name == 'sequence'){
1209 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1210 //}
1211 break;
1212 case 'attribute': // complexType attribute
1213 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1214 $this->xdebug("parsing attribute:");
1215 $this->appendDebug($this->varDump($attrs));
1216 if (!isset($attrs['form'])) {
1217 $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1218 }
1219 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1220 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1221 if (!strpos($v, ':')) {
1222 // no namespace in arrayType attribute value...
1223 if ($this->defaultNamespace[$pos]) {
1224 // ...so use the default
1225 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1226 }
1227 }
1228 }
1229 if(isset($attrs['name'])){
1230 $this->attributes[$attrs['name']] = $attrs;
1231 $aname = $attrs['name'];
1232 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1233 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1234 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1235 } else {
1236 $aname = '';
1237 }
1238 } elseif(isset($attrs['ref'])){
1239 $aname = $attrs['ref'];
1240 $this->attributes[$attrs['ref']] = $attrs;
1241 }
1242
1243 if($this->currentComplexType){ // This should *always* be
1244 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1245 }
1246 // arrayType attribute
1247 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1248 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1249 $prefix = $this->getPrefix($aname);
1250 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1251 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1252 } else {
1253 $v = '';
1254 }
1255 if(strpos($v,'[,]')){
1256 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1257 }
1258 $v = substr($v,0,strpos($v,'[')); // clip the []
1259 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1260 $v = $this->XMLSchemaVersion.':'.$v;
1261 }
1262 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1263 }
1264 break;
1265 case 'complexContent': // (optional) content for a complexType
1266 break;
1267 case 'complexType':
1268 array_push($this->complexTypeStack, $this->currentComplexType);
1269 if(isset($attrs['name'])){
1270 $this->xdebug('processing named complexType '.$attrs['name']);
1271 //$this->currentElement = false;
1272 $this->currentComplexType = $attrs['name'];
1273 $this->complexTypes[$this->currentComplexType] = $attrs;
1274 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1275 // This is for constructs like
1276 // <complexType name="ListOfString" base="soap:Array">
1277 // <sequence>
1278 // <element name="string" type="xsd:string"
1279 // minOccurs="0" maxOccurs="unbounded" />
1280 // </sequence>
1281 // </complexType>
1282 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1283 $this->xdebug('complexType is unusual array');
1284 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1285 } else {
1286 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1287 }
1288 }else{
1289 $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
1290 $this->currentComplexType = $this->currentElement . '_ContainedType';
1291 //$this->currentElement = false;
1292 $this->complexTypes[$this->currentComplexType] = $attrs;
1293 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1294 // This is for constructs like
1295 // <complexType name="ListOfString" base="soap:Array">
1296 // <sequence>
1297 // <element name="string" type="xsd:string"
1298 // minOccurs="0" maxOccurs="unbounded" />
1299 // </sequence>
1300 // </complexType>
1301 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1302 $this->xdebug('complexType is unusual array');
1303 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1304 } else {
1305 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1306 }
1307 }
1308 break;
1309 case 'element':
1310 array_push($this->elementStack, $this->currentElement);
1311 // elements defined as part of a complex type should
1312 // not really be added to $this->elements, but for some
1313 // reason, they are
1314 if (!isset($attrs['form'])) {
1315 $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1316 }
1317 if(isset($attrs['type'])){
1318 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1319 if (! $this->getPrefix($attrs['type'])) {
1320 if ($this->defaultNamespace[$pos]) {
1321 $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1322 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1323 }
1324 }
1325 // This is for constructs like
1326 // <complexType name="ListOfString" base="soap:Array">
1327 // <sequence>
1328 // <element name="string" type="xsd:string"
1329 // minOccurs="0" maxOccurs="unbounded" />
1330 // </sequence>
1331 // </complexType>
1332 if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1333 $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1334 $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1335 }
1336 $this->currentElement = $attrs['name'];
1337 $this->elements[ $attrs['name'] ] = $attrs;
1338 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1339 $ename = $attrs['name'];
1340 } elseif(isset($attrs['ref'])){
1341 $this->xdebug("processing element as ref to ".$attrs['ref']);
1342 $this->currentElement = "ref to ".$attrs['ref'];
1343 $ename = $this->getLocalPart($attrs['ref']);
1344 } else {
1345 $this->xdebug("processing untyped element ".$attrs['name']);
1346 $this->currentElement = $attrs['name'];
1347 $this->elements[ $attrs['name'] ] = $attrs;
1348 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1349 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1350 $this->elements[ $attrs['name'] ]['type'] = $attrs['type'];
1351 $ename = $attrs['name'];
1352 }
1353 if(isset($ename) && $this->currentComplexType){
1354 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1355 }
1356 break;
1357 case 'enumeration': // restriction value list member
1358 $this->xdebug('enumeration ' . $attrs['value']);
1359 if ($this->currentSimpleType) {
1360 $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1361 } elseif ($this->currentComplexType) {
1362 $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1363 }
1364 break;
1365 case 'extension': // simpleContent or complexContent type extension
1366 $this->xdebug('extension ' . $attrs['base']);
1367 if ($this->currentComplexType) {
1368 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1369 }
1370 break;
1371 case 'import':
1372 if (isset($attrs['schemaLocation'])) {
1373 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1374 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1375 } else {
1376 //$this->xdebug('import namespace ' . $attrs['namespace']);
1377 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1378 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1379 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1380 }
1381 }
1382 break;
1383 case 'list': // simpleType value list
1384 break;
1385 case 'restriction': // simpleType, simpleContent or complexContent value restriction
1386 $this->xdebug('restriction ' . $attrs['base']);
1387 if($this->currentSimpleType){
1388 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1389 } elseif($this->currentComplexType){
1390 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1391 if(strstr($attrs['base'],':') == ':Array'){
1392 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1393 }
1394 }
1395 break;
1396 case 'schema':
1397 $this->schemaInfo = $attrs;
1398 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1399 if (isset($attrs['targetNamespace'])) {
1400 $this->schemaTargetNamespace = $attrs['targetNamespace'];
1401 }
1402 if (!isset($attrs['elementFormDefault'])) {
1403 $this->schemaInfo['elementFormDefault'] = 'unqualified';
1404 }
1405 if (!isset($attrs['attributeFormDefault'])) {
1406 $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1407 }
1408 break;
1409 case 'simpleContent': // (optional) content for a complexType
1410 break;
1411 case 'simpleType':
1412 array_push($this->simpleTypeStack, $this->currentSimpleType);
1413 if(isset($attrs['name'])){
1414 $this->xdebug("processing simpleType for name " . $attrs['name']);
1415 $this->currentSimpleType = $attrs['name'];
1416 $this->simpleTypes[ $attrs['name'] ] = $attrs;
1417 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1418 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1419 } else {
1420 $this->xdebug('processing unnamed simpleType for element '.$this->currentElement);
1421 $this->currentSimpleType = $this->currentElement . '_ContainedType';
1422 //$this->currentElement = false;
1423 $this->simpleTypes[$this->currentSimpleType] = $attrs;
1424 $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1425 }
1426 break;
1427 case 'union': // simpleType type list
1428 break;
1429 default:
1430 //$this->xdebug("do not have anything to do for element $name");
1431 }
1432 }
1433
1434 /**
1435 * end-element handler
1436 *
1437 * @param string $parser XML parser object
1438 * @param string $name element name
1439 * @access private
1440 */
1441 function schemaEndElement($parser, $name) {
1442 // bring depth down a notch
1443 $this->depth--;
1444 // position of current element is equal to the last value left in depth_array for my depth
1445 if(isset($this->depth_array[$this->depth])){
1446 $pos = $this->depth_array[$this->depth];
1447 }
1448 // get element prefix
1449 if ($prefix = $this->getPrefix($name)){
1450 // get unqualified name
1451 $name = $this->getLocalPart($name);
1452 } else {
1453 $prefix = '';
1454 }
1455 // move on...
1456 if($name == 'complexType'){
1457 $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1458 $this->currentComplexType = array_pop($this->complexTypeStack);
1459 //$this->currentElement = false;
1460 }
1461 if($name == 'element'){
1462 $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1463 $this->currentElement = array_pop($this->elementStack);
1464 }
1465 if($name == 'simpleType'){
1466 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1467 $this->currentSimpleType = array_pop($this->simpleTypeStack);
1468 }
1469 }
1470
1471 /**
1472 * element content handler
1473 *
1474 * @param string $parser XML parser object
1475 * @param string $data element content
1476 * @access private
1477 */
1478 function schemaCharacterData($parser, $data){
1479 $pos = $this->depth_array[$this->depth - 1];
1480 $this->message[$pos]['cdata'] .= $data;
1481 }
1482
1483 /**
1484 * serialize the schema
1485 *
1486 * @access public
1487 */
1488 function serializeSchema(){
1489
1490 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1491 $xml = '';
1492 // imports
1493 if (sizeof($this->imports) > 0) {
1494 foreach($this->imports as $ns => $list) {
1495 foreach ($list as $ii) {
1496 if ($ii['location'] != '') {
1497 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1498 } else {
1499 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1500 }
1501 }
1502 }
1503 }
1504 // complex types
1505 foreach($this->complexTypes as $typeName => $attrs){
1506 $contentStr = '';
1507 // serialize child elements
1508 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1509 foreach($attrs['elements'] as $element => $eParts){
1510 if(isset($eParts['ref'])){
1511 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1512 } else {
1513 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1514 foreach ($eParts as $aName => $aValue) {
1515 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1516 if ($aName != 'name' && $aName != 'type') {
1517 $contentStr .= " $aName=\"$aValue\"";
1518 }
1519 }
1520 $contentStr .= "/>\n";
1521 }
1522 }
1523 // compositor wraps elements
1524 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1525 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1526 }
1527 }
1528 // attributes
1529 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1530 foreach($attrs['attrs'] as $attr => $aParts){
1531 $contentStr .= " <$schemaPrefix:attribute";
1532 foreach ($aParts as $a => $v) {
1533 if ($a == 'ref' || $a == 'type') {
1534 $contentStr .= " $a=\"".$this->contractQName($v).'"';
1535 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1536 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1537 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1538 } else {
1539 $contentStr .= " $a=\"$v\"";
1540 }
1541 }
1542 $contentStr .= "/>\n";
1543 }
1544 }
1545 // if restriction
1546 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1547 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1548 // complex or simple content
1549 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1550 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1551 }
1552 }
1553 // finalize complex type
1554 if($contentStr != ''){
1555 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1556 } else {
1557 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1558 }
1559 $xml .= $contentStr;
1560 }
1561 // simple types
1562 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1563 foreach($this->simpleTypes as $typeName => $eParts){
1564 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n";
1565 if (isset($eParts['enumeration'])) {
1566 foreach ($eParts['enumeration'] as $e) {
1567 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
1568 }
1569 }
1570 $xml .= " </$schemaPrefix:simpleType>";
1571 }
1572 }
1573 // elements
1574 if(isset($this->elements) && count($this->elements) > 0){
1575 foreach($this->elements as $element => $eParts){
1576 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1577 }
1578 }
1579 // attributes
1580 if(isset($this->attributes) && count($this->attributes) > 0){
1581 foreach($this->attributes as $attr => $aParts){
1582 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1583 }
1584 }
1585 // finish 'er up
1586 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1587 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1588 $el .= " xmlns:$nsp=\"$ns\"\n";
1589 }
1590 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1591 return $xml;
1592 }
1593
1594 /**
1595 * adds debug data to the clas level debug string
1596 *
1597 * @param string $string debug data
1598 * @access private
1599 */
1600 function xdebug($string){
1601 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1602 }
1603
1604 /**
1605 * get the PHP type of a user defined type in the schema
1606 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1607 * returns false if no type exists, or not w/ the given namespace
1608 * else returns a string that is either a native php type, or 'struct'
1609 *
1610 * @param string $type, name of defined type
1611 * @param string $ns, namespace of type
1612 * @return mixed
1613 * @access public
1614 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6.
1615 */
1616 function getPHPType($type,$ns){
1617 t3lib_div::logDeprecatedFunction();
1618
1619 if(isset($this->typemap[$ns][$type])){
1620 //print "found type '$type' and ns $ns in typemap<br>";
1621 return $this->typemap[$ns][$type];
1622 } elseif(isset($this->complexTypes[$type])){
1623 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1624 return $this->complexTypes[$type]['phpType'];
1625 }
1626 return false;
1627 }
1628
1629 /**
1630 * returns an associative array of information about a given type
1631 * returns false if no type exists by the given name
1632 *
1633 * For a complexType typeDef = array(
1634 * 'restrictionBase' => '',
1635 * 'phpType' => '',
1636 * 'compositor' => '(sequence|all)',
1637 * 'elements' => array(), // refs to elements array
1638 * 'attrs' => array() // refs to attributes array
1639 * ... and so on (see addComplexType)
1640 * )
1641 *
1642 * For simpleType or element, the array has different keys.
1643 *
1644 * @param string
1645 * @return mixed
1646 * @access public
1647 * @see addComplexType
1648 * @see addSimpleType
1649 * @see addElement
1650 */
1651 function getTypeDef($type){
1652 //$this->debug("in getTypeDef for type $type");
1653 if(isset($this->complexTypes[$type])){
1654 $this->xdebug("in getTypeDef, found complexType $type");
1655 return $this->complexTypes[$type];
1656 } elseif(isset($this->simpleTypes[$type])){
1657 $this->xdebug("in getTypeDef, found simpleType $type");
1658 if (!isset($this->simpleTypes[$type]['phpType'])) {
1659 // get info for type to tack onto the simple type
1660 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1661 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1662 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1663 $etype = $this->getTypeDef($uqType);
1664 if ($etype) {
1665 $this->xdebug("in getTypeDef, found type for simpleType $type:");
1666 $this->xdebug($this->varDump($etype));
1667 if (isset($etype['phpType'])) {
1668 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1669 }
1670 if (isset($etype['elements'])) {
1671 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1672 }
1673 }
1674 }
1675 return $this->simpleTypes[$type];
1676 } elseif(isset($this->elements[$type])){
1677 $this->xdebug("in getTypeDef, found element $type");
1678 if (!isset($this->elements[$type]['phpType'])) {
1679 // get info for type to tack onto the element
1680 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1681 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1682 $etype = $this->getTypeDef($uqType);
1683 if ($etype) {
1684 $this->xdebug("in getTypeDef, found type for element $type:");
1685 $this->xdebug($this->varDump($etype));
1686 if (isset($etype['phpType'])) {
1687 $this->elements[$type]['phpType'] = $etype['phpType'];
1688 }
1689 if (isset($etype['elements'])) {
1690 $this->elements[$type]['elements'] = $etype['elements'];
1691 }
1692 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1693 $this->xdebug("in getTypeDef, element $type is an XSD type");
1694 $this->elements[$type]['phpType'] = 'scalar';
1695 }
1696 }
1697 return $this->elements[$type];
1698 } elseif(isset($this->attributes[$type])){
1699 $this->xdebug("in getTypeDef, found attribute $type");
1700 return $this->attributes[$type];
1701 } elseif (preg_match('/_ContainedType$/', $type)) {
1702 $this->xdebug("in getTypeDef, have an untyped element $type");
1703 $typeDef['typeClass'] = 'simpleType';
1704 $typeDef['phpType'] = 'scalar';
1705 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1706 return $typeDef;
1707 }
1708 $this->xdebug("in getTypeDef, did not find $type");
1709 return false;
1710 }
1711
1712 /**
1713 * returns a sample serialization of a given type, or false if no type by the given name
1714 *
1715 * @param string $type, name of type
1716 * @return mixed
1717 * @access public
1718 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6.
1719 */
1720 function serializeTypeDef($type){
1721 t3lib_div::logDeprecatedFunction();
1722
1723 //print "in sTD() for type $type<br>";
1724 if($typeDef = $this->getTypeDef($type)){
1725 $str .= '<'.$type;
1726 if(is_array($typeDef['attrs'])){
1727 foreach($attrs as $attName => $data){
1728 $str .= " $attName=\"{type = ".$data['type']."}\"";
1729 }
1730 }
1731 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1732 if(count($typeDef['elements']) > 0){
1733 $str .= ">";
1734 foreach($typeDef['elements'] as $element => $eData){
1735 $str .= $this->serializeTypeDef($element);
1736 }
1737 $str .= "</$type>";
1738 } elseif($typeDef['typeClass'] == 'element') {
1739 $str .= "></$type>";
1740 } else {
1741 $str .= "/>";
1742 }
1743 return $str;
1744 }
1745 return false;
1746 }
1747
1748 /**
1749 * returns HTML form elements that allow a user
1750 * to enter values for creating an instance of the given type.
1751 *
1752 * @param string $name, name for type instance
1753 * @param string $type, name of type
1754 * @return string
1755 * @access public
1756 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6.
1757 */
1758 function typeToForm($name,$type){
1759 t3lib_div::logDeprecatedFunction();
1760
1761 // get typedef
1762 if($typeDef = $this->getTypeDef($type)){
1763 // if struct
1764 if($typeDef['phpType'] == 'struct'){
1765 $buffer .= '<table>';
1766 foreach($typeDef['elements'] as $child => $childDef){
1767 $buffer .= "
1768 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1769 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1770 }
1771 $buffer .= '</table>';
1772 // if array
1773 } elseif($typeDef['phpType'] == 'array'){
1774 $buffer .= '<table>';
1775 for($i=0;$i < 3; $i++){
1776 $buffer .= "
1777 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1778 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1779 }
1780 $buffer .= '</table>';
1781 // if scalar
1782 } else {
1783 $buffer .= "<input type='text' name='parameters[$name]'>";
1784 }
1785 } else {
1786 $buffer .= "<input type='text' name='parameters[$name]'>";
1787 }
1788 return $buffer;
1789 }
1790
1791 /**
1792 * adds a complex type to the schema
1793 *
1794 * example: array
1795 *
1796 * addType(
1797 * 'ArrayOfstring',
1798 * 'complexType',
1799 * 'array',
1800 * '',
1801 * 'SOAP-ENC:Array',
1802 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1803 * 'xsd:string'
1804 * );
1805 *
1806 * example: PHP associative array ( SOAP Struct )
1807 *
1808 * addType(
1809 * 'SOAPStruct',
1810 * 'complexType',
1811 * 'struct',
1812 * 'all',
1813 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1814 * );
1815 *
1816 * @param name
1817 * @param typeClass (complexType|simpleType|attribute)
1818 * @param phpType: currently supported are array and struct (php assoc array)
1819 * @param compositor (all|sequence|choice)
1820 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1821 * @param elements = array ( name = array(name=>'',type=>'') )
1822 * @param attrs = array(
1823 * array(
1824 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1825 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1826 * )
1827 * )
1828 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1829 * @access public
1830 * @see getTypeDef
1831 */
1832 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1833 $this->complexTypes[$name] = array(
1834 'name' => $name,
1835 'typeClass' => $typeClass,
1836 'phpType' => $phpType,
1837 'compositor'=> $compositor,
1838 'restrictionBase' => $restrictionBase,
1839 'elements' => $elements,
1840 'attrs' => $attrs,
1841 'arrayType' => $arrayType
1842 );
1843
1844 $this->xdebug("addComplexType $name:");
1845 $this->appendDebug($this->varDump($this->complexTypes[$name]));
1846 }
1847
1848 /**
1849 * adds a simple type to the schema
1850 *
1851 * @param string $name
1852 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1853 * @param string $typeClass (should always be simpleType)
1854 * @param string $phpType (should always be scalar)
1855 * @param array $enumeration array of values
1856 * @access public
1857 * @see xmlschema
1858 * @see getTypeDef
1859 */
1860 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1861 $this->simpleTypes[$name] = array(
1862 'name' => $name,
1863 'typeClass' => $typeClass,
1864 'phpType' => $phpType,
1865 'type' => $restrictionBase,
1866 'enumeration' => $enumeration
1867 );
1868
1869 $this->xdebug("addSimpleType $name:");
1870 $this->appendDebug($this->varDump($this->simpleTypes[$name]));
1871 }
1872
1873 /**
1874 * adds an element to the schema
1875 *
1876 * @param array $attrs attributes that must include name and type
1877 * @see xmlschema
1878 * @access public
1879 */
1880 function addElement($attrs) {
1881 if (! $this->getPrefix($attrs['type'])) {
1882 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1883 }
1884 $this->elements[ $attrs['name'] ] = $attrs;
1885 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1886
1887 $this->xdebug("addElement " . $attrs['name']);
1888 $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1889 }
1890 }
1891
1892
1893
1894 /**
1895 * For creating serializable abstractions of native PHP types. This class
1896 * allows element name/namespace, XSD type, and XML attributes to be
1897 * associated with a value. This is extremely useful when WSDL is not
1898 * used, but is also useful when WSDL is used with polymorphic types, including
1899 * xsd:anyType and user-defined types.
1900 *
1901 * @author Dietrich Ayala <dietrich@ganx4.com>
1902 * @version $Id$
1903 * @access public
1904 */
1905 class soapval extends nusoap_base {
1906 /**
1907 * The XML element name
1908 *
1909 * @var string
1910 * @access private
1911 */
1912 var $name;
1913 /**
1914 * The XML type name (string or false)
1915 *
1916 * @var mixed
1917 * @access private
1918 */
1919 var $type;
1920 /**
1921 * The PHP value
1922 *
1923 * @var mixed
1924 * @access private
1925 */
1926 var $value;
1927 /**
1928 * The XML element namespace (string or false)
1929 *
1930 * @var mixed
1931 * @access private
1932 */
1933 var $element_ns;
1934 /**
1935 * The XML type namespace (string or false)
1936 *
1937 * @var mixed
1938 * @access private
1939 */
1940 var $type_ns;
1941 /**
1942 * The XML element attributes (array or false)
1943 *
1944 * @var mixed
1945 * @access private
1946 */
1947 var $attributes;
1948
1949 /**
1950 * constructor
1951 *
1952 * @param string $name optional name
1953 * @param mixed $type optional type name
1954 * @param mixed $value optional value
1955 * @param mixed $element_ns optional namespace of value
1956 * @param mixed $type_ns optional namespace of type
1957 * @param mixed $attributes associative array of attributes to add to element serialization
1958 * @access public
1959 */
1960 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1961 parent::nusoap_base();
1962 $this->name = $name;
1963 $this->type = $type;
1964 $this->value = $value;
1965 $this->element_ns = $element_ns;
1966 $this->type_ns = $type_ns;
1967 $this->attributes = $attributes;
1968 }
1969
1970 /**
1971 * return serialized value
1972 *
1973 * @param string $use The WSDL use value (encoded|literal)
1974 * @return string XML data
1975 * @access public
1976 */
1977 function serialize($use='encoded') {
1978 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1979 }
1980
1981 /**
1982 * decodes a soapval object into a PHP native type
1983 *
1984 * @return mixed
1985 * @access public
1986 */
1987 function decode(){
1988 return $this->value;
1989 }
1990 }
1991
1992
1993
1994 /**
1995 * transport class for sending/receiving data via HTTP and HTTPS
1996 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1997 *
1998 * @author Dietrich Ayala <dietrich@ganx4.com>
1999 * @version $Id$
2000 * @access public
2001 */
2002 class soap_transport_http extends nusoap_base {
2003
2004 var $url = '';
2005 var $uri = '';
2006 var $digest_uri = '';
2007 var $scheme = '';
2008 var $host = '';
2009 var $port = '';
2010 var $path = '';
2011 var $request_method = 'POST';
2012 var $protocol_version = '1.0';
2013 var $encoding = '';
2014 var $outgoing_headers = array();
2015 var $incoming_headers = array();
2016 var $incoming_cookies = array();
2017 var $outgoing_payload = '';
2018 var $incoming_payload = '';
2019 var $useSOAPAction = true;
2020 var $persistentConnection = false;
2021 var $ch = false; // cURL handle
2022 var $username = '';
2023 var $password = '';
2024 var $authtype = '';
2025 var $digestRequest = array();
2026 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
2027 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2028 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2029 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2030 // passphrase: SSL key password/passphrase
2031 // verifypeer: default is 1
2032 // verifyhost: default is 1
2033
2034 /**
2035 * constructor
2036 */
2037 function soap_transport_http($url){
2038 parent::nusoap_base();
2039 $this->setURL($url);
2040 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2041 $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
2042 $this->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']);
2043 }
2044
2045 function setURL($url) {
2046 $this->url = $url;
2047
2048 $u = parse_url($url);
2049 foreach($u as $k => $v){
2050 $this->debug("$k = $v");
2051 $this->$k = $v;
2052 }
2053
2054 // add any GET params to path
2055 if(isset($u['query']) && $u['query'] != ''){
2056 $this->path .= '?' . $u['query'];
2057 }
2058
2059 // set default port
2060 if(!isset($u['port'])){
2061 if($u['scheme'] == 'https'){
2062 $this->port = 443;
2063 } else {
2064 $this->port = 80;
2065 }
2066 }
2067
2068 $this->uri = $this->path;
2069 $this->digest_uri = $this->uri;
2070
2071 // build headers
2072 if (!isset($u['port'])) {
2073 $this->outgoing_headers['Host'] = $this->host;
2074 } else {
2075 $this->outgoing_headers['Host'] = $this->host.':'.$this->port;
2076 }
2077 $this->debug('set Host: ' . $this->outgoing_headers['Host']);
2078
2079 if (isset($u['user']) && $u['user'] != '') {
2080 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2081 }
2082 }
2083
2084 function connect($connection_timeout=0,$response_timeout=30){
2085 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2086 // "regular" socket.
2087 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2088 // loaded), and until PHP5 stream_get_wrappers is not available.
2089 // if ($this->scheme == 'https') {
2090 // if (version_compare(phpversion(), '4.3.0') >= 0) {
2091 // if (extension_loaded('openssl')) {
2092 // $this->scheme = 'ssl';
2093 // $this->debug('Using SSL over OpenSSL');
2094 // }
2095 // }
2096 // }
2097 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2098 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2099 // use persistent connection
2100 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2101 if (!feof($this->fp)) {
2102 $this->debug('Re-use persistent connection');
2103 return true;
2104 }
2105 fclose($this->fp);
2106 $this->debug('Closed persistent connection at EOF');
2107 }
2108
2109 // munge host if using OpenSSL
2110 if ($this->scheme == 'ssl') {
2111 $host = 'ssl://' . $this->host;
2112 } else {
2113 $host = $this->host;
2114 }
2115 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2116
2117 // open socket
2118 if($connection_timeout > 0){
2119 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2120 } else {
2121 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2122 }
2123
2124 // test pointer
2125 if(!$this->fp) {
2126 $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2127 if ($this->errno) {
2128 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2129 } else {
2130 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
2131 }
2132 $this->debug($msg);
2133 $this->setError($msg);
2134 return false;
2135 }
2136
2137 // set response timeout
2138 $this->debug('set response timeout to ' . $response_timeout);
2139 socket_set_timeout( $this->fp, $response_timeout);
2140
2141 $this->debug('socket connected');
2142 return true;
2143 } else if ($this->scheme == 'https') {
2144 if (!extension_loaded('curl')) {
2145 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2146 return false;
2147 }
2148 $this->debug('connect using https');
2149 // init CURL
2150 $this->ch = curl_init();
2151 // set url
2152 $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
2153 // add path
2154 $hostURL .= $this->path;
2155 curl_setopt($this->ch, CURLOPT_URL, $hostURL);
2156 // follow location headers (re-directs)
2157 curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
2158 // ask for headers in the response output
2159 curl_setopt($this->ch, CURLOPT_HEADER, 1);
2160 // ask for the response output as the return value
2161 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
2162 // encode
2163 // We manage this ourselves through headers and encoding
2164 // if(function_exists('gzuncompress')){
2165 // curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
2166 // }
2167 // persistent connection
2168 if ($this->persistentConnection) {
2169 // The way we send data, we cannot use persistent connections, since
2170 // there will be some "junk" at the end of our request.
2171 //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
2172 $this->persistentConnection = false;
2173 $this->outgoing_headers['Connection'] = 'close';
2174 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2175 }
2176 // set timeout
2177 if ($connection_timeout != 0) {
2178 curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
2179 }
2180 // TODO: cURL has added a connection timeout separate from the response timeout
2181 //if ($connection_timeout != 0) {
2182 // curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2183 //}
2184 //if ($response_timeout != 0) {
2185 // curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);
2186 //}
2187
2188 // recent versions of cURL turn on peer/host checking by default,
2189 // while PHP binaries are not compiled with a default location for the
2190 // CA cert bundle, so disable peer/host checking.
2191 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2192 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
2193 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
2194
2195 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2196 if ($this->authtype == 'certificate') {
2197 if (isset($this->certRequest['cainfofile'])) {
2198 curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2199 }
2200 if (isset($this->certRequest['verifypeer'])) {
2201 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2202 } else {
2203 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
2204 }
2205 if (isset($this->certRequest['verifyhost'])) {
2206 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2207 } else {
2208 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
2209 }
2210 if (isset($this->certRequest['sslcertfile'])) {
2211 curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2212 }
2213 if (isset($this->certRequest['sslkeyfile'])) {
2214 curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2215 }
2216 if (isset($this->certRequest['passphrase'])) {
2217 curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $this->certRequest['passphrase']);
2218 }
2219 }
2220 $this->debug('cURL connection set up');
2221 return true;
2222 } else {
2223 $this->setError('Unknown scheme ' . $this->scheme);
2224 $this->debug('Unknown scheme ' . $this->scheme);
2225 return false;
2226 }
2227 }
2228
2229 /**
2230 * send the SOAP message via HTTP
2231 *
2232 * @param string $data message data
2233 * @param integer $timeout set connection timeout in seconds
2234 * @param integer $response_timeout set response timeout in seconds
2235 * @param array $cookies cookies to send
2236 * @return string data
2237 * @access public
2238 */
2239 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2240
2241 $this->debug('entered send() with data of length: '.strlen($data));
2242
2243 $this->tryagain = true;
2244 $tries = 0;
2245 while ($this->tryagain) {
2246 $this->tryagain = false;
2247 if ($tries++ < 2) {
2248 // make connnection
2249 if (!$this->connect($timeout, $response_timeout)){
2250 return false;
2251 }
2252
2253 // send request
2254 if (!$this->sendRequest($data, $cookies)){
2255 return false;
2256 }
2257
2258 // get response
2259 $respdata = $this->getResponse();
2260 } else {
2261 $this->setError('Too many tries to get an OK response');
2262 }
2263 }
2264 $this->debug('end of send()');
2265 return $respdata;
2266 }
2267
2268
2269 /**
2270 * send the SOAP message via HTTPS 1.0 using CURL
2271 *
2272 * @param string $msg message data
2273 * @param integer $timeout set connection timeout in seconds
2274 * @param integer $response_timeout set response timeout in seconds
2275 * @param array $cookies cookies to send
2276 * @return string data
2277 * @access public
2278 */
2279 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2280 return $this->send($data, $timeout, $response_timeout, $cookies);
2281 }
2282
2283 /**
2284 * if authenticating, set user credentials here
2285 *
2286 * @param string $username
2287 * @param string $password
2288 * @param string $authtype (basic, digest, certificate)
2289 * @param array $digestRequest (keys must be nonce, nc, realm, qop)
2290 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2291 * @access public
2292 */
2293 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2294 $this->debug("Set credentials for authtype $authtype");
2295 // cf. RFC 2617
2296 if ($authtype == 'basic') {
2297 $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
2298 } elseif ($authtype == 'digest') {
2299 if (isset($digestRequest['nonce'])) {
2300 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2301
2302 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2303
2304 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2305 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2306
2307 // H(A1) = MD5(A1)
2308 $HA1 = md5($A1);
2309
2310 // A2 = Method ":" digest-uri-value
2311 $A2 = 'POST:' . $this->digest_uri;
2312
2313 // H(A2)
2314 $HA2 = md5($A2);
2315
2316 // KD(secret, data) = H(concat(secret, ":", data))
2317 // if qop == auth:
2318 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
2319 // ":" nc-value
2320 // ":" unq(cnonce-value)
2321 // ":" unq(qop-value)
2322 // ":" H(A2)
2323 // ) <">
2324 // if qop is missing,
2325 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2326
2327 $unhashedDigest = '';
2328 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2329 $cnonce = $nonce;
2330 if ($digestRequest['qop'] != '') {
2331 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2332 } else {
2333 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2334 }
2335
2336 $hashedDigest = md5($unhashedDigest);
2337
2338 $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 . '"';
2339 }
2340 } elseif ($authtype == 'certificate') {
2341 $this->certRequest = $certRequest;
2342 }
2343 $this->username = $username;
2344 $this->password = $password;
2345 $this->authtype = $authtype;
2346 $this->digestRequest = $digestRequest;
2347
2348 if (isset($this->outgoing_headers['Authorization'])) {
2349 $this->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
2350 } else {
2351 $this->debug('Authorization header not set');
2352 }
2353 }
2354
2355 /**
2356 * set the soapaction value
2357 *
2358 * @param string $soapaction
2359 * @access public
2360 */
2361 function setSOAPAction($soapaction) {
2362 $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
2363 $this->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']);
2364 }
2365
2366 /**
2367 * use http encoding
2368 *
2369 * @param string $enc encoding style. supported values: gzip, deflate, or both
2370 * @access public
2371 */
2372 function setEncoding($enc='gzip, deflate') {
2373 if (function_exists('gzdeflate')) {
2374 $this->protocol_version = '1.1';
2375 $this->outgoing_headers['Accept-Encoding'] = $enc;
2376 $this->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']);
2377 if (!isset($this->outgoing_headers['Connection'])) {
2378 $this->outgoing_headers['Connection'] = 'close';
2379 $this->persistentConnection = false;
2380 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2381 }
2382 set_magic_quotes_runtime(0);
2383 // deprecated
2384 $this->encoding = $enc;
2385 }
2386 }
2387
2388 /**
2389 * set proxy info here
2390 *
2391 * @param string $proxyhost
2392 * @param string $proxyport
2393 * @param string $proxyusername
2394 * @param string $proxypassword
2395 * @access public
2396 */
2397 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
2398 $this->uri = $this->url;
2399 $this->host = $proxyhost;
2400 $this->port = $proxyport;
2401 if ($proxyusername != '' && $proxypassword != '') {
2402 $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
2403 $this->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']);
2404 }
2405 }
2406
2407 /**
2408 * decode a string that is encoded w/ "chunked' transfer encoding
2409 * as defined in RFC2068 19.4.6
2410 *
2411 * @param string $buffer
2412 * @param string $lb
2413 * @returns string
2414 * @access public
2415 * @deprecated since at least TYPO3 4.3, will be removed in TYPO3 4.6.
2416 */
2417 function decodeChunked($buffer, $lb){
2418 t3lib_div::logDeprecatedFunction();
2419
2420 // length := 0
2421 $length = 0;
2422 $new = '';
2423
2424 // read chunk-size, chunk-extension (if any) and CRLF
2425 // get the position of the linebreak
2426 $chunkend = strpos($buffer, $lb);
2427 if ($chunkend == FALSE) {
2428 $this->debug('no linebreak found in decodeChunked');
2429 return $new;
2430 }
2431 $temp = substr($buffer,0,$chunkend);
2432 $chunk_size = hexdec( trim($temp) );
2433 $chunkstart = $chunkend + strlen($lb);
2434 // while (chunk-size > 0) {
2435 while ($chunk_size > 0) {
2436 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2437 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2438
2439 // Just in case we got a broken connection
2440 if ($chunkend == FALSE) {
2441 $chunk = substr($buffer,$chunkstart);
2442 // append chunk-data to entity-body
2443 $new .= $chunk;
2444 $length += strlen($chunk);
2445 break;
2446 }
2447
2448 // read chunk-data and CRLF
2449 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2450 // append chunk-data to entity-body
2451 $new .= $chunk;
2452 // length := length + chunk-size
2453 $length += strlen($chunk);
2454 // read chunk-size and CRLF
2455 $chunkstart = $chunkend + strlen($lb);
2456
2457 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2458 if ($chunkend == FALSE) {
2459 break; //Just in case we got a broken connection
2460 }
2461 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2462 $chunk_size = hexdec( trim($temp) );
2463 $chunkstart = $chunkend;
2464 }
2465 return $new;
2466 }
2467
2468 /*
2469 * Writes payload, including HTTP headers, to $this->outgoing_payload.
2470 */
2471 function buildPayload($data, $cookie_str = '') {
2472 // add content-length header
2473 $this->outgoing_headers['Content-Length'] = strlen($data);
2474 $this->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']);
2475
2476 // start building outgoing payload:
2477 $req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2478 $this->debug("HTTP request: $req");
2479 $this->outgoing_payload = "$req\r\n";
2480
2481 // loop thru headers, serializing
2482 foreach($this->outgoing_headers as $k => $v){
2483 $hdr = $k.': '.$v;
2484 $this->debug("HTTP header: $hdr");
2485 $this->outgoing_payload .= "$hdr\r\n";
2486 }
2487
2488 // add any cookies
2489 if ($cookie_str != '') {
2490 $hdr = 'Cookie: '.$cookie_str;
2491 $this->debug("HTTP header: $hdr");
2492 $this->outgoing_payload .= "$hdr\r\n";
2493 }
2494
2495 // header/body separator
2496 $this->outgoing_payload .= "\r\n";
2497
2498 // add data
2499 $this->outgoing_payload .= $data;
2500 }
2501
2502 function sendRequest($data, $cookies = NULL) {
2503 // build cookie string
2504 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2505
2506 // build payload
2507 $this->buildPayload($data, $cookie_str);
2508
2509 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2510 // send payload
2511 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2512 $this->setError('couldn\'t write message data to socket');
2513 $this->debug('couldn\'t write message data to socket');
2514 return false;
2515 }
2516 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2517 return true;
2518 } else if ($this->scheme == 'https') {
2519 // set payload
2520 // TODO: cURL does say this should only be the verb, and in fact it
2521 // turns out that the URI and HTTP version are appended to this, which
2522 // some servers refuse to work with
2523 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2524 foreach($this->outgoing_headers as $k => $v){
2525 $curl_headers[] = "$k: $v";
2526 }
2527 if ($cookie_str != '') {
2528 $curl_headers[] = 'Cookie: ' . $cookie_str;
2529 }
2530 curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
2531 if ($this->request_method == "POST") {
2532 curl_setopt($this->ch, CURLOPT_POST, 1);
2533 curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
2534 } else {
2535 }
2536 $this->debug('set cURL payload');
2537 return true;
2538 }
2539 }
2540
2541 function getResponse(){
2542 $this->incoming_payload = '';
2543
2544 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2545 // loop until headers have been retrieved
2546 $data = '';
2547 while (!isset($lb)){
2548
2549 // We might EOF during header read.
2550 if(feof($this->fp)) {
2551 $this->incoming_payload = $data;
2552 $this->debug('found no headers before EOF after length ' . strlen($data));
2553 $this->debug("received before EOF:\n" . $data);
2554 $this->setError('server failed to send headers');
2555 return false;
2556 }
2557
2558 $tmp = fgets($this->fp, 256);
2559 $tmplen = strlen($tmp);
2560 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2561
2562 if ($tmplen == 0) {
2563 $this->incoming_payload = $data;
2564 $this->debug('socket read of headers timed out after length ' . strlen($data));
2565 $this->debug("read before timeout: " . $data);
2566 $this->setError('socket read of headers timed out');
2567 return false;
2568 }
2569
2570 $data .= $tmp;
2571 $pos = strpos($data,"\r\n\r\n");
2572 if($pos > 1){
2573 $lb = "\r\n";
2574 } else {
2575 $pos = strpos($data,"\n\n");
2576 if($pos > 1){
2577 $lb = "\n";
2578 }
2579 }
2580 // remove 100 header
2581 if(isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)){
2582 unset($lb);
2583 $data = '';
2584 }//
2585 }
2586 // store header data
2587 $this->incoming_payload .= $data;
2588 $this->debug('found end of headers after length ' . strlen($data));
2589 // process headers
2590 $header_data = trim(substr($data,0,$pos));
2591 $header_array = explode($lb,$header_data);
2592 $this->incoming_headers = array();
2593 $this->incoming_cookies = array();
2594 foreach($header_array as $header_line){
2595 $arr = explode(':',$header_line, 2);
2596 if(count($arr) > 1){
2597 $header_name = strtolower(trim($arr[0]));
2598 $this->incoming_headers[$header_name] = trim($arr[1]);
2599 if ($header_name == 'set-cookie') {
2600 // TODO: allow multiple cookies from parseCookie
2601 $cookie = $this->parseCookie(trim($arr[1]));
2602 if ($cookie) {
2603 $this->incoming_cookies[] = $cookie;
2604 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2605 } else {
2606 $this->debug('did not find cookie in ' . trim($arr[1]));
2607 }
2608 }
2609 } else if (isset($header_name)) {
2610 // append continuation line to previous header
2611 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2612 }
2613 }
2614
2615 // loop until msg has been received
2616 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2617 $content_length = 2147483647; // ignore any content-length header
2618 $chunked = true;
2619 $this->debug("want to read chunked content");
2620 } elseif (isset($this->incoming_headers['content-length'])) {
2621 $content_length = $this->incoming_headers['content-length'];
2622 $chunked = false;
2623 $this->debug("want to read content of length $content_length");
2624 } else {
2625 $content_length = 2147483647;
2626 $chunked = false;
2627 $this->debug("want to read content to EOF");
2628 }
2629 $data = '';
2630 do {
2631 if ($chunked) {
2632 $tmp = fgets($this->fp, 256);
2633 $tmplen = strlen($tmp);
2634 $this->debug("read chunk line of $tmplen bytes");
2635 if ($tmplen == 0) {
2636 $this->incoming_payload = $data;
2637 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2638 $this->debug("read before timeout:\n" . $data);
2639 $this->setError('socket read of chunk length timed out');
2640 return false;
2641 }
2642 $content_length = hexdec(trim($tmp));
2643 $this->debug("chunk length $content_length");
2644 }
2645 $strlen = 0;
2646 while (($strlen < $content_length) && (!feof($this->fp))) {
2647 $readlen = min(8192, $content_length - $strlen);
2648 $tmp = fread($this->fp, $readlen);
2649 $tmplen = strlen($tmp);
2650 $this->debug("read buffer of $tmplen bytes");
2651 if (($tmplen == 0) && (!feof($this->fp))) {
2652 $this->incoming_payload = $data;
2653 $this->debug('socket read of body timed out after length ' . strlen($data));
2654 $this->debug("read before timeout:\n" . $data);
2655 $this->setError('socket read of body timed out');
2656 return false;
2657 }
2658 $strlen += $tmplen;
2659 $data .= $tmp;
2660 }
2661 if ($chunked && ($content_length > 0)) {
2662 $tmp = fgets($this->fp, 256);
2663 $tmplen = strlen($tmp);
2664 $this->debug("read chunk terminator of $tmplen bytes");
2665 if ($tmplen == 0) {
2666 $this->incoming_payload = $data;
2667 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2668 $this->debug("read before timeout:\n" . $data);
2669 $this->setError('socket read of chunk terminator timed out');
2670 return false;
2671 }
2672 }
2673 } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
2674 if (feof($this->fp)) {
2675 $this->debug('read to EOF');
2676 }
2677 $this->debug('read body of length ' . strlen($data));
2678 $this->incoming_payload .= $data;
2679 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2680
2681 // close filepointer
2682 if(
2683 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
2684 (! $this->persistentConnection) || feof($this->fp)){
2685 fclose($this->fp);
2686 $this->fp = false;
2687 $this->debug('closed socket');
2688 }
2689
2690 // connection was closed unexpectedly
2691 if($this->incoming_payload == ''){
2692 $this->setError('no response from server');
2693 return false;
2694 }
2695
2696 // decode transfer-encoding
2697 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2698 // if(!$data = $this->decodeChunked($data, $lb)){
2699 // $this->setError('Decoding of chunked data failed');
2700 // return false;
2701 // }
2702 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2703 // set decoded payload
2704 // $this->incoming_payload = $header_data.$lb.$lb.$data;
2705 // }
2706
2707 } else if ($this->scheme == 'https') {
2708 // send and receive
2709 $this->debug('send and receive with cURL');
2710 $this->incoming_payload = curl_exec($this->ch);
2711 $data = $this->incoming_payload;
2712
2713 $cErr = curl_error($this->ch);
2714 if ($cErr != '') {
2715 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
2716 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
2717 foreach(curl_getinfo($this->ch) as $k => $v){
2718 $err .= "$k: $v<br>";
2719 }
2720 $this->debug($err);
2721 $this->setError($err);
2722 curl_close($this->ch);
2723 return false;
2724 } else {
2725 //echo '<pre>';
2726 //var_dump(curl_getinfo($this->ch));
2727 //echo '</pre>';
2728 }
2729 // close curl
2730 $this->debug('No cURL error, closing cURL');
2731 curl_close($this->ch);
2732
2733 // remove 100 header(s)
2734 while (preg_match('/^HTTP\/1.1 100/',$data)) {
2735 if ($pos = strpos($data,"\r\n\r\n")) {
2736 $data = ltrim(substr($data,$pos));
2737 } elseif($pos = strpos($data,"\n\n") ) {
2738 $data = ltrim(substr($data,$pos));
2739 }
2740 }
2741
2742 // separate content from HTTP headers
2743 if ($pos = strpos($data,"\r\n\r\n")) {
2744 $lb = "\r\n";
2745 } elseif( $pos = strpos($data,"\n\n")) {
2746 $lb = "\n";
2747 } else {
2748 $this->debug('no proper separation of headers and document');
2749 $this->setError('no proper separation of headers and document');
2750 return false;
2751 }
2752 $header_data = trim(substr($data,0,$pos));
2753 $header_array = explode($lb,$header_data);
2754 $data = ltrim(substr($data,$pos));
2755 $this->debug('found proper separation of headers and document');
2756 $this->debug('cleaned data, stringlen: '.strlen($data));
2757 // clean headers
2758 foreach ($header_array as $header_line) {
2759 $arr = explode(':',$header_line,2);
2760 if(count($arr) > 1){
2761 $header_name = strtolower(trim($arr[0]));
2762 $this->incoming_headers[$header_name] = trim($arr[1]);
2763 if ($header_name == 'set-cookie') {
2764 // TODO: allow multiple cookies from parseCookie
2765 $cookie = $this->parseCookie(trim($arr[1]));
2766 if ($cookie) {
2767 $this->incoming_cookies[] = $cookie;
2768 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2769 } else {
2770 $this->debug('did not find cookie in ' . trim($arr[1]));
2771 }
2772 }
2773 } else if (isset($header_name)) {
2774 // append continuation line to previous header
2775 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2776 }
2777 }
2778 }
2779
2780 $arr = explode(' ', $header_array[0], 3);
2781 $http_version = $arr[0];
2782 $http_status = intval($arr[1]);
2783 $http_reason = count($arr) > 2 ? $arr[2] : '';
2784
2785 // see if we need to resend the request with http digest authentication
2786 if (isset($this->incoming_headers['location']) && $http_status == 301) {
2787 $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers['location']);
2788 $this->setURL($this->incoming_headers['location']);
2789 $this->tryagain = true;
2790 return false;
2791 }
2792
2793 // see if we need to resend the request with http digest authentication
2794 if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
2795 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
2796 if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
2797 $this->debug('Server wants digest authentication');
2798 // remove "Digest " from our elements
2799 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2800
2801 // parse elements into array
2802 $digestElements = explode(',', $digestString);
2803 foreach ($digestElements as $val) {
2804 $tempElement = explode('=', trim($val), 2);
2805 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2806 }
2807
2808 // should have (at least) qop, realm, nonce
2809 if (isset($digestRequest['nonce'])) {
2810 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2811 $this->tryagain = true;
2812 return false;
2813 }
2814 }
2815 $this->debug('HTTP authentication failed');
2816 $this->setError('HTTP authentication failed');
2817 return false;
2818 }
2819
2820 if (
2821 ($http_status >= 300 && $http_status <= 307) ||
2822 ($http_status >= 400 && $http_status <= 417) ||
2823 ($http_status >= 501 && $http_status <= 505)
2824 ) {
2825 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
2826 return false;
2827 }
2828
2829 // decode content-encoding
2830 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2831 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2832 // if decoding works, use it. else assume data wasn't gzencoded
2833 if(function_exists('gzinflate')){
2834 //$timer->setMarker('starting decoding of gzip/deflated content');
2835 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2836 // this means there are no Zlib headers, although there should be
2837 $this->debug('The gzinflate function exists');
2838 $datalen = strlen($data);
2839 if ($this->incoming_headers['content-encoding'] == 'deflate') {
2840 if ($degzdata = @gzinflate($data)) {
2841 $data = $degzdata;
2842 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2843 if (strlen($data) < $datalen) {
2844 // test for the case that the payload has been compressed twice
2845 $this->debug('The inflated payload is smaller than the gzipped one; try again');
2846 if ($degzdata = @gzinflate($data)) {
2847 $data = $degzdata;
2848 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2849 }
2850 }
2851 } else {
2852 $this->debug('Error using gzinflate to inflate the payload');
2853 $this->setError('Error using gzinflate to inflate the payload');
2854 }
2855 } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
2856 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
2857 $data = $degzdata;
2858 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2859 if (strlen($data) < $datalen) {
2860 // test for the case that the payload has been compressed twice
2861 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2862 if ($degzdata = @gzinflate(substr($data, 10))) {
2863 $data = $degzdata;
2864 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2865 }
2866 }
2867 } else {
2868 $this->debug('Error using gzinflate to un-gzip the payload');
2869 $this->setError('Error using gzinflate to un-gzip the payload');
2870 }
2871 }
2872 //$timer->setMarker('finished decoding of gzip/deflated content');
2873 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2874 // set decoded payload
2875 $this->incoming_payload = $header_data.$lb.$lb.$data;
2876 } else {
2877 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2878 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2879 }
2880 } else {
2881 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2882 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2883 }
2884 } else {
2885 $this->debug('No Content-Encoding header');
2886 }
2887
2888 if(strlen($data) == 0){
2889 $this->debug('no data after headers!');
2890 $this->setError('no data present after HTTP headers');
2891 return false;
2892 }
2893
2894 return $data;
2895 }
2896
2897 function setContentType($type, $charset = false) {
2898 $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2899 $this->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']);
2900 }
2901
2902 function usePersistentConnection(){
2903 if (isset($this->outgoing_headers['Accept-Encoding'])) {
2904 return false;
2905 }
2906 $this->protocol_version = '1.1';
2907 $this->persistentConnection = true;
2908 $this->outgoing_headers['Connection'] = 'Keep-Alive';
2909 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2910 return true;
2911 }
2912
2913 /**
2914 * parse an incoming Cookie into it's parts
2915 *
2916 * @param string $cookie_str content of cookie
2917 * @return array with data of that cookie
2918 * @access private
2919 */
2920 /*
2921 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
2922 */
2923 function parseCookie($cookie_str) {
2924 $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
2925 $data = explode(';', $cookie_str);
2926 $value_str = $data[0];
2927
2928 $cookie_param = 'domain=';
2929 $start = strpos($cookie_str, $cookie_param);
2930 if ($start > 0) {
2931 $domain = substr($cookie_str, $start + strlen($cookie_param));
2932 $domain = substr($domain, 0, strpos($domain, ';'));
2933 } else {
2934 $domain = '';
2935 }
2936
2937 $cookie_param = 'expires=';
2938 $start = strpos($cookie_str, $cookie_param);
2939 if ($start > 0) {
2940 $expires = substr($cookie_str, $start + strlen($cookie_param));
2941 $expires = substr($expires, 0, strpos($expires, ';'));
2942 } else {
2943 $expires = '';
2944 }
2945
2946 $cookie_param = 'path=';
2947 $start = strpos($cookie_str, $cookie_param);
2948 if ( $start > 0 ) {
2949 $path = substr($cookie_str, $start + strlen($cookie_param));
2950 $path = substr($path, 0, strpos($path, ';'));
2951 } else {
2952 $path = '/';
2953 }
2954
2955 $cookie_param = ';secure;';
2956 if (strpos($cookie_str, $cookie_param) !== FALSE) {
2957 $secure = true;
2958 } else {
2959 $secure = false;
2960 }
2961
2962 $sep_pos = strpos($value_str, '=');
2963
2964 if ($sep_pos) {
2965 $name = substr($value_str, 0, $sep_pos);
2966 $value = substr($value_str, $sep_pos + 1);
2967 $cookie= array( 'name' => $name,
2968 'value' => $value,
2969 'domain' => $domain,
2970 'path' => $path,
2971 'expires' => $expires,
2972 'secure' => $secure
2973 );