TYPO3  7.6
CookieJar.php
Go to the documentation of this file.
1 <?php
22 require_once 'HTTP/Request2.php';
23 
34 class HTTP_Request2_CookieJar implements Serializable
35 {
50  protected $cookies = array();
51 
56  protected $serializeSession = false;
57 
62  protected $useList = true;
63 
69  protected static $psl = array();
70 
79  public function __construct(
80  $serializeSessionCookies = false, $usePublicSuffixList = true
81  ) {
82  $this->serializeSessionCookies($serializeSessionCookies);
83  $this->usePublicSuffixList($usePublicSuffixList);
84  }
85 
91  protected function now()
92  {
93  $dt = new DateTime();
94  $dt->setTimezone(new DateTimeZone('UTC'));
95  return $dt->format(DateTime::ISO8601);
96  }
97 
122  protected function checkAndUpdateFields(array $cookie, Net_URL2 $setter = null)
123  {
124  if ($missing = array_diff(array('name', 'value'), array_keys($cookie))) {
126  "Cookie array should contain 'name' and 'value' fields",
128  );
129  }
130  if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['name'])) {
132  "Invalid cookie name: '{$cookie['name']}'",
134  );
135  }
136  if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['value'])) {
138  "Invalid cookie value: '{$cookie['value']}'",
140  );
141  }
142  $cookie += array('domain' => '', 'path' => '', 'expires' => null, 'secure' => false);
143 
144  // Need ISO-8601 date @ UTC timezone
145  if (!empty($cookie['expires'])
146  && !preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+0000$/', $cookie['expires'])
147  ) {
148  try {
149  $dt = new DateTime($cookie['expires']);
150  $dt->setTimezone(new DateTimeZone('UTC'));
151  $cookie['expires'] = $dt->format(DateTime::ISO8601);
152  } catch (Exception $e) {
153  throw new HTTP_Request2_LogicException($e->getMessage());
154  }
155  }
156 
157  if (empty($cookie['domain']) || empty($cookie['path'])) {
158  if (!$setter) {
160  'Cookie misses domain and/or path component, cookie setter URL needed',
162  );
163  }
164  if (empty($cookie['domain'])) {
165  if ($host = $setter->getHost()) {
166  $cookie['domain'] = $host;
167  } else {
169  'Setter URL does not contain host part, can\'t set cookie domain',
171  );
172  }
173  }
174  if (empty($cookie['path'])) {
175  $path = $setter->getPath();
176  $cookie['path'] = empty($path)? '/': substr($path, 0, strrpos($path, '/') + 1);
177  }
178  }
179 
180  if ($setter && !$this->domainMatch($setter->getHost(), $cookie['domain'])) {
182  "Domain " . $setter->getHost() . " cannot set cookies for "
183  . $cookie['domain']
184  );
185  }
186 
187  return $cookie;
188  }
189 
199  public function store(array $cookie, Net_URL2 $setter = null)
200  {
201  $cookie = $this->checkAndUpdateFields($cookie, $setter);
202 
203  if (strlen($cookie['value'])
204  && (is_null($cookie['expires']) || $cookie['expires'] > $this->now())
205  ) {
206  if (!isset($this->cookies[$cookie['domain']])) {
207  $this->cookies[$cookie['domain']] = array();
208  }
209  if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
210  $this->cookies[$cookie['domain']][$cookie['path']] = array();
211  }
212  $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
213 
214  } elseif (isset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']])) {
215  unset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']]);
216  }
217  }
218 
227  {
228  foreach ($response->getCookies() as $cookie) {
229  $this->store($cookie, $setter);
230  }
231  }
232 
246  public function getMatching(Net_URL2 $url, $asString = false)
247  {
248  $host = $url->getHost();
249  $path = $url->getPath();
250  $secure = 0 == strcasecmp($url->getScheme(), 'https');
251 
252  $matched = $ret = array();
253  foreach (array_keys($this->cookies) as $domain) {
254  if ($this->domainMatch($host, $domain)) {
255  foreach (array_keys($this->cookies[$domain]) as $cPath) {
256  if (0 === strpos($path, $cPath)) {
257  foreach ($this->cookies[$domain][$cPath] as $name => $cookie) {
258  if (!$cookie['secure'] || $secure) {
259  $matched[$name][strlen($cookie['path'])] = $cookie;
260  }
261  }
262  }
263  }
264  }
265  }
266  foreach ($matched as $cookies) {
267  krsort($cookies);
268  $ret = array_merge($ret, $cookies);
269  }
270  if (!$asString) {
271  return $ret;
272  } else {
273  $str = '';
274  foreach ($ret as $c) {
275  $str .= (empty($str)? '': '; ') . $c['name'] . '=' . $c['value'];
276  }
277  return $str;
278  }
279  }
280 
286  public function getAll()
287  {
288  $cookies = array();
289  foreach (array_keys($this->cookies) as $domain) {
290  foreach (array_keys($this->cookies[$domain]) as $path) {
291  foreach ($this->cookies[$domain][$path] as $name => $cookie) {
292  $cookies[] = $cookie;
293  }
294  }
295  }
296  return $cookies;
297  }
298 
304  public function serializeSessionCookies($serialize)
305  {
306  $this->serializeSession = (bool)$serialize;
307  }
308 
330  public function usePublicSuffixList($useList)
331  {
332  $this->useList = (bool)$useList;
333  }
334 
342  public function serialize()
343  {
344  $cookies = $this->getAll();
345  if (!$this->serializeSession) {
346  for ($i = count($cookies) - 1; $i >= 0; $i--) {
347  if (empty($cookies[$i]['expires'])) {
348  unset($cookies[$i]);
349  }
350  }
351  }
352  return serialize(array(
353  'cookies' => $cookies,
354  'serializeSession' => $this->serializeSession,
355  'useList' => $this->useList
356  ));
357  }
358 
366  public function unserialize($serialized)
367  {
368  $data = unserialize($serialized);
369  $now = $this->now();
370  $this->serializeSessionCookies($data['serializeSession']);
371  $this->usePublicSuffixList($data['useList']);
372  foreach ($data['cookies'] as $cookie) {
373  if (!empty($cookie['expires']) && $cookie['expires'] <= $now) {
374  continue;
375  }
376  if (!isset($this->cookies[$cookie['domain']])) {
377  $this->cookies[$cookie['domain']] = array();
378  }
379  if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
380  $this->cookies[$cookie['domain']][$cookie['path']] = array();
381  }
382  $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
383  }
384  }
385 
398  public function domainMatch($requestHost, $cookieDomain)
399  {
400  if ($requestHost == $cookieDomain) {
401  return true;
402  }
403  // IP address, we require exact match
404  if (preg_match('/^(?:\d{1,3}\.){3}\d{1,3}$/', $requestHost)) {
405  return false;
406  }
407  if ('.' != $cookieDomain[0]) {
408  $cookieDomain = '.' . $cookieDomain;
409  }
410  // prevents setting cookies for '.com' and similar domains
411  if (!$this->useList && substr_count($cookieDomain, '.') < 2
412  || $this->useList && !self::getRegisteredDomain($cookieDomain)
413  ) {
414  return false;
415  }
416  return substr('.' . $requestHost, -strlen($cookieDomain)) == $cookieDomain;
417  }
418 
431  public static function getRegisteredDomain($domain)
432  {
433  $domainParts = explode('.', ltrim($domain, '.'));
434 
435  // load the list if needed
436  if (empty(self::$psl)) {
437  $path = '@data_dir@' . DIRECTORY_SEPARATOR . 'HTTP_Request2';
438  if (0 === strpos($path, '@' . 'data_dir@')) {
439  $path = realpath(
440  dirname(__FILE__) . DIRECTORY_SEPARATOR . '..'
441  . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data'
442  );
443  }
444  self::$psl = include_once $path . DIRECTORY_SEPARATOR . 'public-suffix-list.php';
445  }
446 
447  if (!($result = self::checkDomainsList($domainParts, self::$psl))) {
448  // known TLD, invalid domain name
449  return false;
450  }
451 
452  // unknown TLD
453  if (!strpos($result, '.')) {
454  // fallback to checking that domain "has at least two dots"
455  if (2 > ($count = count($domainParts))) {
456  return false;
457  }
458  return $domainParts[$count - 2] . '.' . $domainParts[$count - 1];
459  }
460  return $result;
461  }
462 
471  protected static function checkDomainsList(array $domainParts, $listNode)
472  {
473  $sub = array_pop($domainParts);
474  $result = null;
475 
476  if (!is_array($listNode) || is_null($sub)
477  || array_key_exists('!' . $sub, $listNode)
478  ) {
479  return $sub;
480 
481  } elseif (array_key_exists($sub, $listNode)) {
482  $result = self::checkDomainsList($domainParts, $listNode[$sub]);
483 
484  } elseif (array_key_exists('*', $listNode)) {
485  $result = self::checkDomainsList($domainParts, $listNode['*']);
486 
487  } else {
488  return $sub;
489  }
490 
491  return (strlen($result) > 0) ? ($result . '.' . $sub) : null;
492  }
493 }
494 ?>