PageRenderTime 88ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/gforge/www/soap/nusoap.php

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