/includes/libs/Cookie.php

https://gitlab.com/link233/bootmw · PHP · 208 lines · 111 code · 23 blank · 74 comment · 58 complexity · 411d1db78492e6f06b5ad600164b2e48 MD5 · raw file

  1. <?php
  2. /**
  3. * Cookie for HTTP requests.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup HTTP
  22. */
  23. class Cookie {
  24. protected $name;
  25. protected $value;
  26. protected $expires;
  27. protected $path;
  28. protected $domain;
  29. protected $isSessionKey = true;
  30. // TO IMPLEMENT protected $secure
  31. // TO IMPLEMENT? protected $maxAge (add onto expires)
  32. // TO IMPLEMENT? protected $version
  33. // TO IMPLEMENT? protected $comment
  34. function __construct( $name, $value, $attr ) {
  35. $this->name = $name;
  36. $this->set( $value, $attr );
  37. }
  38. /**
  39. * Sets a cookie. Used before a request to set up any individual
  40. * cookies. Used internally after a request to parse the
  41. * Set-Cookie headers.
  42. *
  43. * @param string $value The value of the cookie
  44. * @param array $attr Possible key/values:
  45. * expires A date string
  46. * path The path this cookie is used on
  47. * domain Domain this cookie is used on
  48. * @throws InvalidArgumentException
  49. */
  50. public function set( $value, $attr ) {
  51. $this->value = $value;
  52. if ( isset( $attr['expires'] ) ) {
  53. $this->isSessionKey = false;
  54. $this->expires = strtotime( $attr['expires'] );
  55. }
  56. if ( isset( $attr['path'] ) ) {
  57. $this->path = $attr['path'];
  58. } else {
  59. $this->path = '/';
  60. }
  61. if ( isset( $attr['domain'] ) ) {
  62. if ( self::validateCookieDomain( $attr['domain'] ) ) {
  63. $this->domain = $attr['domain'];
  64. }
  65. } else {
  66. throw new InvalidArgumentException( '$attr must contain a domain' );
  67. }
  68. }
  69. /**
  70. * Return the true if the cookie is valid is valid. Otherwise,
  71. * false. The uses a method similar to IE cookie security
  72. * described here:
  73. * http://kuza55.blogspot.com/2008/02/understanding-cookie-security.html
  74. * A better method might be to use a blacklist like
  75. * http://publicsuffix.org/
  76. *
  77. * @todo fixme fails to detect 3-letter top-level domains
  78. * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably
  79. * not a big problem in practice, but there are test cases)
  80. *
  81. * @param string $domain The domain to validate
  82. * @param string $originDomain (optional) the domain the cookie originates from
  83. * @return bool
  84. */
  85. public static function validateCookieDomain( $domain, $originDomain = null ) {
  86. $dc = explode( ".", $domain );
  87. // Don't allow a trailing dot or addresses without a or just a leading dot
  88. if ( substr( $domain, -1 ) == '.' ||
  89. count( $dc ) <= 1 ||
  90. count( $dc ) == 2 && $dc[0] === ''
  91. ) {
  92. return false;
  93. }
  94. // Only allow full, valid IP addresses
  95. if ( preg_match( '/^[0-9.]+$/', $domain ) ) {
  96. if ( count( $dc ) != 4 ) {
  97. return false;
  98. }
  99. if ( ip2long( $domain ) === false ) {
  100. return false;
  101. }
  102. if ( $originDomain == null || $originDomain == $domain ) {
  103. return true;
  104. }
  105. }
  106. // Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk"
  107. if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) {
  108. if ( ( count( $dc ) == 2 && strlen( $dc[0] ) <= 2 )
  109. || ( count( $dc ) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) {
  110. return false;
  111. }
  112. if ( ( count( $dc ) == 2 || ( count( $dc ) == 3 && $dc[0] == '' ) )
  113. && preg_match( '/(com|net|org|gov|edu)\...$/', $domain ) ) {
  114. return false;
  115. }
  116. }
  117. if ( $originDomain != null ) {
  118. if ( substr( $domain, 0, 1 ) != '.' && $domain != $originDomain ) {
  119. return false;
  120. }
  121. if ( substr( $domain, 0, 1 ) == '.'
  122. && substr_compare(
  123. $originDomain,
  124. $domain,
  125. -strlen( $domain ),
  126. strlen( $domain ),
  127. true
  128. ) != 0
  129. ) {
  130. return false;
  131. }
  132. }
  133. return true;
  134. }
  135. /**
  136. * Serialize the cookie jar into a format useful for HTTP Request headers.
  137. *
  138. * @param string $path The path that will be used. Required.
  139. * @param string $domain The domain that will be used. Required.
  140. * @return string
  141. */
  142. public function serializeToHttpRequest( $path, $domain ) {
  143. $ret = '';
  144. if ( $this->canServeDomain( $domain )
  145. && $this->canServePath( $path )
  146. && $this->isUnExpired() ) {
  147. $ret = $this->name . '=' . $this->value;
  148. }
  149. return $ret;
  150. }
  151. /**
  152. * @param string $domain
  153. * @return bool
  154. */
  155. protected function canServeDomain( $domain ) {
  156. if ( $domain == $this->domain
  157. || ( strlen( $domain ) > strlen( $this->domain )
  158. && substr( $this->domain, 0, 1 ) == '.'
  159. && substr_compare(
  160. $domain,
  161. $this->domain,
  162. -strlen( $this->domain ),
  163. strlen( $this->domain ),
  164. true
  165. ) == 0
  166. )
  167. ) {
  168. return true;
  169. }
  170. return false;
  171. }
  172. /**
  173. * @param string $path
  174. * @return bool
  175. */
  176. protected function canServePath( $path ) {
  177. return ( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 );
  178. }
  179. /**
  180. * @return bool
  181. */
  182. protected function isUnExpired() {
  183. return $this->isSessionKey || $this->expires > time();
  184. }
  185. }