PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wpsc-includes/nusoap/class.soap_transport_http.php

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