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