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