• 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 
00036 class CAS_CookieJar {
00037 
00038         private $_cookies;
00039 
00047         public function __construct (array &$storageArray) {
00048                 $this->_cookies =& $storageArray;
00049         }
00050 
00062         public function storeCookies ($request_url, $response_headers) {
00063                 $urlParts = parse_url($request_url);
00064                 $defaultDomain = $urlParts['host'];
00065 
00066                 $cookies = $this->parseCookieHeaders($response_headers, $defaultDomain);
00067 
00068                 // var_dump($cookies);
00069                 foreach ($cookies as $cookie) {
00070                         // Enforce the same-origin policy by verifying that the cookie
00071                         // would match the url that is setting it
00072                         if (!$this->cookieMatchesTarget($cookie, $urlParts))
00073                                 continue;
00074 
00075                         // store the cookie
00076                         $this->storeCookie($cookie);
00077 
00078                         phpCAS::trace($cookie['name'].' -> '.$cookie['value']);
00079                 }
00080         }
00081 
00092         public function getCookies ($request_url) {
00093                 if (!count($this->_cookies))
00094                         return array();
00095 
00096                 // If our request URL can't be parsed, no cookies apply.
00097                 $target = parse_url($request_url);
00098                 if ($target === FALSE)
00099                         return array();
00100 
00101                 $this->expireCookies();
00102 
00103                 $matching_cookies = array();
00104                 foreach ($this->_cookies as $key => $cookie) {
00105                         if ($this->cookieMatchesTarget($cookie, $target)) {
00106                                 $matching_cookies[$cookie['name']] = $cookie['value'];
00107                         }
00108                 }
00109                 return $matching_cookies;
00110         }
00111 
00112 
00120         protected function parseCookieHeaders( $header, $defaultDomain ) {
00121                 phpCAS::traceBegin();
00122                 $cookies = array();
00123                 foreach( $header as $line ) {
00124                         if( preg_match( '/^Set-Cookie2?: /i', $line ) ) {
00125                                 $cookies[] = $this->parseCookieHeader($line, $defaultDomain);
00126                         }
00127                 }
00128 
00129                 phpCAS::traceEnd($cookies);
00130                 return $cookies;
00131         }
00132 
00142         protected function parseCookieHeader ($line, $defaultDomain) {
00143                 if (!$defaultDomain)
00144                         throw new InvalidArgumentException('$defaultDomain was not provided.');
00145 
00146                 // Set our default values
00147                 $cookie = array(
00148                         'domain' => $defaultDomain,
00149                         'path' => '/',
00150                         'secure' => false,
00151                 );
00152 
00153                 $line = preg_replace( '/^Set-Cookie2?: /i', '', trim( $line ) );
00154 
00155                 // trim any trailing semicolons.
00156                 $line = trim($line, ';');
00157 
00158                 phpCAS::trace("Cookie Line: $line");
00159 
00160                 // This implementation makes the assumption that semicolons will not
00161                 // be present in quoted attribute values. While attribute values that
00162                 // contain semicolons are allowed by RFC2965, they are hopefully rare
00163                 // enough to ignore for our purposes.
00164                 $attributeStrings = explode( ';', $line );
00165 
00166                 foreach( $attributeStrings as $attributeString ) {
00167                         // This implementation makes the assumption that equals symbols will not
00168                         // be present in quoted attribute values. While attribute values that
00169                         // contain equals symbols are allowed by RFC2965, they are hopefully rare
00170                         // enough to ignore for our purposes.
00171                         $attributeParts = explode( '=', $attributeString );
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 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 InvalidArgumentException('$target must be an array of URL attributes as generated by parse_url().');
00291                 if (!isset($target['host']))
00292                         throw new 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 Sat Mar 26 2011 12:11:03 for phpCAS by  doxygen 1.7.1