soap_transport_http

Transport class for sending/receiving data via HTTP and HTTPS NOTE: PHP must be compiled with the CURL extension for HTTPS support.

Defined (2)

The class is defined in the following location(s).

/lib/class.soap_transport_http.php  
  1. class soap_transport_http extends nusoap_base { 
  2.  
  3. var $url = ''; 
  4. var $uri = ''; 
  5. var $digest_uri = ''; 
  6. var $scheme = ''; 
  7. var $host = ''; 
  8. var $port = ''; 
  9. var $path = ''; 
  10. var $request_method = 'POST'; 
  11. var $protocol_version = '1.0'; 
  12. var $encoding = ''; 
  13. var $outgoing_headers = array(); 
  14. var $incoming_headers = array(); 
  15. var $incoming_cookies = array(); 
  16. var $outgoing_payload = ''; 
  17. var $incoming_payload = ''; 
  18. var $response_status_line; // HTTP response status line 
  19. var $useSOAPAction = true; 
  20. var $persistentConnection = false; 
  21. var $ch = false; // cURL handle 
  22. var $ch_options = array(); // cURL custom options 
  23. var $use_curl = false; // force cURL use 
  24. var $proxy = null; // proxy information (associative array) 
  25. var $username = ''; 
  26. var $password = ''; 
  27. var $authtype = ''; 
  28. var $digestRequest = array(); 
  29. var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional) 
  30. // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem' 
  31. // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem' 
  32. // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem' 
  33. // passphrase: SSL key password/passphrase 
  34. // certpassword: SSL certificate password 
  35. // verifypeer: default is 1 
  36. // verifyhost: default is 1 
  37.  
  38. /** 
  39. * constructor 
  40. * @param string $url The URL to which to connect 
  41. * @param array $curl_options User-specified cURL options 
  42. * @param boolean $use_curl Whether to try to force cURL use 
  43. * @access public 
  44. */ 
  45. function soap_transport_http($url, $curl_options = NULL, $use_curl = false) { 
  46. parent::nusoap_base(); 
  47. $this->debug("ctor url=$url use_curl=$use_curl curl_options:"); 
  48. $this->appendDebug($this->varDump($curl_options)); 
  49. $this->setURL($url); 
  50. if (is_array($curl_options)) { 
  51. $this->ch_options = $curl_options; 
  52. $this->use_curl = $use_curl; 
  53. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); 
  54. $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')'); 
  55.  
  56. /** 
  57. * sets a cURL option 
  58. * @param mixed $option The cURL option (always integer?) 
  59. * @param mixed $value The cURL option value 
  60. * @access private 
  61. */ 
  62. function setCurlOption($option, $value) { 
  63. $this->debug("setCurlOption option=$option, value="); 
  64. $this->appendDebug($this->varDump($value)); 
  65. curl_setopt($this->ch, $option, $value); 
  66.  
  67. /** 
  68. * sets an HTTP header 
  69. * @param string $name The name of the header 
  70. * @param string $value The value of the header 
  71. * @access private 
  72. */ 
  73. function setHeader($name, $value) { 
  74. $this->outgoing_headers[$name] = $value; 
  75. $this->debug("set header $name: $value"); 
  76.  
  77. /** 
  78. * unsets an HTTP header 
  79. * @param string $name The name of the header 
  80. * @access private 
  81. */ 
  82. function unsetHeader($name) { 
  83. if (isset($this->outgoing_headers[$name])) { 
  84. $this->debug("unset header $name"); 
  85. unset($this->outgoing_headers[$name]); 
  86.  
  87. /** 
  88. * sets the URL to which to connect 
  89. * @param string $url The URL to which to connect 
  90. * @access private 
  91. */ 
  92. function setURL($url) { 
  93. $this->url = $url; 
  94.  
  95. $u = parse_url($url); 
  96. foreach($u as $k => $v) { 
  97. $this->debug("parsed URL $k = $v"); 
  98. $this->$k = $v; 
  99.  
  100. // add any GET params to path 
  101. if(isset($u['query']) && $u['query'] != '') { 
  102. $this->path .= '?' . $u['query']; 
  103.  
  104. // set default port 
  105. if(!isset($u['port'])) { 
  106. if($u['scheme'] == 'https') { 
  107. $this->port = 443; 
  108. } else { 
  109. $this->port = 80; 
  110.  
  111. $this->uri = $this->path; 
  112. $this->digest_uri = $this->uri; 
  113.  
  114. // build headers 
  115. if (!isset($u['port'])) { 
  116. $this->setHeader('Host', $this->host); 
  117. } else { 
  118. $this->setHeader('Host', $this->host.':'.$this->port); 
  119.  
  120. if (isset($u['user']) && $u['user'] != '') { 
  121. $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : ''); 
  122.  
  123. /** 
  124. * gets the I/O method to use 
  125. * @return string I/O method to use (socket|curl|unknown) 
  126. * @access private 
  127. */ 
  128. function io_method() { 
  129. if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) 
  130. return 'curl'; 
  131. if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) 
  132. return 'socket'; 
  133. return 'unknown'; 
  134.  
  135. /** 
  136. * establish an HTTP connection 
  137. * @param integer $timeout set connection timeout in seconds 
  138. * @param integer $response_timeout set response timeout in seconds 
  139. * @return boolean true if connected, false if not 
  140. * @access private 
  141. */ 
  142. function connect($connection_timeout=0, $response_timeout=30) { 
  143. // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like 
  144. // "regular" socket. 
  145. // TODO: disabled for now because OpenSSL must be *compiled* in (not just 
  146. // loaded), and until PHP5 stream_get_wrappers is not available. 
  147. // if ($this->scheme == 'https') { 
  148. // if (version_compare(phpversion(), '4.3.0') >= 0) { 
  149. // if (extension_loaded('openssl')) { 
  150. // $this->scheme = 'ssl'; 
  151. // $this->debug('Using SSL over OpenSSL'); 
  152. // } 
  153. // } 
  154. // } 
  155. $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port"); 
  156. if ($this->io_method() == 'socket') { 
  157. if (!is_array($this->proxy)) { 
  158. $host = $this->host; 
  159. $port = $this->port; 
  160. } else { 
  161. $host = $this->proxy['host']; 
  162. $port = $this->proxy['port']; 
  163.  
  164. // use persistent connection 
  165. if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) { 
  166. if (!feof($this->fp)) { 
  167. $this->debug('Re-use persistent connection'); 
  168. return true; 
  169. fclose($this->fp); 
  170. $this->debug('Closed persistent connection at EOF'); 
  171.  
  172. // munge host if using OpenSSL 
  173. if ($this->scheme == 'ssl') { 
  174. $host = 'ssl://' . $host; 
  175. $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout); 
  176.  
  177. // open socket 
  178. if($connection_timeout > 0) { 
  179. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); 
  180. } else { 
  181. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); 
  182.  
  183. // test pointer 
  184. if(!$this->fp) { 
  185. $msg = 'Couldn\'t open socket connection to server ' . $this->url; 
  186. if ($this->errno) { 
  187. $msg .= ', Error ('.$this->errno.'): '.$this->error_str; 
  188. } else { 
  189. $msg .= ' prior to connect(). This is often a problem looking up the host name.'; 
  190. $this->debug($msg); 
  191. $this->setError($msg); 
  192. return false; 
  193.  
  194. // set response timeout 
  195. $this->debug('set response timeout to ' . $response_timeout); 
  196. socket_set_timeout( $this->fp, $response_timeout); 
  197.  
  198. $this->debug('socket connected'); 
  199. return true; 
  200. } else if ($this->io_method() == 'curl') { 
  201. if (!extension_loaded('curl')) { 
  202. // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); 
  203. $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.'); 
  204. return false; 
  205. // Avoid warnings when PHP does not have these options 
  206. if (defined('CURLOPT_CONNECTIONTIMEOUT')) 
  207. $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT; 
  208. else 
  209. $CURLOPT_CONNECTIONTIMEOUT = 78; 
  210. if (defined('CURLOPT_HTTPAUTH')) 
  211. $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH; 
  212. else 
  213. $CURLOPT_HTTPAUTH = 107; 
  214. if (defined('CURLOPT_PROXYAUTH')) 
  215. $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH; 
  216. else 
  217. $CURLOPT_PROXYAUTH = 111; 
  218. if (defined('CURLAUTH_BASIC')) 
  219. $CURLAUTH_BASIC = CURLAUTH_BASIC; 
  220. else 
  221. $CURLAUTH_BASIC = 1; 
  222. if (defined('CURLAUTH_DIGEST')) 
  223. $CURLAUTH_DIGEST = CURLAUTH_DIGEST; 
  224. else 
  225. $CURLAUTH_DIGEST = 2; 
  226. if (defined('CURLAUTH_NTLM')) 
  227. $CURLAUTH_NTLM = CURLAUTH_NTLM; 
  228. else 
  229. $CURLAUTH_NTLM = 8; 
  230.  
  231. $this->debug('connect using cURL'); 
  232. // init CURL 
  233. $this->ch = curl_init(); 
  234. // set url 
  235. $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host"; 
  236. // add path 
  237. $hostURL .= $this->path; 
  238. $this->setCurlOption(CURLOPT_URL, $hostURL); 
  239. // follow location headers (re-directs) 
  240. if (ini_get('safe_mode') || ini_get('open_basedir')) { 
  241. $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION'); 
  242. $this->debug('safe_mode = '); 
  243. $this->appendDebug($this->varDump(ini_get('safe_mode'))); 
  244. $this->debug('open_basedir = '); 
  245. $this->appendDebug($this->varDump(ini_get('open_basedir'))); 
  246. } else { 
  247. $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1); 
  248. // ask for headers in the response output 
  249. $this->setCurlOption(CURLOPT_HEADER, 1); 
  250. // ask for the response output as the return value 
  251. $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1); 
  252. // encode 
  253. // We manage this ourselves through headers and encoding 
  254. // if(function_exists('gzuncompress')) { 
  255. // $this->setCurlOption(CURLOPT_ENCODING, 'deflate'); 
  256. // } 
  257. // persistent connection 
  258. if ($this->persistentConnection) { 
  259. // I believe the following comment is now bogus, having applied to 
  260. // the code when it used CURLOPT_CUSTOMREQUEST to send the request. 
  261. // The way we send data, we cannot use persistent connections, since 
  262. // there will be some "junk" at the end of our request. 
  263. //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true); 
  264. $this->persistentConnection = false; 
  265. $this->setHeader('Connection', 'close'); 
  266. // set timeouts 
  267. if ($connection_timeout != 0) { 
  268. $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout); 
  269. if ($response_timeout != 0) { 
  270. $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout); 
  271.  
  272. if ($this->scheme == 'https') { 
  273. $this->debug('set cURL SSL verify options'); 
  274. // recent versions of cURL turn on peer/host checking by default,  
  275. // while PHP binaries are not compiled with a default location for the 
  276. // CA cert bundle, so disable peer/host checking. 
  277. //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');  
  278. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0); 
  279. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0); 
  280.  
  281. // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo) 
  282. if ($this->authtype == 'certificate') { 
  283. $this->debug('set cURL certificate options'); 
  284. if (isset($this->certRequest['cainfofile'])) { 
  285. $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']); 
  286. if (isset($this->certRequest['verifypeer'])) { 
  287. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']); 
  288. } else { 
  289. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1); 
  290. if (isset($this->certRequest['verifyhost'])) { 
  291. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']); 
  292. } else { 
  293. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1); 
  294. if (isset($this->certRequest['sslcertfile'])) { 
  295. $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']); 
  296. if (isset($this->certRequest['sslkeyfile'])) { 
  297. $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']); 
  298. if (isset($this->certRequest['passphrase'])) { 
  299. $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']); 
  300. if (isset($this->certRequest['certpassword'])) { 
  301. $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']); 
  302. if ($this->authtype && ($this->authtype != 'certificate')) { 
  303. if ($this->username) { 
  304. $this->debug('set cURL username/password'); 
  305. $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password"); 
  306. if ($this->authtype == 'basic') { 
  307. $this->debug('set cURL for Basic authentication'); 
  308. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC); 
  309. if ($this->authtype == 'digest') { 
  310. $this->debug('set cURL for digest authentication'); 
  311. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST); 
  312. if ($this->authtype == 'ntlm') { 
  313. $this->debug('set cURL for NTLM authentication'); 
  314. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM); 
  315. if (is_array($this->proxy)) { 
  316. $this->debug('set cURL proxy options'); 
  317. if ($this->proxy['port'] != '') { 
  318. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']); 
  319. } else { 
  320. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']); 
  321. if ($this->proxy['username'] || $this->proxy['password']) { 
  322. $this->debug('set cURL proxy authentication options'); 
  323. $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']); 
  324. if ($this->proxy['authtype'] == 'basic') { 
  325. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC); 
  326. if ($this->proxy['authtype'] == 'ntlm') { 
  327. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM); 
  328. $this->debug('cURL connection set up'); 
  329. return true; 
  330. } else { 
  331. $this->setError('Unknown scheme ' . $this->scheme); 
  332. $this->debug('Unknown scheme ' . $this->scheme); 
  333. return false; 
  334.  
  335. /** 
  336. * sends the SOAP request and gets the SOAP response via HTTP[S] 
  337. * @param string $data message data 
  338. * @param integer $timeout set connection timeout in seconds 
  339. * @param integer $response_timeout set response timeout in seconds 
  340. * @param array $cookies cookies to send 
  341. * @return string data 
  342. * @access public 
  343. */ 
  344. function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) { 
  345.  
  346. $this->debug('entered send() with data of length: '.strlen($data)); 
  347.  
  348. $this->tryagain = true; 
  349. $tries = 0; 
  350. while ($this->tryagain) { 
  351. $this->tryagain = false; 
  352. if ($tries++ < 2) { 
  353. // make connnection 
  354. if (!$this->connect($timeout, $response_timeout)) { 
  355. return false; 
  356.  
  357. // send request 
  358. if (!$this->sendRequest($data, $cookies)) { 
  359. return false; 
  360.  
  361. // get response 
  362. $respdata = $this->getResponse(); 
  363. } else { 
  364. $this->setError("Too many tries to get an OK response ($this->response_status_line)"); 
  365. }  
  366. $this->debug('end of send()'); 
  367. return $respdata; 
  368.  
  369.  
  370. /** 
  371. * sends the SOAP request and gets the SOAP response via HTTPS using CURL 
  372. * @param string $data message data 
  373. * @param integer $timeout set connection timeout in seconds 
  374. * @param integer $response_timeout set response timeout in seconds 
  375. * @param array $cookies cookies to send 
  376. * @return string data 
  377. * @access public 
  378. * @deprecated 
  379. */ 
  380. function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) { 
  381. return $this->send($data, $timeout, $response_timeout, $cookies); 
  382.  
  383. /** 
  384. * if authenticating, set user credentials here 
  385. * @param string $username 
  386. * @param string $password 
  387. * @param string $authtype (basic|digest|certificate|ntlm) 
  388. * @param array $digestRequest (keys must be nonce, nc, realm, qop) 
  389. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) 
  390. * @access public 
  391. */ 
  392. function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) { 
  393. $this->debug("setCredentials username=$username authtype=$authtype digestRequest="); 
  394. $this->appendDebug($this->varDump($digestRequest)); 
  395. $this->debug("certRequest="); 
  396. $this->appendDebug($this->varDump($certRequest)); 
  397. // cf. RFC 2617 
  398. if ($authtype == 'basic') { 
  399. $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':', '', $username).':'.$password)); 
  400. } elseif ($authtype == 'digest') { 
  401. if (isset($digestRequest['nonce'])) { 
  402. $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; 
  403.  
  404. // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) 
  405.  
  406. // A1 = unq(username-value) ":" unq(realm-value) ":" passwd 
  407. $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password; 
  408.  
  409. // H(A1) = MD5(A1) 
  410. $HA1 = md5($A1); 
  411.  
  412. // A2 = Method ":" digest-uri-value 
  413. $A2 = $this->request_method . ':' . $this->digest_uri; 
  414.  
  415. // H(A2) 
  416. $HA2 = md5($A2); 
  417.  
  418. // KD(secret, data) = H(concat(secret, ":", data)) 
  419. // if qop == auth: 
  420. // request-digest = <"> < KD ( H(A1), unq(nonce-value) 
  421. // ":" nc-value 
  422. // ":" unq(cnonce-value) 
  423. // ":" unq(qop-value) 
  424. // ":" H(A2) 
  425. // ) <"> 
  426. // if qop is missing,  
  427. // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> 
  428.  
  429. $unhashedDigest = ''; 
  430. $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; 
  431. $cnonce = $nonce; 
  432. if ($digestRequest['qop'] != '') { 
  433. $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; 
  434. } else { 
  435. $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; 
  436.  
  437. $hashedDigest = md5($unhashedDigest); 
  438.  
  439. $opaque = '';  
  440. if (isset($digestRequest['opaque'])) { 
  441. $opaque = ', opaque="' . $digestRequest['opaque'] . '"'; 
  442.  
  443. $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'); 
  444. } elseif ($authtype == 'certificate') { 
  445. $this->certRequest = $certRequest; 
  446. $this->debug('Authorization header not set for certificate'); 
  447. } elseif ($authtype == 'ntlm') { 
  448. // do nothing 
  449. $this->debug('Authorization header not set for ntlm'); 
  450. $this->username = $username; 
  451. $this->password = $password; 
  452. $this->authtype = $authtype; 
  453. $this->digestRequest = $digestRequest; 
  454.  
  455. /** 
  456. * set the soapaction value 
  457. * @param string $soapaction 
  458. * @access public 
  459. */ 
  460. function setSOAPAction($soapaction) { 
  461. $this->setHeader('SOAPAction', '"' . $soapaction . '"'); 
  462.  
  463. /** 
  464. * use http encoding 
  465. * @param string $enc encoding style. supported values: gzip, deflate, or both 
  466. * @access public 
  467. */ 
  468. function setEncoding($enc='gzip, deflate') { 
  469. if (function_exists('gzdeflate')) { 
  470. $this->protocol_version = '1.1'; 
  471. $this->setHeader('Accept-Encoding', $enc); 
  472. if (!isset($this->outgoing_headers['Connection'])) { 
  473. $this->setHeader('Connection', 'close'); 
  474. $this->persistentConnection = false; 
  475. // deprecated as of PHP 5.3.0 
  476. //set_magic_quotes_runtime(0); 
  477. $this->encoding = $enc; 
  478.  
  479. /** 
  480. * set proxy info here 
  481. * @param string $proxyhost use an empty string to remove proxy 
  482. * @param string $proxyport 
  483. * @param string $proxyusername 
  484. * @param string $proxypassword 
  485. * @param string $proxyauthtype (basic|ntlm) 
  486. * @access public 
  487. */ 
  488. function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') { 
  489. if ($proxyhost) { 
  490. $this->proxy = array( 
  491. 'host' => $proxyhost,  
  492. 'port' => $proxyport,  
  493. 'username' => $proxyusername,  
  494. 'password' => $proxypassword,  
  495. 'authtype' => $proxyauthtype 
  496. ); 
  497. if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') { 
  498. $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword)); 
  499. } else { 
  500. $this->debug('remove proxy'); 
  501. $proxy = null; 
  502. unsetHeader('Proxy-Authorization'); 
  503.  
  504.  
  505. /** 
  506. * Test if the given string starts with a header that is to be skipped. 
  507. * Skippable headers result from chunked transfer and proxy requests. 
  508. * @param string $data The string to check. 
  509. * @returns boolean Whether a skippable header was found. 
  510. * @access private 
  511. */ 
  512. function isSkippableCurlHeader(&$data) { 
  513. $skipHeaders = array( 'HTTP/1.1 100',  
  514. 'HTTP/1.0 301',  
  515. 'HTTP/1.1 301',  
  516. 'HTTP/1.0 302',  
  517. 'HTTP/1.1 302',  
  518. 'HTTP/1.0 401',  
  519. 'HTTP/1.1 401',  
  520. 'HTTP/1.0 200 Connection established'); 
  521. foreach ($skipHeaders as $hd) { 
  522. $prefix = substr($data, 0, strlen($hd)); 
  523. if ($prefix == $hd) return true; 
  524.  
  525. return false; 
  526.  
  527. /** 
  528. * decode a string that is encoded w/ "chunked' transfer encoding 
  529. * as defined in RFC2068 19.4.6 
  530. * @param string $buffer 
  531. * @param string $lb 
  532. * @returns string 
  533. * @access public 
  534. * @deprecated 
  535. */ 
  536. function decodeChunked($buffer, $lb) { 
  537. // length := 0 
  538. $length = 0; 
  539. $new = ''; 
  540.  
  541. // read chunk-size, chunk-extension (if any) and CRLF 
  542. // get the position of the linebreak 
  543. $chunkend = strpos($buffer, $lb); 
  544. if ($chunkend == FALSE) { 
  545. $this->debug('no linebreak found in decodeChunked'); 
  546. return $new; 
  547. $temp = substr($buffer, 0, $chunkend); 
  548. $chunk_size = hexdec( trim($temp) ); 
  549. $chunkstart = $chunkend + strlen($lb); 
  550. // while (chunk-size > 0) { 
  551. while ($chunk_size > 0) { 
  552. $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); 
  553. $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); 
  554.  
  555. // Just in case we got a broken connection 
  556. if ($chunkend == FALSE) { 
  557. $chunk = substr($buffer, $chunkstart); 
  558. // append chunk-data to entity-body 
  559. $new .= $chunk; 
  560. $length += strlen($chunk); 
  561. break; 
  562.  
  563. // read chunk-data and CRLF 
  564. $chunk = substr($buffer, $chunkstart, $chunkend-$chunkstart); 
  565. // append chunk-data to entity-body 
  566. $new .= $chunk; 
  567. // length := length + chunk-size 
  568. $length += strlen($chunk); 
  569. // read chunk-size and CRLF 
  570. $chunkstart = $chunkend + strlen($lb); 
  571.  
  572. $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); 
  573. if ($chunkend == FALSE) { 
  574. break; //Just in case we got a broken connection 
  575. $temp = substr($buffer, $chunkstart, $chunkend-$chunkstart); 
  576. $chunk_size = hexdec( trim($temp) ); 
  577. $chunkstart = $chunkend; 
  578. return $new; 
  579.  
  580. /** 
  581. * Writes the payload, including HTTP headers, to $this->outgoing_payload. 
  582. * @param string $data HTTP body 
  583. * @param string $cookie_str data for HTTP Cookie header 
  584. * @return void 
  585. * @access private 
  586. */ 
  587. function buildPayload($data, $cookie_str = '') { 
  588. // Note: for cURL connections, $this->outgoing_payload is ignored,  
  589. // as is the Content-Length header, but these are still created as 
  590. // debugging guides. 
  591.  
  592. // add content-length header 
  593. if ($this->request_method != 'GET') { 
  594. $this->setHeader('Content-Length', strlen($data)); 
  595.  
  596. // start building outgoing payload: 
  597. if ($this->proxy) { 
  598. $uri = $this->url; 
  599. } else { 
  600. $uri = $this->uri; 
  601. $req = "$this->request_method $uri HTTP/$this->protocol_version"; 
  602. $this->debug("HTTP request: $req"); 
  603. $this->outgoing_payload = "$req\r\n"; 
  604.  
  605. // loop thru headers, serializing 
  606. foreach($this->outgoing_headers as $k => $v) { 
  607. $hdr = $k.': '.$v; 
  608. $this->debug("HTTP header: $hdr"); 
  609. $this->outgoing_payload .= "$hdr\r\n"; 
  610.  
  611. // add any cookies 
  612. if ($cookie_str != '') { 
  613. $hdr = 'Cookie: '.$cookie_str; 
  614. $this->debug("HTTP header: $hdr"); 
  615. $this->outgoing_payload .= "$hdr\r\n"; 
  616.  
  617. // header/body separator 
  618. $this->outgoing_payload .= "\r\n"; 
  619.  
  620. // add data 
  621. $this->outgoing_payload .= $data; 
  622.  
  623. /** 
  624. * sends the SOAP request via HTTP[S] 
  625. * @param string $data message data 
  626. * @param array $cookies cookies to send 
  627. * @return boolean true if OK, false if problem 
  628. * @access private 
  629. */ 
  630. function sendRequest($data, $cookies = NULL) { 
  631. // build cookie string 
  632. $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https'))); 
  633.  
  634. // build payload 
  635. $this->buildPayload($data, $cookie_str); 
  636.  
  637. if ($this->io_method() == 'socket') { 
  638. // send payload 
  639. if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { 
  640. $this->setError('couldn\'t write message data to socket'); 
  641. $this->debug('couldn\'t write message data to socket'); 
  642. return false; 
  643. $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); 
  644. return true; 
  645. } else if ($this->io_method() == 'curl') { 
  646. // set payload 
  647. // cURL does say this should only be the verb, and in fact it 
  648. // turns out that the URI and HTTP version are appended to this, which 
  649. // some servers refuse to work with (so we no longer use this method!) 
  650. //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); 
  651. $curl_headers = array(); 
  652. foreach($this->outgoing_headers as $k => $v) { 
  653. if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') { 
  654. $this->debug("Skip cURL header $k: $v"); 
  655. } else { 
  656. $curl_headers[] = "$k: $v"; 
  657. if ($cookie_str != '') { 
  658. $curl_headers[] = 'Cookie: ' . $cookie_str; 
  659. $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers); 
  660. $this->debug('set cURL HTTP headers'); 
  661. if ($this->request_method == "POST") { 
  662. $this->setCurlOption(CURLOPT_POST, 1); 
  663. $this->setCurlOption(CURLOPT_POSTFIELDS, $data); 
  664. $this->debug('set cURL POST data'); 
  665. } else { 
  666. // insert custom user-set cURL options 
  667. foreach ($this->ch_options as $key => $val) { 
  668. $this->setCurlOption($key, $val); 
  669.  
  670. $this->debug('set cURL payload'); 
  671. return true; 
  672.  
  673. /** 
  674. * gets the SOAP response via HTTP[S] 
  675. * @return string the response (also sets member variables like incoming_payload) 
  676. * @access private 
  677. */ 
  678. function getResponse() { 
  679. $this->incoming_payload = ''; 
  680.  
  681. if ($this->io_method() == 'socket') { 
  682. // loop until headers have been retrieved 
  683. $data = ''; 
  684. while (!isset($lb)) { 
  685.  
  686. // We might EOF during header read. 
  687. if(feof($this->fp)) { 
  688. $this->incoming_payload = $data; 
  689. $this->debug('found no headers before EOF after length ' . strlen($data)); 
  690. $this->debug("received before EOF:\n" . $data); 
  691. $this->setError('server failed to send headers'); 
  692. return false; 
  693.  
  694. $tmp = fgets($this->fp, 256); 
  695. $tmplen = strlen($tmp); 
  696. $this->debug("read line of $tmplen bytes: " . trim($tmp)); 
  697.  
  698. if ($tmplen == 0) { 
  699. $this->incoming_payload = $data; 
  700. $this->debug('socket read of headers timed out after length ' . strlen($data)); 
  701. $this->debug("read before timeout: " . $data); 
  702. $this->setError('socket read of headers timed out'); 
  703. return false; 
  704.  
  705. $data .= $tmp; 
  706. $pos = strpos($data, "\r\n\r\n"); 
  707. if($pos > 1) { 
  708. $lb = "\r\n"; 
  709. } else { 
  710. $pos = strpos($data, "\n\n"); 
  711. if($pos > 1) { 
  712. $lb = "\n"; 
  713. // remove 100 headers 
  714. if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) { 
  715. unset($lb); 
  716. $data = ''; 
  717. }// 
  718. // store header data 
  719. $this->incoming_payload .= $data; 
  720. $this->debug('found end of headers after length ' . strlen($data)); 
  721. // process headers 
  722. $header_data = trim(substr($data, 0, $pos)); 
  723. $header_array = explode($lb, $header_data); 
  724. $this->incoming_headers = array(); 
  725. $this->incoming_cookies = array(); 
  726. foreach($header_array as $header_line) { 
  727. $arr = explode(':', $header_line, 2); 
  728. if(count($arr) > 1) { 
  729. $header_name = strtolower(trim($arr[0])); 
  730. $this->incoming_headers[$header_name] = trim($arr[1]); 
  731. if ($header_name == 'set-cookie') { 
  732. // TODO: allow multiple cookies from parseCookie 
  733. $cookie = $this->parseCookie(trim($arr[1])); 
  734. if ($cookie) { 
  735. $this->incoming_cookies[] = $cookie; 
  736. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 
  737. } else { 
  738. $this->debug('did not find cookie in ' . trim($arr[1])); 
  739. } else if (isset($header_name)) { 
  740. // append continuation line to previous header 
  741. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 
  742.  
  743. // loop until msg has been received 
  744. if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { 
  745. $content_length = 2147483647; // ignore any content-length header 
  746. $chunked = true; 
  747. $this->debug("want to read chunked content"); 
  748. } elseif (isset($this->incoming_headers['content-length'])) { 
  749. $content_length = $this->incoming_headers['content-length']; 
  750. $chunked = false; 
  751. $this->debug("want to read content of length $content_length"); 
  752. } else { 
  753. $content_length = 2147483647; 
  754. $chunked = false; 
  755. $this->debug("want to read content to EOF"); 
  756. $data = ''; 
  757. do { 
  758. if ($chunked) { 
  759. $tmp = fgets($this->fp, 256); 
  760. $tmplen = strlen($tmp); 
  761. $this->debug("read chunk line of $tmplen bytes"); 
  762. if ($tmplen == 0) { 
  763. $this->incoming_payload = $data; 
  764. $this->debug('socket read of chunk length timed out after length ' . strlen($data)); 
  765. $this->debug("read before timeout:\n" . $data); 
  766. $this->setError('socket read of chunk length timed out'); 
  767. return false; 
  768. $content_length = hexdec(trim($tmp)); 
  769. $this->debug("chunk length $content_length"); 
  770. $strlen = 0; 
  771. while (($strlen < $content_length) && (!feof($this->fp))) { 
  772. $readlen = min(8192, $content_length - $strlen); 
  773. $tmp = fread($this->fp, $readlen); 
  774. $tmplen = strlen($tmp); 
  775. $this->debug("read buffer of $tmplen bytes"); 
  776. if (($tmplen == 0) && (!feof($this->fp))) { 
  777. $this->incoming_payload = $data; 
  778. $this->debug('socket read of body timed out after length ' . strlen($data)); 
  779. $this->debug("read before timeout:\n" . $data); 
  780. $this->setError('socket read of body timed out'); 
  781. return false; 
  782. $strlen += $tmplen; 
  783. $data .= $tmp; 
  784. if ($chunked && ($content_length > 0)) { 
  785. $tmp = fgets($this->fp, 256); 
  786. $tmplen = strlen($tmp); 
  787. $this->debug("read chunk terminator of $tmplen bytes"); 
  788. if ($tmplen == 0) { 
  789. $this->incoming_payload = $data; 
  790. $this->debug('socket read of chunk terminator timed out after length ' . strlen($data)); 
  791. $this->debug("read before timeout:\n" . $data); 
  792. $this->setError('socket read of chunk terminator timed out'); 
  793. return false; 
  794. } while ($chunked && ($content_length > 0) && (!feof($this->fp))); 
  795. if (feof($this->fp)) { 
  796. $this->debug('read to EOF'); 
  797. $this->debug('read body of length ' . strlen($data)); 
  798. $this->incoming_payload .= $data; 
  799. $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); 
  800.  
  801. // close filepointer 
  802. if( 
  803. (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||  
  804. (! $this->persistentConnection) || feof($this->fp)) { 
  805. fclose($this->fp); 
  806. $this->fp = false; 
  807. $this->debug('closed socket'); 
  808.  
  809. // connection was closed unexpectedly 
  810. if($this->incoming_payload == '') { 
  811. $this->setError('no response from server'); 
  812. return false; 
  813.  
  814. // decode transfer-encoding 
  815. // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { 
  816. // if(!$data = $this->decodeChunked($data, $lb)) { 
  817. // $this->setError('Decoding of chunked data failed'); 
  818. // return false; 
  819. // } 
  820. //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>"; 
  821. // set decoded payload 
  822. // $this->incoming_payload = $header_data.$lb.$lb.$data; 
  823. // } 
  824.  
  825. } else if ($this->io_method() == 'curl') { 
  826. // send and receive 
  827. $this->debug('send and receive with cURL'); 
  828. $this->incoming_payload = curl_exec($this->ch); 
  829. $data = $this->incoming_payload; 
  830.  
  831. $cErr = curl_error($this->ch); 
  832. if ($cErr != '') { 
  833. $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>'; 
  834. // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE 
  835. foreach(curl_getinfo($this->ch) as $k => $v) { 
  836. $err .= "$k: $v<br>"; 
  837. $this->debug($err); 
  838. $this->setError($err); 
  839. curl_close($this->ch); 
  840. return false; 
  841. } else { 
  842. //echo '<pre>'; 
  843. //var_dump(curl_getinfo($this->ch)); 
  844. //echo '</pre>'; 
  845. // close curl 
  846. $this->debug('No cURL error, closing cURL'); 
  847. curl_close($this->ch); 
  848.  
  849. // try removing skippable headers 
  850. $savedata = $data; 
  851. while ($this->isSkippableCurlHeader($data)) { 
  852. $this->debug("Found HTTP header to skip"); 
  853. if ($pos = strpos($data, "\r\n\r\n")) { 
  854. $data = ltrim(substr($data, $pos)); 
  855. } elseif($pos = strpos($data, "\n\n") ) { 
  856. $data = ltrim(substr($data, $pos)); 
  857.  
  858. if ($data == '') { 
  859. // have nothing left; just remove 100 header(s) 
  860. $data = $savedata; 
  861. while (preg_match('/^HTTP\/1.1 100/', $data)) { 
  862. if ($pos = strpos($data, "\r\n\r\n")) { 
  863. $data = ltrim(substr($data, $pos)); 
  864. } elseif($pos = strpos($data, "\n\n") ) { 
  865. $data = ltrim(substr($data, $pos)); 
  866.  
  867. // separate content from HTTP headers 
  868. if ($pos = strpos($data, "\r\n\r\n")) { 
  869. $lb = "\r\n"; 
  870. } elseif( $pos = strpos($data, "\n\n")) { 
  871. $lb = "\n"; 
  872. } else { 
  873. $this->debug('no proper separation of headers and document'); 
  874. $this->setError('no proper separation of headers and document'); 
  875. return false; 
  876. $header_data = trim(substr($data, 0, $pos)); 
  877. $header_array = explode($lb, $header_data); 
  878. $data = ltrim(substr($data, $pos)); 
  879. $this->debug('found proper separation of headers and document'); 
  880. $this->debug('cleaned data, stringlen: '.strlen($data)); 
  881. // clean headers 
  882. foreach ($header_array as $header_line) { 
  883. $arr = explode(':', $header_line, 2); 
  884. if(count($arr) > 1) { 
  885. $header_name = strtolower(trim($arr[0])); 
  886. $this->incoming_headers[$header_name] = trim($arr[1]); 
  887. if ($header_name == 'set-cookie') { 
  888. // TODO: allow multiple cookies from parseCookie 
  889. $cookie = $this->parseCookie(trim($arr[1])); 
  890. if ($cookie) { 
  891. $this->incoming_cookies[] = $cookie; 
  892. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 
  893. } else { 
  894. $this->debug('did not find cookie in ' . trim($arr[1])); 
  895. } else if (isset($header_name)) { 
  896. // append continuation line to previous header 
  897. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 
  898.  
  899. $this->response_status_line = $header_array[0]; 
  900. $arr = explode(' ', $this->response_status_line, 3); 
  901. $http_version = $arr[0]; 
  902. $http_status = intval($arr[1]); 
  903. $http_reason = count($arr) > 2 ? $arr[2] : ''; 
  904.  
  905. // see if we need to resend the request with http digest authentication 
  906. if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) { 
  907. $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']); 
  908. $this->setURL($this->incoming_headers['location']); 
  909. $this->tryagain = true; 
  910. return false; 
  911.  
  912. // see if we need to resend the request with http digest authentication 
  913. if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) { 
  914. $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']); 
  915. if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) { 
  916. $this->debug('Server wants digest authentication'); 
  917. // remove "Digest " from our elements 
  918. $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); 
  919.  
  920. // parse elements into array 
  921. $digestElements = explode(', ', $digestString); 
  922. foreach ($digestElements as $val) { 
  923. $tempElement = explode('=', trim($val), 2); 
  924. $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); 
  925.  
  926. // should have (at least) qop, realm, nonce 
  927. if (isset($digestRequest['nonce'])) { 
  928. $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); 
  929. $this->tryagain = true; 
  930. return false; 
  931. $this->debug('HTTP authentication failed'); 
  932. $this->setError('HTTP authentication failed'); 
  933. return false; 
  934.  
  935. if ( 
  936. ($http_status >= 300 && $http_status <= 307) || 
  937. ($http_status >= 400 && $http_status <= 417) || 
  938. ($http_status >= 501 && $http_status <= 505) 
  939. ) { 
  940. $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)"); 
  941. return false; 
  942.  
  943. // decode content-encoding 
  944. if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') { 
  945. if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') { 
  946. // if decoding works, use it. else assume data wasn't gzencoded 
  947. if(function_exists('gzinflate')) { 
  948. //$timer->setMarker('starting decoding of gzip/deflated content'); 
  949. // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress) 
  950. // this means there are no Zlib headers, although there should be 
  951. $this->debug('The gzinflate function exists'); 
  952. $datalen = strlen($data); 
  953. if ($this->incoming_headers['content-encoding'] == 'deflate') { 
  954. if ($degzdata = @gzinflate($data)) { 
  955. $data = $degzdata; 
  956. $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes'); 
  957. if (strlen($data) < $datalen) { 
  958. // test for the case that the payload has been compressed twice 
  959. $this->debug('The inflated payload is smaller than the gzipped one; try again'); 
  960. if ($degzdata = @gzinflate($data)) { 
  961. $data = $degzdata; 
  962. $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes'); 
  963. } else { 
  964. $this->debug('Error using gzinflate to inflate the payload'); 
  965. $this->setError('Error using gzinflate to inflate the payload'); 
  966. } elseif ($this->incoming_headers['content-encoding'] == 'gzip') { 
  967. if ($degzdata = @gzinflate(substr($data, 10))) { // do our best 
  968. $data = $degzdata; 
  969. $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes'); 
  970. if (strlen($data) < $datalen) { 
  971. // test for the case that the payload has been compressed twice 
  972. $this->debug('The un-gzipped payload is smaller than the gzipped one; try again'); 
  973. if ($degzdata = @gzinflate(substr($data, 10))) { 
  974. $data = $degzdata; 
  975. $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes'); 
  976. } else { 
  977. $this->debug('Error using gzinflate to un-gzip the payload'); 
  978. $this->setError('Error using gzinflate to un-gzip the payload'); 
  979. //$timer->setMarker('finished decoding of gzip/deflated content'); 
  980. //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>"; 
  981. // set decoded payload 
  982. $this->incoming_payload = $header_data.$lb.$lb.$data; 
  983. } else { 
  984. $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 
  985. $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 
  986. } else { 
  987. $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 
  988. $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 
  989. } else { 
  990. $this->debug('No Content-Encoding header'); 
  991.  
  992. if(strlen($data) == 0) { 
  993. $this->debug('no data after headers!'); 
  994. $this->setError('no data present after HTTP headers'); 
  995. return false; 
  996.  
  997. return $data; 
  998.  
  999. /** 
  1000. * sets the content-type for the SOAP message to be sent 
  1001. * @param string $type the content type, MIME style 
  1002. * @param mixed $charset character set used for encoding (or false) 
  1003. * @access public 
  1004. */ 
  1005. function setContentType($type, $charset = false) { 
  1006. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); 
  1007.  
  1008. /** 
  1009. * specifies that an HTTP persistent connection should be used 
  1010. * @return boolean whether the request was honored by this method. 
  1011. * @access public 
  1012. */ 
  1013. function usePersistentConnection() { 
  1014. if (isset($this->outgoing_headers['Accept-Encoding'])) { 
  1015. return false; 
  1016. $this->protocol_version = '1.1'; 
  1017. $this->persistentConnection = true; 
  1018. $this->setHeader('Connection', 'Keep-Alive'); 
  1019. return true; 
  1020.  
  1021. /** 
  1022. * parse an incoming Cookie into it's parts 
  1023. * @param string $cookie_str content of cookie 
  1024. * @return array with data of that cookie 
  1025. * @access private 
  1026. */ 
  1027. /** 
  1028. * TODO: allow a Set-Cookie string to be parsed into multiple cookies 
  1029. */ 
  1030. function parseCookie($cookie_str) { 
  1031. $cookie_str = str_replace('; ', ';', $cookie_str) . ';'; 
  1032. $data = preg_split('/;/', $cookie_str); 
  1033. $value_str = $data[0]; 
  1034.  
  1035. $cookie_param = 'domain='; 
  1036. $start = strpos($cookie_str, $cookie_param); 
  1037. if ($start > 0) { 
  1038. $domain = substr($cookie_str, $start + strlen($cookie_param)); 
  1039. $domain = substr($domain, 0, strpos($domain, ';')); 
  1040. } else { 
  1041. $domain = ''; 
  1042.  
  1043. $cookie_param = 'expires='; 
  1044. $start = strpos($cookie_str, $cookie_param); 
  1045. if ($start > 0) { 
  1046. $expires = substr($cookie_str, $start + strlen($cookie_param)); 
  1047. $expires = substr($expires, 0, strpos($expires, ';')); 
  1048. } else { 
  1049. $expires = ''; 
  1050.  
  1051. $cookie_param = 'path='; 
  1052. $start = strpos($cookie_str, $cookie_param); 
  1053. if ( $start > 0 ) { 
  1054. $path = substr($cookie_str, $start + strlen($cookie_param)); 
  1055. $path = substr($path, 0, strpos($path, ';')); 
  1056. } else { 
  1057. $path = '/'; 
  1058.  
  1059. $cookie_param = ';secure;'; 
  1060. if (strpos($cookie_str, $cookie_param) !== FALSE) { 
  1061. $secure = true; 
  1062. } else { 
  1063. $secure = false; 
  1064.  
  1065. $sep_pos = strpos($value_str, '='); 
  1066.  
  1067. if ($sep_pos) { 
  1068. $name = substr($value_str, 0, $sep_pos); 
  1069. $value = substr($value_str, $sep_pos + 1); 
  1070. $cookie= array( 'name' => $name,  
  1071. 'value' => $value,  
  1072. 'domain' => $domain,  
  1073. 'path' => $path,  
  1074. 'expires' => $expires,  
  1075. 'secure' => $secure 
  1076. );  
  1077. return $cookie; 
  1078. return false; 
  1079.  
  1080. /** 
  1081. * sort out cookies for the current request 
  1082. * @param array $cookies array with all cookies 
  1083. * @param boolean $secure is the send-content secure or not? 
  1084. * @return string for Cookie-HTTP-Header 
  1085. * @access private 
  1086. */ 
  1087. function getCookiesForRequest($cookies, $secure=false) { 
  1088. $cookie_str = ''; 
  1089. if ((! is_null($cookies)) && (is_array($cookies))) { 
  1090. foreach ($cookies as $cookie) { 
  1091. if (! is_array($cookie)) { 
  1092. continue; 
  1093. $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']); 
  1094. if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { 
  1095. if (strtotime($cookie['expires']) <= time()) { 
  1096. $this->debug('cookie has expired'); 
  1097. continue; 
  1098. if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) { 
  1099. $domain = preg_quote($cookie['domain']); 
  1100. if (! preg_match("'.*$domain$'i", $this->host)) { 
  1101. $this->debug('cookie has different domain'); 
  1102. continue; 
  1103. if ((isset($cookie['path'])) && (! empty($cookie['path']))) { 
  1104. $path = preg_quote($cookie['path']); 
  1105. if (! preg_match("'^$path.*'i", $this->path)) { 
  1106. $this->debug('cookie is for a different path'); 
  1107. continue; 
  1108. if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) { 
  1109. $this->debug('cookie is secure, transport is not'); 
  1110. continue; 
  1111. $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; '; 
  1112. $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']); 
  1113. return $cookie_str; 
/lib/nusoap.php  
  1. class soap_transport_http extends nusoap_base { 
  2.  
  3. var $url = ''; 
  4. var $uri = ''; 
  5. var $digest_uri = ''; 
  6. var $scheme = ''; 
  7. var $host = ''; 
  8. var $port = ''; 
  9. var $path = ''; 
  10. var $request_method = 'POST'; 
  11. var $protocol_version = '1.0'; 
  12. var $encoding = ''; 
  13. var $outgoing_headers = array(); 
  14. var $incoming_headers = array(); 
  15. var $incoming_cookies = array(); 
  16. var $outgoing_payload = ''; 
  17. var $incoming_payload = ''; 
  18. var $response_status_line; // HTTP response status line 
  19. var $useSOAPAction = true; 
  20. var $persistentConnection = false; 
  21. var $ch = false; // cURL handle 
  22. var $ch_options = array(); // cURL custom options 
  23. var $use_curl = false; // force cURL use 
  24. var $proxy = null; // proxy information (associative array) 
  25. var $username = ''; 
  26. var $password = ''; 
  27. var $authtype = ''; 
  28. var $digestRequest = array(); 
  29. var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional) 
  30. // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem' 
  31. // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem' 
  32. // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem' 
  33. // passphrase: SSL key password/passphrase 
  34. // certpassword: SSL certificate password 
  35. // verifypeer: default is 1 
  36. // verifyhost: default is 1 
  37.  
  38. /** 
  39. * constructor 
  40. * @param string $url The URL to which to connect 
  41. * @param array $curl_options User-specified cURL options 
  42. * @param boolean $use_curl Whether to try to force cURL use 
  43. * @access public 
  44. */ 
  45. function soap_transport_http($url, $curl_options = NULL, $use_curl = false) { 
  46. parent::nusoap_base(); 
  47. $this->debug("ctor url=$url use_curl=$use_curl curl_options:"); 
  48. $this->appendDebug($this->varDump($curl_options)); 
  49. $this->setURL($url); 
  50. if (is_array($curl_options)) { 
  51. $this->ch_options = $curl_options; 
  52. $this->use_curl = $use_curl; 
  53. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); 
  54. $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')'); 
  55.  
  56. /** 
  57. * sets a cURL option 
  58. * @param mixed $option The cURL option (always integer?) 
  59. * @param mixed $value The cURL option value 
  60. * @access private 
  61. */ 
  62. function setCurlOption($option, $value) { 
  63. $this->debug("setCurlOption option=$option, value="); 
  64. $this->appendDebug($this->varDump($value)); 
  65. curl_setopt($this->ch, $option, $value); 
  66.  
  67. /** 
  68. * sets an HTTP header 
  69. * @param string $name The name of the header 
  70. * @param string $value The value of the header 
  71. * @access private 
  72. */ 
  73. function setHeader($name, $value) { 
  74. $this->outgoing_headers[$name] = $value; 
  75. $this->debug("set header $name: $value"); 
  76.  
  77. /** 
  78. * unsets an HTTP header 
  79. * @param string $name The name of the header 
  80. * @access private 
  81. */ 
  82. function unsetHeader($name) { 
  83. if (isset($this->outgoing_headers[$name])) { 
  84. $this->debug("unset header $name"); 
  85. unset($this->outgoing_headers[$name]); 
  86.  
  87. /** 
  88. * sets the URL to which to connect 
  89. * @param string $url The URL to which to connect 
  90. * @access private 
  91. */ 
  92. function setURL($url) { 
  93. $this->url = $url; 
  94.  
  95. $u = parse_url($url); 
  96. foreach($u as $k => $v) { 
  97. $this->debug("parsed URL $k = $v"); 
  98. $this->$k = $v; 
  99.  
  100. // add any GET params to path 
  101. if(isset($u['query']) && $u['query'] != '') { 
  102. $this->path .= '?' . $u['query']; 
  103.  
  104. // set default port 
  105. if(!isset($u['port'])) { 
  106. if($u['scheme'] == 'https') { 
  107. $this->port = 443; 
  108. } else { 
  109. $this->port = 80; 
  110.  
  111. $this->uri = $this->path; 
  112. $this->digest_uri = $this->uri; 
  113.  
  114. // build headers 
  115. if (!isset($u['port'])) { 
  116. $this->setHeader('Host', $this->host); 
  117. } else { 
  118. $this->setHeader('Host', $this->host.':'.$this->port); 
  119.  
  120. if (isset($u['user']) && $u['user'] != '') { 
  121. $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : ''); 
  122.  
  123. /** 
  124. * gets the I/O method to use 
  125. * @return string I/O method to use (socket|curl|unknown) 
  126. * @access private 
  127. */ 
  128. function io_method() { 
  129. if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) 
  130. return 'curl'; 
  131. if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) 
  132. return 'socket'; 
  133. return 'unknown'; 
  134.  
  135. /** 
  136. * establish an HTTP connection 
  137. * @param integer $timeout set connection timeout in seconds 
  138. * @param integer $response_timeout set response timeout in seconds 
  139. * @return boolean true if connected, false if not 
  140. * @access private 
  141. */ 
  142. function connect($connection_timeout=0, $response_timeout=30) { 
  143. // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like 
  144. // "regular" socket. 
  145. // TODO: disabled for now because OpenSSL must be *compiled* in (not just 
  146. // loaded), and until PHP5 stream_get_wrappers is not available. 
  147. // if ($this->scheme == 'https') { 
  148. // if (version_compare(phpversion(), '4.3.0') >= 0) { 
  149. // if (extension_loaded('openssl')) { 
  150. // $this->scheme = 'ssl'; 
  151. // $this->debug('Using SSL over OpenSSL'); 
  152. // } 
  153. // } 
  154. // } 
  155. $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port"); 
  156. if ($this->io_method() == 'socket') { 
  157. if (!is_array($this->proxy)) { 
  158. $host = $this->host; 
  159. $port = $this->port; 
  160. } else { 
  161. $host = $this->proxy['host']; 
  162. $port = $this->proxy['port']; 
  163.  
  164. // use persistent connection 
  165. if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) { 
  166. if (!feof($this->fp)) { 
  167. $this->debug('Re-use persistent connection'); 
  168. return true; 
  169. fclose($this->fp); 
  170. $this->debug('Closed persistent connection at EOF'); 
  171.  
  172. // munge host if using OpenSSL 
  173. if ($this->scheme == 'ssl') { 
  174. $host = 'ssl://' . $host; 
  175. $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout); 
  176.  
  177. // open socket 
  178. if($connection_timeout > 0) { 
  179. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); 
  180. } else { 
  181. $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); 
  182.  
  183. // test pointer 
  184. if(!$this->fp) { 
  185. $msg = 'Couldn\'t open socket connection to server ' . $this->url; 
  186. if ($this->errno) { 
  187. $msg .= ', Error ('.$this->errno.'): '.$this->error_str; 
  188. } else { 
  189. $msg .= ' prior to connect(). This is often a problem looking up the host name.'; 
  190. $this->debug($msg); 
  191. $this->setError($msg); 
  192. return false; 
  193.  
  194. // set response timeout 
  195. $this->debug('set response timeout to ' . $response_timeout); 
  196. socket_set_timeout( $this->fp, $response_timeout); 
  197.  
  198. $this->debug('socket connected'); 
  199. return true; 
  200. } else if ($this->io_method() == 'curl') { 
  201. if (!extension_loaded('curl')) { 
  202. // $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); 
  203. $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.'); 
  204. return false; 
  205. // Avoid warnings when PHP does not have these options 
  206. if (defined('CURLOPT_CONNECTIONTIMEOUT')) 
  207. $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT; 
  208. else 
  209. $CURLOPT_CONNECTIONTIMEOUT = 78; 
  210. if (defined('CURLOPT_HTTPAUTH')) 
  211. $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH; 
  212. else 
  213. $CURLOPT_HTTPAUTH = 107; 
  214. if (defined('CURLOPT_PROXYAUTH')) 
  215. $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH; 
  216. else 
  217. $CURLOPT_PROXYAUTH = 111; 
  218. if (defined('CURLAUTH_BASIC')) 
  219. $CURLAUTH_BASIC = CURLAUTH_BASIC; 
  220. else 
  221. $CURLAUTH_BASIC = 1; 
  222. if (defined('CURLAUTH_DIGEST')) 
  223. $CURLAUTH_DIGEST = CURLAUTH_DIGEST; 
  224. else 
  225. $CURLAUTH_DIGEST = 2; 
  226. if (defined('CURLAUTH_NTLM')) 
  227. $CURLAUTH_NTLM = CURLAUTH_NTLM; 
  228. else 
  229. $CURLAUTH_NTLM = 8; 
  230.  
  231. $this->debug('connect using cURL'); 
  232. // init CURL 
  233. $this->ch = curl_init(); 
  234. // set url 
  235. $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host"; 
  236. // add path 
  237. $hostURL .= $this->path; 
  238. $this->setCurlOption(CURLOPT_URL, $hostURL); 
  239. // follow location headers (re-directs) 
  240. if (ini_get('safe_mode') || ini_get('open_basedir')) { 
  241. $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION'); 
  242. $this->debug('safe_mode = '); 
  243. $this->appendDebug($this->varDump(ini_get('safe_mode'))); 
  244. $this->debug('open_basedir = '); 
  245. $this->appendDebug($this->varDump(ini_get('open_basedir'))); 
  246. } else { 
  247. $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1); 
  248. // ask for headers in the response output 
  249. $this->setCurlOption(CURLOPT_HEADER, 1); 
  250. // ask for the response output as the return value 
  251. $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1); 
  252. // encode 
  253. // We manage this ourselves through headers and encoding 
  254. // if(function_exists('gzuncompress')) { 
  255. // $this->setCurlOption(CURLOPT_ENCODING, 'deflate'); 
  256. // } 
  257. // persistent connection 
  258. if ($this->persistentConnection) { 
  259. // I believe the following comment is now bogus, having applied to 
  260. // the code when it used CURLOPT_CUSTOMREQUEST to send the request. 
  261. // The way we send data, we cannot use persistent connections, since 
  262. // there will be some "junk" at the end of our request. 
  263. //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true); 
  264. $this->persistentConnection = false; 
  265. $this->setHeader('Connection', 'close'); 
  266. // set timeouts 
  267. if ($connection_timeout != 0) { 
  268. $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout); 
  269. if ($response_timeout != 0) { 
  270. $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout); 
  271.  
  272. if ($this->scheme == 'https') { 
  273. $this->debug('set cURL SSL verify options'); 
  274. // recent versions of cURL turn on peer/host checking by default,  
  275. // while PHP binaries are not compiled with a default location for the 
  276. // CA cert bundle, so disable peer/host checking. 
  277. //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');  
  278. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0); 
  279. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0); 
  280.  
  281. // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo) 
  282. if ($this->authtype == 'certificate') { 
  283. $this->debug('set cURL certificate options'); 
  284. if (isset($this->certRequest['cainfofile'])) { 
  285. $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']); 
  286. if (isset($this->certRequest['verifypeer'])) { 
  287. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']); 
  288. } else { 
  289. $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1); 
  290. if (isset($this->certRequest['verifyhost'])) { 
  291. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']); 
  292. } else { 
  293. $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1); 
  294. if (isset($this->certRequest['sslcertfile'])) { 
  295. $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']); 
  296. if (isset($this->certRequest['sslkeyfile'])) { 
  297. $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']); 
  298. if (isset($this->certRequest['passphrase'])) { 
  299. $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']); 
  300. if (isset($this->certRequest['certpassword'])) { 
  301. $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']); 
  302. if ($this->authtype && ($this->authtype != 'certificate')) { 
  303. if ($this->username) { 
  304. $this->debug('set cURL username/password'); 
  305. $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password"); 
  306. if ($this->authtype == 'basic') { 
  307. $this->debug('set cURL for Basic authentication'); 
  308. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC); 
  309. if ($this->authtype == 'digest') { 
  310. $this->debug('set cURL for digest authentication'); 
  311. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST); 
  312. if ($this->authtype == 'ntlm') { 
  313. $this->debug('set cURL for NTLM authentication'); 
  314. $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM); 
  315. if (is_array($this->proxy)) { 
  316. $this->debug('set cURL proxy options'); 
  317. if ($this->proxy['port'] != '') { 
  318. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']); 
  319. } else { 
  320. $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']); 
  321. if ($this->proxy['username'] || $this->proxy['password']) { 
  322. $this->debug('set cURL proxy authentication options'); 
  323. $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']); 
  324. if ($this->proxy['authtype'] == 'basic') { 
  325. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC); 
  326. if ($this->proxy['authtype'] == 'ntlm') { 
  327. $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM); 
  328. $this->debug('cURL connection set up'); 
  329. return true; 
  330. } else { 
  331. $this->setError('Unknown scheme ' . $this->scheme); 
  332. $this->debug('Unknown scheme ' . $this->scheme); 
  333. return false; 
  334.  
  335. /** 
  336. * sends the SOAP request and gets the SOAP response via HTTP[S] 
  337. * @param string $data message data 
  338. * @param integer $timeout set connection timeout in seconds 
  339. * @param integer $response_timeout set response timeout in seconds 
  340. * @param array $cookies cookies to send 
  341. * @return string data 
  342. * @access public 
  343. */ 
  344. function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) { 
  345.  
  346. $this->debug('entered send() with data of length: '.strlen($data)); 
  347.  
  348. $this->tryagain = true; 
  349. $tries = 0; 
  350. while ($this->tryagain) { 
  351. $this->tryagain = false; 
  352. if ($tries++ < 2) { 
  353. // make connnection 
  354. if (!$this->connect($timeout, $response_timeout)) { 
  355. return false; 
  356.  
  357. // send request 
  358. if (!$this->sendRequest($data, $cookies)) { 
  359. return false; 
  360.  
  361. // get response 
  362. $respdata = $this->getResponse(); 
  363. } else { 
  364. $this->setError("Too many tries to get an OK response ($this->response_status_line)"); 
  365. }  
  366. $this->debug('end of send()'); 
  367. return $respdata; 
  368.  
  369.  
  370. /** 
  371. * sends the SOAP request and gets the SOAP response via HTTPS using CURL 
  372. * @param string $data message data 
  373. * @param integer $timeout set connection timeout in seconds 
  374. * @param integer $response_timeout set response timeout in seconds 
  375. * @param array $cookies cookies to send 
  376. * @return string data 
  377. * @access public 
  378. * @deprecated 
  379. */ 
  380. function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) { 
  381. return $this->send($data, $timeout, $response_timeout, $cookies); 
  382.  
  383. /** 
  384. * if authenticating, set user credentials here 
  385. * @param string $username 
  386. * @param string $password 
  387. * @param string $authtype (basic|digest|certificate|ntlm) 
  388. * @param array $digestRequest (keys must be nonce, nc, realm, qop) 
  389. * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) 
  390. * @access public 
  391. */ 
  392. function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) { 
  393. $this->debug("setCredentials username=$username authtype=$authtype digestRequest="); 
  394. $this->appendDebug($this->varDump($digestRequest)); 
  395. $this->debug("certRequest="); 
  396. $this->appendDebug($this->varDump($certRequest)); 
  397. // cf. RFC 2617 
  398. if ($authtype == 'basic') { 
  399. $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':', '', $username).':'.$password)); 
  400. } elseif ($authtype == 'digest') { 
  401. if (isset($digestRequest['nonce'])) { 
  402. $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; 
  403.  
  404. // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) 
  405.  
  406. // A1 = unq(username-value) ":" unq(realm-value) ":" passwd 
  407. $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password; 
  408.  
  409. // H(A1) = MD5(A1) 
  410. $HA1 = md5($A1); 
  411.  
  412. // A2 = Method ":" digest-uri-value 
  413. $A2 = $this->request_method . ':' . $this->digest_uri; 
  414.  
  415. // H(A2) 
  416. $HA2 = md5($A2); 
  417.  
  418. // KD(secret, data) = H(concat(secret, ":", data)) 
  419. // if qop == auth: 
  420. // request-digest = <"> < KD ( H(A1), unq(nonce-value) 
  421. // ":" nc-value 
  422. // ":" unq(cnonce-value) 
  423. // ":" unq(qop-value) 
  424. // ":" H(A2) 
  425. // ) <"> 
  426. // if qop is missing,  
  427. // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> 
  428.  
  429. $unhashedDigest = ''; 
  430. $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; 
  431. $cnonce = $nonce; 
  432. if ($digestRequest['qop'] != '') { 
  433. $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; 
  434. } else { 
  435. $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; 
  436.  
  437. $hashedDigest = md5($unhashedDigest); 
  438.  
  439. $opaque = '';  
  440. if (isset($digestRequest['opaque'])) { 
  441. $opaque = ', opaque="' . $digestRequest['opaque'] . '"'; 
  442.  
  443. $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'); 
  444. } elseif ($authtype == 'certificate') { 
  445. $this->certRequest = $certRequest; 
  446. $this->debug('Authorization header not set for certificate'); 
  447. } elseif ($authtype == 'ntlm') { 
  448. // do nothing 
  449. $this->debug('Authorization header not set for ntlm'); 
  450. $this->username = $username; 
  451. $this->password = $password; 
  452. $this->authtype = $authtype; 
  453. $this->digestRequest = $digestRequest; 
  454.  
  455. /** 
  456. * set the soapaction value 
  457. * @param string $soapaction 
  458. * @access public 
  459. */ 
  460. function setSOAPAction($soapaction) { 
  461. $this->setHeader('SOAPAction', '"' . $soapaction . '"'); 
  462.  
  463. /** 
  464. * use http encoding 
  465. * @param string $enc encoding style. supported values: gzip, deflate, or both 
  466. * @access public 
  467. */ 
  468. function setEncoding($enc='gzip, deflate') { 
  469. if (function_exists('gzdeflate')) { 
  470. $this->protocol_version = '1.1'; 
  471. $this->setHeader('Accept-Encoding', $enc); 
  472. if (!isset($this->outgoing_headers['Connection'])) { 
  473. $this->setHeader('Connection', 'close'); 
  474. $this->persistentConnection = false; 
  475. // deprecated as of PHP 5.3.0 
  476. //set_magic_quotes_runtime(0); 
  477. $this->encoding = $enc; 
  478.  
  479. /** 
  480. * set proxy info here 
  481. * @param string $proxyhost use an empty string to remove proxy 
  482. * @param string $proxyport 
  483. * @param string $proxyusername 
  484. * @param string $proxypassword 
  485. * @param string $proxyauthtype (basic|ntlm) 
  486. * @access public 
  487. */ 
  488. function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') { 
  489. if ($proxyhost) { 
  490. $this->proxy = array( 
  491. 'host' => $proxyhost,  
  492. 'port' => $proxyport,  
  493. 'username' => $proxyusername,  
  494. 'password' => $proxypassword,  
  495. 'authtype' => $proxyauthtype 
  496. ); 
  497. if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') { 
  498. $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword)); 
  499. } else { 
  500. $this->debug('remove proxy'); 
  501. $proxy = null; 
  502. unsetHeader('Proxy-Authorization'); 
  503.  
  504.  
  505. /** 
  506. * Test if the given string starts with a header that is to be skipped. 
  507. * Skippable headers result from chunked transfer and proxy requests. 
  508. * @param string $data The string to check. 
  509. * @returns boolean Whether a skippable header was found. 
  510. * @access private 
  511. */ 
  512. function isSkippableCurlHeader(&$data) { 
  513. $skipHeaders = array( 'HTTP/1.1 100',  
  514. 'HTTP/1.0 301',  
  515. 'HTTP/1.1 301',  
  516. 'HTTP/1.0 302',  
  517. 'HTTP/1.1 302',  
  518. 'HTTP/1.0 401',  
  519. 'HTTP/1.1 401',  
  520. 'HTTP/1.0 200 Connection established'); 
  521. foreach ($skipHeaders as $hd) { 
  522. $prefix = substr($data, 0, strlen($hd)); 
  523. if ($prefix == $hd) return true; 
  524.  
  525. return false; 
  526.  
  527. /** 
  528. * decode a string that is encoded w/ "chunked' transfer encoding 
  529. * as defined in RFC2068 19.4.6 
  530. * @param string $buffer 
  531. * @param string $lb 
  532. * @returns string 
  533. * @access public 
  534. * @deprecated 
  535. */ 
  536. function decodeChunked($buffer, $lb) { 
  537. // length := 0 
  538. $length = 0; 
  539. $new = ''; 
  540.  
  541. // read chunk-size, chunk-extension (if any) and CRLF 
  542. // get the position of the linebreak 
  543. $chunkend = strpos($buffer, $lb); 
  544. if ($chunkend == FALSE) { 
  545. $this->debug('no linebreak found in decodeChunked'); 
  546. return $new; 
  547. $temp = substr($buffer, 0, $chunkend); 
  548. $chunk_size = hexdec( trim($temp) ); 
  549. $chunkstart = $chunkend + strlen($lb); 
  550. // while (chunk-size > 0) { 
  551. while ($chunk_size > 0) { 
  552. $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); 
  553. $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); 
  554.  
  555. // Just in case we got a broken connection 
  556. if ($chunkend == FALSE) { 
  557. $chunk = substr($buffer, $chunkstart); 
  558. // append chunk-data to entity-body 
  559. $new .= $chunk; 
  560. $length += strlen($chunk); 
  561. break; 
  562.  
  563. // read chunk-data and CRLF 
  564. $chunk = substr($buffer, $chunkstart, $chunkend-$chunkstart); 
  565. // append chunk-data to entity-body 
  566. $new .= $chunk; 
  567. // length := length + chunk-size 
  568. $length += strlen($chunk); 
  569. // read chunk-size and CRLF 
  570. $chunkstart = $chunkend + strlen($lb); 
  571.  
  572. $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); 
  573. if ($chunkend == FALSE) { 
  574. break; //Just in case we got a broken connection 
  575. $temp = substr($buffer, $chunkstart, $chunkend-$chunkstart); 
  576. $chunk_size = hexdec( trim($temp) ); 
  577. $chunkstart = $chunkend; 
  578. return $new; 
  579.  
  580. /** 
  581. * Writes the payload, including HTTP headers, to $this->outgoing_payload. 
  582. * @param string $data HTTP body 
  583. * @param string $cookie_str data for HTTP Cookie header 
  584. * @return void 
  585. * @access private 
  586. */ 
  587. function buildPayload($data, $cookie_str = '') { 
  588. // Note: for cURL connections, $this->outgoing_payload is ignored,  
  589. // as is the Content-Length header, but these are still created as 
  590. // debugging guides. 
  591.  
  592. // add content-length header 
  593. if ($this->request_method != 'GET') { 
  594. $this->setHeader('Content-Length', strlen($data)); 
  595.  
  596. // start building outgoing payload: 
  597. if ($this->proxy) { 
  598. $uri = $this->url; 
  599. } else { 
  600. $uri = $this->uri; 
  601. $req = "$this->request_method $uri HTTP/$this->protocol_version"; 
  602. $this->debug("HTTP request: $req"); 
  603. $this->outgoing_payload = "$req\r\n"; 
  604.  
  605. // loop thru headers, serializing 
  606. foreach($this->outgoing_headers as $k => $v) { 
  607. $hdr = $k.': '.$v; 
  608. $this->debug("HTTP header: $hdr"); 
  609. $this->outgoing_payload .= "$hdr\r\n"; 
  610.  
  611. // add any cookies 
  612. if ($cookie_str != '') { 
  613. $hdr = 'Cookie: '.$cookie_str; 
  614. $this->debug("HTTP header: $hdr"); 
  615. $this->outgoing_payload .= "$hdr\r\n"; 
  616.  
  617. // header/body separator 
  618. $this->outgoing_payload .= "\r\n"; 
  619.  
  620. // add data 
  621. $this->outgoing_payload .= $data; 
  622.  
  623. /** 
  624. * sends the SOAP request via HTTP[S] 
  625. * @param string $data message data 
  626. * @param array $cookies cookies to send 
  627. * @return boolean true if OK, false if problem 
  628. * @access private 
  629. */ 
  630. function sendRequest($data, $cookies = NULL) { 
  631. // build cookie string 
  632. $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https'))); 
  633.  
  634. // build payload 
  635. $this->buildPayload($data, $cookie_str); 
  636.  
  637. if ($this->io_method() == 'socket') { 
  638. // send payload 
  639. if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { 
  640. $this->setError('couldn\'t write message data to socket'); 
  641. $this->debug('couldn\'t write message data to socket'); 
  642. return false; 
  643. $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); 
  644. return true; 
  645. } else if ($this->io_method() == 'curl') { 
  646. // set payload 
  647. // cURL does say this should only be the verb, and in fact it 
  648. // turns out that the URI and HTTP version are appended to this, which 
  649. // some servers refuse to work with (so we no longer use this method!) 
  650. //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); 
  651. $curl_headers = array(); 
  652. foreach($this->outgoing_headers as $k => $v) { 
  653. if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') { 
  654. $this->debug("Skip cURL header $k: $v"); 
  655. } else { 
  656. $curl_headers[] = "$k: $v"; 
  657. if ($cookie_str != '') { 
  658. $curl_headers[] = 'Cookie: ' . $cookie_str; 
  659. $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers); 
  660. $this->debug('set cURL HTTP headers'); 
  661. if ($this->request_method == "POST") { 
  662. $this->setCurlOption(CURLOPT_POST, 1); 
  663. $this->setCurlOption(CURLOPT_POSTFIELDS, $data); 
  664. $this->debug('set cURL POST data'); 
  665. } else { 
  666. // insert custom user-set cURL options 
  667. foreach ($this->ch_options as $key => $val) { 
  668. $this->setCurlOption($key, $val); 
  669.  
  670. $this->debug('set cURL payload'); 
  671. return true; 
  672.  
  673. /** 
  674. * gets the SOAP response via HTTP[S] 
  675. * @return string the response (also sets member variables like incoming_payload) 
  676. * @access private 
  677. */ 
  678. function getResponse() { 
  679. $this->incoming_payload = ''; 
  680.  
  681. if ($this->io_method() == 'socket') { 
  682. // loop until headers have been retrieved 
  683. $data = ''; 
  684. while (!isset($lb)) { 
  685.  
  686. // We might EOF during header read. 
  687. if(feof($this->fp)) { 
  688. $this->incoming_payload = $data; 
  689. $this->debug('found no headers before EOF after length ' . strlen($data)); 
  690. $this->debug("received before EOF:\n" . $data); 
  691. $this->setError('server failed to send headers'); 
  692. return false; 
  693.  
  694. $tmp = fgets($this->fp, 256); 
  695. $tmplen = strlen($tmp); 
  696. $this->debug("read line of $tmplen bytes: " . trim($tmp)); 
  697.  
  698. if ($tmplen == 0) { 
  699. $this->incoming_payload = $data; 
  700. $this->debug('socket read of headers timed out after length ' . strlen($data)); 
  701. $this->debug("read before timeout: " . $data); 
  702. $this->setError('socket read of headers timed out'); 
  703. return false; 
  704.  
  705. $data .= $tmp; 
  706. $pos = strpos($data, "\r\n\r\n"); 
  707. if($pos > 1) { 
  708. $lb = "\r\n"; 
  709. } else { 
  710. $pos = strpos($data, "\n\n"); 
  711. if($pos > 1) { 
  712. $lb = "\n"; 
  713. // remove 100 headers 
  714. if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) { 
  715. unset($lb); 
  716. $data = ''; 
  717. }// 
  718. // store header data 
  719. $this->incoming_payload .= $data; 
  720. $this->debug('found end of headers after length ' . strlen($data)); 
  721. // process headers 
  722. $header_data = trim(substr($data, 0, $pos)); 
  723. $header_array = explode($lb, $header_data); 
  724. $this->incoming_headers = array(); 
  725. $this->incoming_cookies = array(); 
  726. foreach($header_array as $header_line) { 
  727. $arr = explode(':', $header_line, 2); 
  728. if(count($arr) > 1) { 
  729. $header_name = strtolower(trim($arr[0])); 
  730. $this->incoming_headers[$header_name] = trim($arr[1]); 
  731. if ($header_name == 'set-cookie') { 
  732. // TODO: allow multiple cookies from parseCookie 
  733. $cookie = $this->parseCookie(trim($arr[1])); 
  734. if ($cookie) { 
  735. $this->incoming_cookies[] = $cookie; 
  736. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 
  737. } else { 
  738. $this->debug('did not find cookie in ' . trim($arr[1])); 
  739. } else if (isset($header_name)) { 
  740. // append continuation line to previous header 
  741. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 
  742.  
  743. // loop until msg has been received 
  744. if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { 
  745. $content_length = 2147483647; // ignore any content-length header 
  746. $chunked = true; 
  747. $this->debug("want to read chunked content"); 
  748. } elseif (isset($this->incoming_headers['content-length'])) { 
  749. $content_length = $this->incoming_headers['content-length']; 
  750. $chunked = false; 
  751. $this->debug("want to read content of length $content_length"); 
  752. } else { 
  753. $content_length = 2147483647; 
  754. $chunked = false; 
  755. $this->debug("want to read content to EOF"); 
  756. $data = ''; 
  757. do { 
  758. if ($chunked) { 
  759. $tmp = fgets($this->fp, 256); 
  760. $tmplen = strlen($tmp); 
  761. $this->debug("read chunk line of $tmplen bytes"); 
  762. if ($tmplen == 0) { 
  763. $this->incoming_payload = $data; 
  764. $this->debug('socket read of chunk length timed out after length ' . strlen($data)); 
  765. $this->debug("read before timeout:\n" . $data); 
  766. $this->setError('socket read of chunk length timed out'); 
  767. return false; 
  768. $content_length = hexdec(trim($tmp)); 
  769. $this->debug("chunk length $content_length"); 
  770. $strlen = 0; 
  771. while (($strlen < $content_length) && (!feof($this->fp))) { 
  772. $readlen = min(8192, $content_length - $strlen); 
  773. $tmp = fread($this->fp, $readlen); 
  774. $tmplen = strlen($tmp); 
  775. $this->debug("read buffer of $tmplen bytes"); 
  776. if (($tmplen == 0) && (!feof($this->fp))) { 
  777. $this->incoming_payload = $data; 
  778. $this->debug('socket read of body timed out after length ' . strlen($data)); 
  779. $this->debug("read before timeout:\n" . $data); 
  780. $this->setError('socket read of body timed out'); 
  781. return false; 
  782. $strlen += $tmplen; 
  783. $data .= $tmp; 
  784. if ($chunked && ($content_length > 0)) { 
  785. $tmp = fgets($this->fp, 256); 
  786. $tmplen = strlen($tmp); 
  787. $this->debug("read chunk terminator of $tmplen bytes"); 
  788. if ($tmplen == 0) { 
  789. $this->incoming_payload = $data; 
  790. $this->debug('socket read of chunk terminator timed out after length ' . strlen($data)); 
  791. $this->debug("read before timeout:\n" . $data); 
  792. $this->setError('socket read of chunk terminator timed out'); 
  793. return false; 
  794. } while ($chunked && ($content_length > 0) && (!feof($this->fp))); 
  795. if (feof($this->fp)) { 
  796. $this->debug('read to EOF'); 
  797. $this->debug('read body of length ' . strlen($data)); 
  798. $this->incoming_payload .= $data; 
  799. $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); 
  800.  
  801. // close filepointer 
  802. if( 
  803. (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||  
  804. (! $this->persistentConnection) || feof($this->fp)) { 
  805. fclose($this->fp); 
  806. $this->fp = false; 
  807. $this->debug('closed socket'); 
  808.  
  809. // connection was closed unexpectedly 
  810. if($this->incoming_payload == '') { 
  811. $this->setError('no response from server'); 
  812. return false; 
  813.  
  814. // decode transfer-encoding 
  815. // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { 
  816. // if(!$data = $this->decodeChunked($data, $lb)) { 
  817. // $this->setError('Decoding of chunked data failed'); 
  818. // return false; 
  819. // } 
  820. //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>"; 
  821. // set decoded payload 
  822. // $this->incoming_payload = $header_data.$lb.$lb.$data; 
  823. // } 
  824.  
  825. } else if ($this->io_method() == 'curl') { 
  826. // send and receive 
  827. $this->debug('send and receive with cURL'); 
  828. $this->incoming_payload = curl_exec($this->ch); 
  829. $data = $this->incoming_payload; 
  830.  
  831. $cErr = curl_error($this->ch); 
  832. if ($cErr != '') { 
  833. $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>'; 
  834. // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE 
  835. foreach(curl_getinfo($this->ch) as $k => $v) { 
  836. $err .= "$k: $v<br>"; 
  837. $this->debug($err); 
  838. $this->setError($err); 
  839. curl_close($this->ch); 
  840. return false; 
  841. } else { 
  842. //echo '<pre>'; 
  843. //var_dump(curl_getinfo($this->ch)); 
  844. //echo '</pre>'; 
  845. // close curl 
  846. $this->debug('No cURL error, closing cURL'); 
  847. curl_close($this->ch); 
  848.  
  849. // try removing skippable headers 
  850. $savedata = $data; 
  851. while ($this->isSkippableCurlHeader($data)) { 
  852. $this->debug("Found HTTP header to skip"); 
  853. if ($pos = strpos($data, "\r\n\r\n")) { 
  854. $data = ltrim(substr($data, $pos)); 
  855. } elseif($pos = strpos($data, "\n\n") ) { 
  856. $data = ltrim(substr($data, $pos)); 
  857.  
  858. if ($data == '') { 
  859. // have nothing left; just remove 100 header(s) 
  860. $data = $savedata; 
  861. while (preg_match('/^HTTP\/1.1 100/', $data)) { 
  862. if ($pos = strpos($data, "\r\n\r\n")) { 
  863. $data = ltrim(substr($data, $pos)); 
  864. } elseif($pos = strpos($data, "\n\n") ) { 
  865. $data = ltrim(substr($data, $pos)); 
  866.  
  867. // separate content from HTTP headers 
  868. if ($pos = strpos($data, "\r\n\r\n")) { 
  869. $lb = "\r\n"; 
  870. } elseif( $pos = strpos($data, "\n\n")) { 
  871. $lb = "\n"; 
  872. } else { 
  873. $this->debug('no proper separation of headers and document'); 
  874. $this->setError('no proper separation of headers and document'); 
  875. return false; 
  876. $header_data = trim(substr($data, 0, $pos)); 
  877. $header_array = explode($lb, $header_data); 
  878. $data = ltrim(substr($data, $pos)); 
  879. $this->debug('found proper separation of headers and document'); 
  880. $this->debug('cleaned data, stringlen: '.strlen($data)); 
  881. // clean headers 
  882. foreach ($header_array as $header_line) { 
  883. $arr = explode(':', $header_line, 2); 
  884. if(count($arr) > 1) { 
  885. $header_name = strtolower(trim($arr[0])); 
  886. $this->incoming_headers[$header_name] = trim($arr[1]); 
  887. if ($header_name == 'set-cookie') { 
  888. // TODO: allow multiple cookies from parseCookie 
  889. $cookie = $this->parseCookie(trim($arr[1])); 
  890. if ($cookie) { 
  891. $this->incoming_cookies[] = $cookie; 
  892. $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 
  893. } else { 
  894. $this->debug('did not find cookie in ' . trim($arr[1])); 
  895. } else if (isset($header_name)) { 
  896. // append continuation line to previous header 
  897. $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 
  898.  
  899. $this->response_status_line = $header_array[0]; 
  900. $arr = explode(' ', $this->response_status_line, 3); 
  901. $http_version = $arr[0]; 
  902. $http_status = intval($arr[1]); 
  903. $http_reason = count($arr) > 2 ? $arr[2] : ''; 
  904.  
  905. // see if we need to resend the request with http digest authentication 
  906. if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) { 
  907. $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']); 
  908. $this->setURL($this->incoming_headers['location']); 
  909. $this->tryagain = true; 
  910. return false; 
  911.  
  912. // see if we need to resend the request with http digest authentication 
  913. if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) { 
  914. $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']); 
  915. if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) { 
  916. $this->debug('Server wants digest authentication'); 
  917. // remove "Digest " from our elements 
  918. $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); 
  919.  
  920. // parse elements into array 
  921. $digestElements = explode(', ', $digestString); 
  922. foreach ($digestElements as $val) { 
  923. $tempElement = explode('=', trim($val), 2); 
  924. $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); 
  925.  
  926. // should have (at least) qop, realm, nonce 
  927. if (isset($digestRequest['nonce'])) { 
  928. $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); 
  929. $this->tryagain = true; 
  930. return false; 
  931. $this->debug('HTTP authentication failed'); 
  932. $this->setError('HTTP authentication failed'); 
  933. return false; 
  934.  
  935. if ( 
  936. ($http_status >= 300 && $http_status <= 307) || 
  937. ($http_status >= 400 && $http_status <= 417) || 
  938. ($http_status >= 501 && $http_status <= 505) 
  939. ) { 
  940. $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)"); 
  941. return false; 
  942.  
  943. // decode content-encoding 
  944. if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') { 
  945. if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') { 
  946. // if decoding works, use it. else assume data wasn't gzencoded 
  947. if(function_exists('gzinflate')) { 
  948. //$timer->setMarker('starting decoding of gzip/deflated content'); 
  949. // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress) 
  950. // this means there are no Zlib headers, although there should be 
  951. $this->debug('The gzinflate function exists'); 
  952. $datalen = strlen($data); 
  953. if ($this->incoming_headers['content-encoding'] == 'deflate') { 
  954. if ($degzdata = @gzinflate($data)) { 
  955. $data = $degzdata; 
  956. $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes'); 
  957. if (strlen($data) < $datalen) { 
  958. // test for the case that the payload has been compressed twice 
  959. $this->debug('The inflated payload is smaller than the gzipped one; try again'); 
  960. if ($degzdata = @gzinflate($data)) { 
  961. $data = $degzdata; 
  962. $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes'); 
  963. } else { 
  964. $this->debug('Error using gzinflate to inflate the payload'); 
  965. $this->setError('Error using gzinflate to inflate the payload'); 
  966. } elseif ($this->incoming_headers['content-encoding'] == 'gzip') { 
  967. if ($degzdata = @gzinflate(substr($data, 10))) { // do our best 
  968. $data = $degzdata; 
  969. $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes'); 
  970. if (strlen($data) < $datalen) { 
  971. // test for the case that the payload has been compressed twice 
  972. $this->debug('The un-gzipped payload is smaller than the gzipped one; try again'); 
  973. if ($degzdata = @gzinflate(substr($data, 10))) { 
  974. $data = $degzdata; 
  975. $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes'); 
  976. } else { 
  977. $this->debug('Error using gzinflate to un-gzip the payload'); 
  978. $this->setError('Error using gzinflate to un-gzip the payload'); 
  979. //$timer->setMarker('finished decoding of gzip/deflated content'); 
  980. //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>"; 
  981. // set decoded payload 
  982. $this->incoming_payload = $header_data.$lb.$lb.$data; 
  983. } else { 
  984. $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 
  985. $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 
  986. } else { 
  987. $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 
  988. $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 
  989. } else { 
  990. $this->debug('No Content-Encoding header'); 
  991.  
  992. if(strlen($data) == 0) { 
  993. $this->debug('no data after headers!'); 
  994. $this->setError('no data present after HTTP headers'); 
  995. return false; 
  996.  
  997. return $data; 
  998.  
  999. /** 
  1000. * sets the content-type for the SOAP message to be sent 
  1001. * @param string $type the content type, MIME style 
  1002. * @param mixed $charset character set used for encoding (or false) 
  1003. * @access public 
  1004. */ 
  1005. function setContentType($type, $charset = false) { 
  1006. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); 
  1007.  
  1008. /** 
  1009. * specifies that an HTTP persistent connection should be used 
  1010. * @return boolean whether the request was honored by this method. 
  1011. * @access public 
  1012. */ 
  1013. function usePersistentConnection() { 
  1014. if (isset($this->outgoing_headers['Accept-Encoding'])) { 
  1015. return false; 
  1016. $this->protocol_version = '1.1'; 
  1017. $this->persistentConnection = true; 
  1018. $this->setHeader('Connection', 'Keep-Alive'); 
  1019. return true; 
  1020.  
  1021. /** 
  1022. * parse an incoming Cookie into it's parts 
  1023. * @param string $cookie_str content of cookie 
  1024. * @return array with data of that cookie 
  1025. * @access private 
  1026. */ 
  1027. /** 
  1028. * TODO: allow a Set-Cookie string to be parsed into multiple cookies 
  1029. */ 
  1030. function parseCookie($cookie_str) { 
  1031. $cookie_str = str_replace('; ', ';', $cookie_str) . ';'; 
  1032. $data = preg_split('/;/', $cookie_str); 
  1033. $value_str = $data[0]; 
  1034.  
  1035. $cookie_param = 'domain='; 
  1036. $start = strpos($cookie_str, $cookie_param); 
  1037. if ($start > 0) { 
  1038. $domain = substr($cookie_str, $start + strlen($cookie_param)); 
  1039. $domain = substr($domain, 0, strpos($domain, ';')); 
  1040. } else { 
  1041. $domain = ''; 
  1042.  
  1043. $cookie_param = 'expires='; 
  1044. $start = strpos($cookie_str, $cookie_param); 
  1045. if ($start > 0) { 
  1046. $expires = substr($cookie_str, $start + strlen($cookie_param)); 
  1047. $expires = substr($expires, 0, strpos($expires, ';')); 
  1048. } else { 
  1049. $expires = ''; 
  1050.  
  1051. $cookie_param = 'path='; 
  1052. $start = strpos($cookie_str, $cookie_param); 
  1053. if ( $start > 0 ) { 
  1054. $path = substr($cookie_str, $start + strlen($cookie_param)); 
  1055. $path = substr($path, 0, strpos($path, ';')); 
  1056. } else { 
  1057. $path = '/'; 
  1058.  
  1059. $cookie_param = ';secure;'; 
  1060. if (strpos($cookie_str, $cookie_param) !== FALSE) { 
  1061. $secure = true; 
  1062. } else { 
  1063. $secure = false; 
  1064.  
  1065. $sep_pos = strpos($value_str, '='); 
  1066.  
  1067. if ($sep_pos) { 
  1068. $name = substr($value_str, 0, $sep_pos); 
  1069. $value = substr($value_str, $sep_pos + 1); 
  1070. $cookie= array( 'name' => $name,  
  1071. 'value' => $value,  
  1072. 'domain' => $domain,  
  1073. 'path' => $path,  
  1074. 'expires' => $expires,  
  1075. 'secure' => $secure 
  1076. );  
  1077. return $cookie; 
  1078. return false; 
  1079.  
  1080. /** 
  1081. * sort out cookies for the current request 
  1082. * @param array $cookies array with all cookies 
  1083. * @param boolean $secure is the send-content secure or not? 
  1084. * @return string for Cookie-HTTP-Header 
  1085. * @access private 
  1086. */ 
  1087. function getCookiesForRequest($cookies, $secure=false) { 
  1088. $cookie_str = ''; 
  1089. if ((! is_null($cookies)) && (is_array($cookies))) { 
  1090. foreach ($cookies as $cookie) { 
  1091. if (! is_array($cookie)) { 
  1092. continue; 
  1093. $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']); 
  1094. if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { 
  1095. if (strtotime($cookie['expires']) <= time()) { 
  1096. $this->debug('cookie has expired'); 
  1097. continue; 
  1098. if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) { 
  1099. $domain = preg_quote($cookie['domain']); 
  1100. if (! preg_match("'.*$domain$'i", $this->host)) { 
  1101. $this->debug('cookie has different domain'); 
  1102. continue; 
  1103. if ((isset($cookie['path'])) && (! empty($cookie['path']))) { 
  1104. $path = preg_quote($cookie['path']); 
  1105. if (! preg_match("'^$path.*'i", $this->path)) { 
  1106. $this->debug('cookie is for a different path'); 
  1107. continue; 
  1108. if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) { 
  1109. $this->debug('cookie is secure, transport is not'); 
  1110. continue; 
  1111. $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; '; 
  1112. $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']); 
  1113. return $cookie_str;