Cookie::isUnExpired()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
24
class Cookie {
25
	protected $name;
26
	protected $value;
27
	protected $expires;
28
	protected $path;
29
	protected $domain;
30
	protected $isSessionKey = true;
31
	// TO IMPLEMENT	 protected $secure
32
	// TO IMPLEMENT? protected $maxAge (add onto expires)
33
	// TO IMPLEMENT? protected $version
34
	// TO IMPLEMENT? protected $comment
35
36
	function __construct( $name, $value, $attr ) {
37
		$this->name = $name;
38
		$this->set( $value, $attr );
39
	}
40
41
	/**
42
	 * Sets a cookie.  Used before a request to set up any individual
43
	 * cookies. Used internally after a request to parse the
44
	 * Set-Cookie headers.
45
	 *
46
	 * @param string $value The value of the cookie
47
	 * @param array $attr Possible key/values:
48
	 *        expires A date string
49
	 *        path    The path this cookie is used on
50
	 *        domain  Domain this cookie is used on
51
	 * @throws InvalidArgumentException
52
	 */
53
	public function set( $value, $attr ) {
54
		$this->value = $value;
55
56
		if ( isset( $attr['expires'] ) ) {
57
			$this->isSessionKey = false;
58
			$this->expires = strtotime( $attr['expires'] );
59
		}
60
61
		if ( isset( $attr['path'] ) ) {
62
			$this->path = $attr['path'];
63
		} else {
64
			$this->path = '/';
65
		}
66
67
		if ( isset( $attr['domain'] ) ) {
68
			if ( self::validateCookieDomain( $attr['domain'] ) ) {
69
				$this->domain = $attr['domain'];
70
			}
71
		} else {
72
			throw new InvalidArgumentException( '$attr must contain a domain' );
73
		}
74
	}
75
76
	/**
77
	 * Return the true if the cookie is valid is valid.  Otherwise,
78
	 * false.  The uses a method similar to IE cookie security
79
	 * described here:
80
	 * http://kuza55.blogspot.com/2008/02/understanding-cookie-security.html
81
	 * A better method might be to use a blacklist like
82
	 * http://publicsuffix.org/
83
	 *
84
	 * @todo fixme fails to detect 3-letter top-level domains
85
	 * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably
86
	 * not a big problem in practice, but there are test cases)
87
	 *
88
	 * @param string $domain The domain to validate
89
	 * @param string $originDomain (optional) the domain the cookie originates from
90
	 * @return bool
91
	 */
92
	public static function validateCookieDomain( $domain, $originDomain = null ) {
93
		$dc = explode( ".", $domain );
94
95
		// Don't allow a trailing dot or addresses without a or just a leading dot
96
		if ( substr( $domain, -1 ) == '.' ||
97
			count( $dc ) <= 1 ||
98
			count( $dc ) == 2 && $dc[0] === ''
99
		) {
100
			return false;
101
		}
102
103
		// Only allow full, valid IP addresses
104
		if ( preg_match( '/^[0-9.]+$/', $domain ) ) {
105
			if ( count( $dc ) != 4 ) {
106
				return false;
107
			}
108
109
			if ( ip2long( $domain ) === false ) {
110
				return false;
111
			}
112
113
			if ( $originDomain == null || $originDomain == $domain ) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $originDomain of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
114
				return true;
115
			}
116
117
		}
118
119
		// Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk"
120
		if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) {
121
			if ( ( count( $dc ) == 2 && strlen( $dc[0] ) <= 2 )
122
				|| ( count( $dc ) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) {
123
				return false;
124
			}
125
			if ( ( count( $dc ) == 2 || ( count( $dc ) == 3 && $dc[0] == '' ) )
126
				&& preg_match( '/(com|net|org|gov|edu)\...$/', $domain ) ) {
127
				return false;
128
			}
129
		}
130
131
		if ( $originDomain != null ) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $originDomain of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
132
			if ( substr( $domain, 0, 1 ) != '.' && $domain != $originDomain ) {
133
				return false;
134
			}
135
136
			if ( substr( $domain, 0, 1 ) == '.'
137
				&& substr_compare(
138
					$originDomain,
139
					$domain,
140
					-strlen( $domain ),
141
					strlen( $domain ),
142
					true
143
				) != 0
144
			) {
145
				return false;
146
			}
147
		}
148
149
		return true;
150
	}
151
152
	/**
153
	 * Serialize the cookie jar into a format useful for HTTP Request headers.
154
	 *
155
	 * @param string $path The path that will be used. Required.
156
	 * @param string $domain The domain that will be used. Required.
157
	 * @return string
158
	 */
159
	public function serializeToHttpRequest( $path, $domain ) {
160
		$ret = '';
161
162
		if ( $this->canServeDomain( $domain )
163
				&& $this->canServePath( $path )
164
				&& $this->isUnExpired() ) {
165
			$ret = $this->name . '=' . $this->value;
166
		}
167
168
		return $ret;
169
	}
170
171
	/**
172
	 * @param string $domain
173
	 * @return bool
174
	 */
175
	protected function canServeDomain( $domain ) {
176
		if ( $domain == $this->domain
177
			|| ( strlen( $domain ) > strlen( $this->domain )
178
				&& substr( $this->domain, 0, 1 ) == '.'
179
				&& substr_compare(
180
					$domain,
181
					$this->domain,
182
					-strlen( $this->domain ),
183
					strlen( $this->domain ),
184
					true
185
				) == 0
186
			)
187
		) {
188
			return true;
189
		}
190
191
		return false;
192
	}
193
194
	/**
195
	 * @param string $path
196
	 * @return bool
197
	 */
198
	protected function canServePath( $path ) {
199
		return ( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 );
200
	}
201
202
	/**
203
	 * @return bool
204
	 */
205
	protected function isUnExpired() {
206
		return $this->isSessionKey || $this->expires > time();
207
	}
208
}
209