phpCAS  version 1.3.9
CookieJar.php
Go to the documentation of this file.
1 <?php
2 
42 {
43 
44  private $_cookies;
45 
54  public function __construct (array &$storageArray)
55  {
56  $this->_cookies =& $storageArray;
57  }
58 
70  public function storeCookies ($request_url, $response_headers)
71  {
72  $urlParts = parse_url($request_url);
73  $defaultDomain = $urlParts['host'];
74 
75  $cookies = $this->parseCookieHeaders($response_headers, $defaultDomain);
76 
77  foreach ($cookies as $cookie) {
78  // Enforce the same-origin policy by verifying that the cookie
79  // would match the url that is setting it
80  if (!$this->cookieMatchesTarget($cookie, $urlParts)) {
81  continue;
82  }
83 
84  // store the cookie
85  $this->storeCookie($cookie);
86 
87  phpCAS::trace($cookie['name'].' -> '.$cookie['value']);
88  }
89  }
90 
101  public function getCookies ($request_url)
102  {
103  if (!count($this->_cookies)) {
104  return array();
105  }
106 
107  // If our request URL can't be parsed, no cookies apply.
108  $target = parse_url($request_url);
109  if ($target === false) {
110  return array();
111  }
112 
113  $this->expireCookies();
114 
115  $matching_cookies = array();
116  foreach ($this->_cookies as $key => $cookie) {
117  if ($this->cookieMatchesTarget($cookie, $target)) {
118  $matching_cookies[$cookie['name']] = $cookie['value'];
119  }
120  }
121  return $matching_cookies;
122  }
123 
124 
135  protected function parseCookieHeaders( $header, $defaultDomain )
136  {
138  $cookies = array();
139  foreach ( $header as $line ) {
140  if ( preg_match('/^Set-Cookie2?: /i', $line)) {
141  $cookies[] = $this->parseCookieHeader($line, $defaultDomain);
142  }
143  }
144 
145  phpCAS::traceEnd($cookies);
146  return $cookies;
147  }
148 
160  protected function parseCookieHeader ($line, $defaultDomain)
161  {
162  if (!$defaultDomain) {
164  '$defaultDomain was not provided.'
165  );
166  }
167 
168  // Set our default values
169  $cookie = array(
170  'domain' => $defaultDomain,
171  'path' => '/',
172  'secure' => false,
173  );
174 
175  $line = preg_replace('/^Set-Cookie2?: /i', '', trim($line));
176 
177  // trim any trailing semicolons.
178  $line = trim($line, ';');
179 
180  phpCAS::trace("Cookie Line: $line");
181 
182  // This implementation makes the assumption that semicolons will not
183  // be present in quoted attribute values. While attribute values that
184  // contain semicolons are allowed by RFC2965, they are hopefully rare
185  // enough to ignore for our purposes. Most browsers make the same
186  // assumption.
187  $attributeStrings = explode(';', $line);
188 
189  foreach ( $attributeStrings as $attributeString ) {
190  // split on the first equals sign and use the rest as value
191  $attributeParts = explode('=', $attributeString, 2);
192 
193  $attributeName = trim($attributeParts[0]);
194  $attributeNameLC = strtolower($attributeName);
195 
196  if (isset($attributeParts[1])) {
197  $attributeValue = trim($attributeParts[1]);
198  // Values may be quoted strings.
199  if (strpos($attributeValue, '"') === 0) {
200  $attributeValue = trim($attributeValue, '"');
201  // unescape any escaped quotes:
202  $attributeValue = str_replace('\"', '"', $attributeValue);
203  }
204  } else {
205  $attributeValue = null;
206  }
207 
208  switch ($attributeNameLC) {
209  case 'expires':
210  $cookie['expires'] = strtotime($attributeValue);
211  break;
212  case 'max-age':
213  $cookie['max-age'] = (int)$attributeValue;
214  // Set an expiry time based on the max-age
215  if ($cookie['max-age']) {
216  $cookie['expires'] = time() + $cookie['max-age'];
217  } else {
218  // If max-age is zero, then the cookie should be removed
219  // imediately so set an expiry before now.
220  $cookie['expires'] = time() - 1;
221  }
222  break;
223  case 'secure':
224  $cookie['secure'] = true;
225  break;
226  case 'domain':
227  case 'path':
228  case 'port':
229  case 'version':
230  case 'comment':
231  case 'commenturl':
232  case 'discard':
233  case 'httponly':
234  $cookie[$attributeNameLC] = $attributeValue;
235  break;
236  default:
237  $cookie['name'] = $attributeName;
238  $cookie['value'] = $attributeValue;
239  }
240  }
241 
242  return $cookie;
243  }
244 
254  protected function storeCookie ($cookie)
255  {
256  // Discard any old versions of this cookie.
257  $this->discardCookie($cookie);
258  $this->_cookies[] = $cookie;
259 
260  }
261 
271  protected function discardCookie ($cookie)
272  {
273  if (!isset($cookie['domain'])
274  || !isset($cookie['path'])
275  || !isset($cookie['path'])
276  ) {
277  throw new CAS_InvalidArgumentException('Invalid Cookie array passed.');
278  }
279 
280  foreach ($this->_cookies as $key => $old_cookie) {
281  if ( $cookie['domain'] == $old_cookie['domain']
282  && $cookie['path'] == $old_cookie['path']
283  && $cookie['name'] == $old_cookie['name']
284  ) {
285  unset($this->_cookies[$key]);
286  }
287  }
288  }
289 
297  protected function expireCookies ()
298  {
299  foreach ($this->_cookies as $key => $cookie) {
300  if (isset($cookie['expires']) && $cookie['expires'] < time()) {
301  unset($this->_cookies[$key]);
302  }
303  }
304  }
305 
316  protected function cookieMatchesTarget ($cookie, $target)
317  {
318  if (!is_array($target)) {
320  '$target must be an array of URL attributes as generated by parse_url().'
321  );
322  }
323  if (!isset($target['host'])) {
325  '$target must be an array of URL attributes as generated by parse_url().'
326  );
327  }
328 
329  // Verify that the scheme matches
330  if ($cookie['secure'] && $target['scheme'] != 'https') {
331  return false;
332  }
333 
334  // Verify that the host matches
335  // Match domain and mulit-host cookies
336  if (strpos($cookie['domain'], '.') === 0) {
337  // .host.domain.edu cookies are valid for host.domain.edu
338  if (substr($cookie['domain'], 1) == $target['host']) {
339  // continue with other checks
340  } else {
341  // non-exact host-name matches.
342  // check that the target host a.b.c.edu is within .b.c.edu
343  $pos = strripos($target['host'], $cookie['domain']);
344  if (!$pos) {
345  return false;
346  }
347  // verify that the cookie domain is the last part of the host.
348  if ($pos + strlen($cookie['domain']) != strlen($target['host'])) {
349  return false;
350  }
351  // verify that the host name does not contain interior dots as per
352  // RFC 2965 section 3.3.2 Rejecting Cookies
353  // http://www.ietf.org/rfc/rfc2965.txt
354  $hostname = substr($target['host'], 0, $pos);
355  if (strpos($hostname, '.') !== false) {
356  return false;
357  }
358  }
359  } else {
360  // If the cookie host doesn't begin with '.',
361  // the host must case-insensitive match exactly
362  if (strcasecmp($target['host'], $cookie['domain']) !== 0) {
363  return false;
364  }
365  }
366 
367  // Verify that the port matches
368  if (isset($cookie['ports'])
369  && !in_array($target['port'], $cookie['ports'])
370  ) {
371  return false;
372  }
373 
374  // Verify that the path matches
375  if (strpos($target['path'], $cookie['path']) !== 0) {
376  return false;
377  }
378 
379  return true;
380  }
381 
382 }
383 
384 ?>
parseCookieHeader($line, $defaultDomain)
Definition: CookieJar.php:160
cookieMatchesTarget($cookie, $target)
Definition: CookieJar.php:316
static traceEnd($res='')
Definition: CAS.php:675
__construct(array &$storageArray)
Definition: CookieJar.php:54
static trace($str)
Definition: CAS.php:616
storeCookie($cookie)
Definition: CookieJar.php:254
getCookies($request_url)
Definition: CookieJar.php:101
parseCookieHeaders( $header, $defaultDomain)
Definition: CookieJar.php:135
storeCookies($request_url, $response_headers)
Definition: CookieJar.php:70
static traceBegin()
Definition: CAS.php:628
discardCookie($cookie)
Definition: CookieJar.php:271