• Main Page
  • Related Pages
  • Modules
  • Classes
  • Files
  • Examples
  • File List
  • File Members

CAS/CookieJar.php

Go to the documentation of this file.
00001 <?php
00002 /*
00003  * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *
00009  *     * Redistributions of source code must retain the above copyright notice,
00010  *       this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright notice,
00012  *       this list of conditions and the following disclaimer in the documentation
00013  *       and/or other materials provided with the distribution.
00014  *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
00015  *       Collaborative nor the names of its contributors may be used to endorse or
00016  *       promote products derived from this software without specific prior
00017  *       written permission.
00018 
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00020  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
00023  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00026  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00031 include_once(dirname(__FILE__).'/InvalidArgumentException.php');
00032 
00038 class CAS_CookieJar {
00039 
00040         private $_cookies;
00041 
00049         public function __construct (array &$storageArray) {
00050                 $this->_cookies =& $storageArray;
00051         }
00052 
00064         public function storeCookies ($request_url, $response_headers) {
00065                 $urlParts = parse_url($request_url);
00066                 $defaultDomain = $urlParts['host'];
00067 
00068                 $cookies = $this->parseCookieHeaders($response_headers, $defaultDomain);
00069 
00070                 // var_dump($cookies);
00071                 foreach ($cookies as $cookie) {
00072                         // Enforce the same-origin policy by verifying that the cookie
00073                         // would match the url that is setting it
00074                         if (!$this->cookieMatchesTarget($cookie, $urlParts))
00075                                 continue;
00076 
00077                         // store the cookie
00078                         $this->storeCookie($cookie);
00079 
00080                         phpCAS::trace($cookie['name'].' -> '.$cookie['value']);
00081                 }
00082         }
00083 
00094         public function getCookies ($request_url) {
00095                 if (!count($this->_cookies))
00096                         return array();
00097 
00098                 // If our request URL can't be parsed, no cookies apply.
00099                 $target = parse_url($request_url);
00100                 if ($target === FALSE)
00101                         return array();
00102 
00103                 $this->expireCookies();
00104 
00105                 $matching_cookies = array();
00106                 foreach ($this->_cookies as $key => $cookie) {
00107                         if ($this->cookieMatchesTarget($cookie, $target)) {
00108                                 $matching_cookies[$cookie['name']] = $cookie['value'];
00109                         }
00110                 }
00111                 return $matching_cookies;
00112         }
00113 
00114 
00122         protected function parseCookieHeaders( $header, $defaultDomain ) {
00123                 phpCAS::traceBegin();
00124                 $cookies = array();
00125                 foreach( $header as $line ) {
00126                         if( preg_match( '/^Set-Cookie2?: /i', $line ) ) {
00127                                 $cookies[] = $this->parseCookieHeader($line, $defaultDomain);
00128                         }
00129                 }
00130 
00131                 phpCAS::traceEnd($cookies);
00132                 return $cookies;
00133         }
00134 
00144         protected function parseCookieHeader ($line, $defaultDomain) {
00145                 if (!$defaultDomain)
00146                         throw new CAS_InvalidArgumentException('$defaultDomain was not provided.');
00147 
00148                 // Set our default values
00149                 $cookie = array(
00150                         'domain' => $defaultDomain,
00151                         'path' => '/',
00152                         'secure' => false,
00153                 );
00154 
00155                 $line = preg_replace( '/^Set-Cookie2?: /i', '', trim( $line ) );
00156 
00157                 // trim any trailing semicolons.
00158                 $line = trim($line, ';');
00159 
00160                 phpCAS::trace("Cookie Line: $line");
00161 
00162                 // This implementation makes the assumption that semicolons will not
00163                 // be present in quoted attribute values. While attribute values that
00164                 // contain semicolons are allowed by RFC2965, they are hopefully rare
00165                 // enough to ignore for our purposes. Most browsers make the same 
00166                 // assumption.
00167                 $attributeStrings = explode( ';', $line );
00168 
00169                 foreach( $attributeStrings as $attributeString ) {
00170                         // split on the first equals sign and use the rest as value
00171                         $attributeParts = explode( '=', $attributeString, 2);
00172 
00173                         $attributeName = trim($attributeParts[0]);
00174                         $attributeNameLC = strtolower($attributeName);
00175 
00176                         if (isset($attributeParts[1])) {
00177                                 $attributeValue = trim($attributeParts[1]);
00178                                 // Values may be quoted strings.
00179                                 if (strpos($attributeValue, '"') === 0) {
00180                                         $attributeValue = trim($attributeValue, '"');
00181                                         // unescape any escaped quotes:
00182                                         $attributeValue = str_replace('\"', '"', $attributeValue);
00183                                 }
00184                         } else {
00185                                 $attributeValue = null;
00186                         }
00187 
00188                         switch ($attributeNameLC) {
00189                                 case 'expires':
00190                                         $cookie['expires'] = strtotime($attributeValue);
00191                                         break;
00192                                 case 'max-age':
00193                                         $cookie['max-age'] = (int)$attributeValue;
00194                                         // Set an expiry time based on the max-age
00195                                         if ($cookie['max-age'])
00196                                                 $cookie['expires'] = time() + $cookie['max-age'];
00197                                         // If max-age is zero, then the cookie should be removed imediately
00198                                         // so set an expiry before now.
00199                                         else
00200                                                 $cookie['expires'] = time() - 1;
00201                                         break;
00202                                 case 'secure':
00203                                         $cookie['secure'] = true;
00204                                         break;
00205                                 case 'domain':
00206                                 case 'path':
00207                                 case 'port':
00208                                 case 'version':
00209                                 case 'comment':
00210                                 case 'commenturl':
00211                                 case 'discard':
00212                                 case 'httponly':
00213                                         $cookie[$attributeNameLC] = $attributeValue;
00214                                         break;
00215                                 default:
00216                                         $cookie['name'] = $attributeName;
00217                                         $cookie['value'] = $attributeValue;
00218                         }
00219                 }
00220 
00221                 return $cookie;
00222         }
00223 
00233         protected function storeCookie ($cookie) {
00234                 // Discard any old versions of this cookie.
00235                 $this->discardCookie($cookie);
00236                 $this->_cookies[] = $cookie;
00237 
00238         }
00239 
00249         protected function discardCookie ($cookie) {
00250                 if (!isset($cookie['domain']) || !isset($cookie['path']) || !isset($cookie['path']))
00251                         throw new CAS_InvalidArgumentException('Invalid Cookie array passed.');
00252 
00253                 foreach ($this->_cookies as $key => $old_cookie) {
00254                         if ($cookie['domain'] == $old_cookie['domain']
00255                         && $cookie['path'] == $old_cookie['path']
00256                         && $cookie['name'] == $old_cookie['name'])
00257                         {
00258                                 unset($this->_cookies[$key]);
00259                         }
00260                 }
00261         }
00262 
00270         protected function expireCookies () {
00271                 foreach ($this->_cookies as $key => $cookie) {
00272                         if (isset($cookie['expires']) && $cookie['expires'] < time()) {
00273                                 unset($this->_cookies[$key]);
00274                         }
00275                 }
00276         }
00277 
00288         protected function cookieMatchesTarget ($cookie, $target) {
00289                 if (!is_array($target))
00290                         throw new CAS_InvalidArgumentException('$target must be an array of URL attributes as generated by parse_url().');
00291                 if (!isset($target['host']))
00292                         throw new CAS_InvalidArgumentException('$target must be an array of URL attributes as generated by parse_url().');
00293 
00294                 // Verify that the scheme matches
00295                 if ($cookie['secure'] && $target['scheme'] != 'https')
00296                 return false;
00297                 
00298                 // Verify that the host matches
00299                 // Match domain and mulit-host cookies
00300                 if (strpos($cookie['domain'], '.') === 0) {
00301                         // check that the target host a.b.c.edu is within .b.c.edu
00302                         $pos = strripos($target['host'], $cookie['domain']);
00303                         if (!$pos)
00304                         return false;
00305                         // verify that the cookie domain is the last part of the host.
00306                         if ($pos + strlen($cookie['domain']) != strlen($target['host']))
00307                         return false;
00308                 }
00309                 // If the cookie host doesn't begin with '.', the host must case-insensitive
00310                 // match exactly
00311                 else {
00312                         if (strcasecmp($target['host'], $cookie['domain']) !== 0)
00313                         return false;
00314                 }
00315 
00316                 // Verify that the port matches
00317                 if (isset($cookie['ports']) && !in_array($target['port'], $cookie['ports']))
00318                 return false;
00319 
00320                 // Verify that the path matches
00321                 if (strpos($target['path'], $cookie['path']) !== 0)
00322                 return false;
00323 
00324                 return true;
00325         }
00326 
00327 }
00328 
00329 ?>

Generated on Sun Jun 5 2011 19:05:26 for phpCAS by  doxygen 1.7.1