Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13532 anikendra 1
<?php
2
/**
3
 * Cookie Component
4
 *
5
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://cakephp.org CakePHP(tm) Project
14
 * @package       Cake.Controller.Component
15
 * @since         CakePHP(tm) v 1.2.0.4213
16
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
17
 */
18
 
19
App::uses('Component', 'Controller');
20
App::uses('Security', 'Utility');
21
App::uses('Hash', 'Utility');
22
 
23
/**
24
 * Cookie Component.
25
 *
26
 * Cookie handling for the controller.
27
 *
28
 * @package       Cake.Controller.Component
29
 * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html
30
 *
31
 */
32
class CookieComponent extends Component {
33
 
34
/**
35
 * The name of the cookie.
36
 *
37
 * Overridden with the controller beforeFilter();
38
 * $this->Cookie->name = 'CookieName';
39
 *
40
 * @var string
41
 */
42
	public $name = 'CakeCookie';
43
 
44
/**
45
 * The time a cookie will remain valid.
46
 *
47
 * Can be either integer Unix timestamp or a date string.
48
 *
49
 * Overridden with the controller beforeFilter();
50
 * $this->Cookie->time = '5 Days';
51
 *
52
 * @var mixed
53
 */
54
	public $time = null;
55
 
56
/**
57
 * Cookie path.
58
 *
59
 * Overridden with the controller beforeFilter();
60
 * $this->Cookie->path = '/';
61
 *
62
 * The path on the server in which the cookie will be available on.
63
 * If public $cookiePath is set to '/foo/', the cookie will only be available
64
 * within the /foo/ directory and all sub-directories such as /foo/bar/ of domain.
65
 * The default value is the entire domain.
66
 *
67
 * @var string
68
 */
69
	public $path = '/';
70
 
71
/**
72
 * Domain path.
73
 *
74
 * The domain that the cookie is available.
75
 *
76
 * Overridden with the controller beforeFilter();
77
 * $this->Cookie->domain = '.example.com';
78
 *
79
 * To make the cookie available on all subdomains of example.com.
80
 * Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter
81
 *
82
 * @var string
83
 */
84
	public $domain = '';
85
 
86
/**
87
 * Secure HTTPS only cookie.
88
 *
89
 * Overridden with the controller beforeFilter();
90
 * $this->Cookie->secure = true;
91
 *
92
 * Indicates that the cookie should only be transmitted over a secure HTTPS connection.
93
 * When set to true, the cookie will only be set if a secure connection exists.
94
 *
95
 * @var boolean
96
 */
97
	public $secure = false;
98
 
99
/**
100
 * Encryption key.
101
 *
102
 * Overridden with the controller beforeFilter();
103
 * $this->Cookie->key = 'SomeRandomString';
104
 *
105
 * @var string
106
 */
107
	public $key = null;
108
 
109
/**
110
 * HTTP only cookie
111
 *
112
 * Set to true to make HTTP only cookies. Cookies that are HTTP only
113
 * are not accessible in JavaScript.
114
 *
115
 * @var boolean
116
 */
117
	public $httpOnly = false;
118
 
119
/**
120
 * Values stored in the cookie.
121
 *
122
 * Accessed in the controller using $this->Cookie->read('Name.key');
123
 *
124
 * @see CookieComponent::read();
125
 * @var string
126
 */
127
	protected $_values = array();
128
 
129
/**
130
 * Type of encryption to use.
131
 *
132
 * Currently two methods are available: cipher and rijndael
133
 * Defaults to Security::cipher();
134
 *
135
 * @var string
136
 */
137
	protected $_type = 'cipher';
138
 
139
/**
140
 * Used to reset cookie time if $expire is passed to CookieComponent::write()
141
 *
142
 * @var string
143
 */
144
	protected $_reset = null;
145
 
146
/**
147
 * Expire time of the cookie
148
 *
149
 * This is controlled by CookieComponent::time;
150
 *
151
 * @var string
152
 */
153
	protected $_expires = 0;
154
 
155
/**
156
 * A reference to the Controller's CakeResponse object
157
 *
158
 * @var CakeResponse
159
 */
160
	protected $_response = null;
161
 
162
/**
163
 * Constructor
164
 *
165
 * @param ComponentCollection $collection A ComponentCollection for this component
166
 * @param array $settings Array of settings.
167
 */
168
	public function __construct(ComponentCollection $collection, $settings = array()) {
169
		$this->key = Configure::read('Security.salt');
170
		parent::__construct($collection, $settings);
171
		if (isset($this->time)) {
172
			$this->_expire($this->time);
173
		}
174
 
175
		$controller = $collection->getController();
176
		if ($controller && isset($controller->response)) {
177
			$this->_response = $controller->response;
178
		} else {
179
			$this->_response = new CakeResponse();
180
		}
181
	}
182
 
183
/**
184
 * Start CookieComponent for use in the controller
185
 *
186
 * @param Controller $controller
187
 * @return void
188
 */
189
	public function startup(Controller $controller) {
190
		$this->_expire($this->time);
191
 
192
		$this->_values[$this->name] = array();
193
	}
194
 
195
/**
196
 * Write a value to the $_COOKIE[$key];
197
 *
198
 * Optional [Name.], required key, optional $value, optional $encrypt, optional $expires
199
 * $this->Cookie->write('[Name.]key, $value);
200
 *
201
 * By default all values are encrypted.
202
 * You must pass $encrypt false to store values in clear test
203
 *
204
 * You must use this method before any output is sent to the browser.
205
 * Failure to do so will result in header already sent errors.
206
 *
207
 * @param string|array $key Key for the value
208
 * @param mixed $value Value
209
 * @param boolean $encrypt Set to true to encrypt value, false otherwise
210
 * @param integer|string $expires Can be either the number of seconds until a cookie
211
 *   expires, or a strtotime compatible time offset.
212
 * @return void
213
 * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::write
214
 */
215
	public function write($key, $value = null, $encrypt = true, $expires = null) {
216
		if (empty($this->_values[$this->name])) {
217
			$this->read();
218
		}
219
 
220
		if ($encrypt === null) {
221
			$encrypt = true;
222
		}
223
		$this->_encrypted = $encrypt;
224
		$this->_expire($expires);
225
 
226
		if (!is_array($key)) {
227
			$key = array($key => $value);
228
		}
229
 
230
		foreach ($key as $name => $value) {
231
			$names = array($name);
232
			if (strpos($name, '.') !== false) {
233
				$names = explode('.', $name, 2);
234
			}
235
			$firstName = $names[0];
236
			$isMultiValue = (is_array($value) || count($names) > 1);
237
 
238
			if (!isset($this->_values[$this->name][$firstName]) && $isMultiValue) {
239
				$this->_values[$this->name][$firstName] = array();
240
			}
241
 
242
			if (count($names) > 1) {
243
				$this->_values[$this->name][$firstName] = Hash::insert(
244
					$this->_values[$this->name][$firstName],
245
					$names[1],
246
					$value
247
				);
248
			} else {
249
				$this->_values[$this->name][$firstName] = $value;
250
			}
251
			$this->_write('[' . $firstName . ']', $this->_values[$this->name][$firstName]);
252
		}
253
		$this->_encrypted = true;
254
	}
255
 
256
/**
257
 * Read the value of the $_COOKIE[$key];
258
 *
259
 * Optional [Name.], required key
260
 * $this->Cookie->read(Name.key);
261
 *
262
 * @param string $key Key of the value to be obtained. If none specified, obtain map key => values
263
 * @return string or null, value for specified key
264
 * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::read
265
 */
266
	public function read($key = null) {
267
		if (empty($this->_values[$this->name]) && isset($_COOKIE[$this->name])) {
268
			$this->_values[$this->name] = $this->_decrypt($_COOKIE[$this->name]);
269
		}
270
		if (empty($this->_values[$this->name])) {
271
			$this->_values[$this->name] = array();
272
		}
273
		if ($key === null) {
274
			return $this->_values[$this->name];
275
		}
276
 
277
		if (strpos($key, '.') !== false) {
278
			$names = explode('.', $key, 2);
279
			$key = $names[0];
280
		}
281
		if (!isset($this->_values[$this->name][$key])) {
282
			return null;
283
		}
284
 
285
		if (!empty($names[1])) {
286
			return Hash::get($this->_values[$this->name][$key], $names[1]);
287
		}
288
		return $this->_values[$this->name][$key];
289
	}
290
 
291
/**
292
 * Returns true if given variable is set in cookie.
293
 *
294
 * @param string $var Variable name to check for
295
 * @return boolean True if variable is there
296
 */
297
	public function check($key = null) {
298
		if (empty($key)) {
299
			return false;
300
		}
301
		return $this->read($key) !== null;
302
	}
303
 
304
/**
305
 * Delete a cookie value
306
 *
307
 * Optional [Name.], required key
308
 * $this->Cookie->delete('Name.key);
309
 *
310
 * You must use this method before any output is sent to the browser.
311
 * Failure to do so will result in header already sent errors.
312
 *
313
 * This method will delete both the top level and 2nd level cookies set.
314
 * For example assuming that $name = App, deleting `User` will delete
315
 * both `App[User]` and any other cookie values like `App[User][email]`
316
 * This is done to clean up cookie storage from before 2.4.3, where cookies
317
 * were stored inconsistently.
318
 *
319
 * @param string $key Key of the value to be deleted
320
 * @return void
321
 * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::delete
322
 */
323
	public function delete($key) {
324
		if (empty($this->_values[$this->name])) {
325
			$this->read();
326
		}
327
		if (strpos($key, '.') === false) {
328
			if (isset($this->_values[$this->name][$key]) && is_array($this->_values[$this->name][$key])) {
329
				foreach ($this->_values[$this->name][$key] as $idx => $val) {
330
					$this->_delete("[$key][$idx]");
331
				}
332
			}
333
			$this->_delete("[$key]");
334
			unset($this->_values[$this->name][$key]);
335
			return;
336
		}
337
		$names = explode('.', $key, 2);
338
		if (isset($this->_values[$this->name][$names[0]])) {
339
			$this->_values[$this->name][$names[0]] = Hash::remove($this->_values[$this->name][$names[0]], $names[1]);
340
		}
341
		$this->_delete('[' . implode('][', $names) . ']');
342
	}
343
 
344
/**
345
 * Destroy current cookie
346
 *
347
 * You must use this method before any output is sent to the browser.
348
 * Failure to do so will result in header already sent errors.
349
 *
350
 * @return void
351
 * @link http://book.cakephp.org/2.0/en/core-libraries/components/cookie.html#CookieComponent::destroy
352
 */
353
	public function destroy() {
354
		if (isset($_COOKIE[$this->name])) {
355
			$this->_values[$this->name] = $this->_decrypt($_COOKIE[$this->name]);
356
		}
357
 
358
		foreach ($this->_values[$this->name] as $name => $value) {
359
			if (is_array($value)) {
360
				foreach ($value as $key => $val) {
361
					unset($this->_values[$this->name][$name][$key]);
362
					$this->_delete("[$name][$key]");
363
				}
364
			}
365
			unset($this->_values[$this->name][$name]);
366
			$this->_delete("[$name]");
367
		}
368
	}
369
 
370
/**
371
 * Will allow overriding default encryption method. Use this method
372
 * in ex: AppController::beforeFilter() before you have read or
373
 * written any cookies.
374
 *
375
 * @param string $type Encryption method
376
 * @return void
377
 */
378
	public function type($type = 'cipher') {
379
		$availableTypes = array(
380
			'cipher',
381
			'rijndael'
382
		);
383
		if (!in_array($type, $availableTypes)) {
384
			trigger_error(__d('cake_dev', 'You must use cipher or rijndael for cookie encryption type'), E_USER_WARNING);
385
			$type = 'cipher';
386
		}
387
		$this->_type = $type;
388
	}
389
 
390
/**
391
 * Set the expire time for a session variable.
392
 *
393
 * Creates a new expire time for a session variable.
394
 * $expire can be either integer Unix timestamp or a date string.
395
 *
396
 * Used by write()
397
 * CookieComponent::write(string, string, boolean, 8400);
398
 * CookieComponent::write(string, string, boolean, '5 Days');
399
 *
400
 * @param integer|string $expires Can be either Unix timestamp, or date string
401
 * @return integer Unix timestamp
402
 */
403
	protected function _expire($expires = null) {
404
		if ($expires === null) {
405
			return $this->_expires;
406
		}
407
		$this->_reset = $this->_expires;
408
		if (!$expires) {
409
			return $this->_expires = 0;
410
		}
411
		$now = new DateTime();
412
 
413
		if (is_int($expires) || is_numeric($expires)) {
414
			return $this->_expires = $now->format('U') + intval($expires);
415
		}
416
		$now->modify($expires);
417
		return $this->_expires = $now->format('U');
418
	}
419
 
420
/**
421
 * Set cookie
422
 *
423
 * @param string $name Name for cookie
424
 * @param string $value Value for cookie
425
 * @return void
426
 */
427
	protected function _write($name, $value) {
428
		$this->_response->cookie(array(
429
			'name' => $this->name . $name,
430
			'value' => $this->_encrypt($value),
431
			'expire' => $this->_expires,
432
			'path' => $this->path,
433
			'domain' => $this->domain,
434
			'secure' => $this->secure,
435
			'httpOnly' => $this->httpOnly
436
		));
437
 
438
		if (!empty($this->_reset)) {
439
			$this->_expires = $this->_reset;
440
			$this->_reset = null;
441
		}
442
	}
443
 
444
/**
445
 * Sets a cookie expire time to remove cookie value
446
 *
447
 * @param string $name Name of cookie
448
 * @return void
449
 */
450
	protected function _delete($name) {
451
		$this->_response->cookie(array(
452
			'name' => $this->name . $name,
453
			'value' => '',
454
			'expire' => time() - 42000,
455
			'path' => $this->path,
456
			'domain' => $this->domain,
457
			'secure' => $this->secure,
458
			'httpOnly' => $this->httpOnly
459
		));
460
	}
461
 
462
/**
463
 * Encrypts $value using public $type method in Security class
464
 *
465
 * @param string $value Value to encrypt
466
 * @return string Encoded values
467
 */
468
	protected function _encrypt($value) {
469
		if (is_array($value)) {
470
			$value = $this->_implode($value);
471
		}
472
 
473
		if ($this->_encrypted === true) {
474
			$type = $this->_type;
475
			$value = "Q2FrZQ==." . base64_encode(Security::$type($value, $this->key, 'encrypt'));
476
		}
477
		return $value;
478
	}
479
 
480
/**
481
 * Decrypts $value using public $type method in Security class
482
 *
483
 * @param array $values Values to decrypt
484
 * @return string decrypted string
485
 */
486
	protected function _decrypt($values) {
487
		$decrypted = array();
488
		$type = $this->_type;
489
 
490
		foreach ((array)$values as $name => $value) {
491
			if (is_array($value)) {
492
				foreach ($value as $key => $val) {
493
					$pos = strpos($val, 'Q2FrZQ==.');
494
					$decrypted[$name][$key] = $this->_explode($val);
495
 
496
					if ($pos !== false) {
497
						$val = substr($val, 8);
498
						$decrypted[$name][$key] = $this->_explode(Security::$type(base64_decode($val), $this->key, 'decrypt'));
499
					}
500
				}
501
			} else {
502
				$pos = strpos($value, 'Q2FrZQ==.');
503
				$decrypted[$name] = $this->_explode($value);
504
 
505
				if ($pos !== false) {
506
					$value = substr($value, 8);
507
					$decrypted[$name] = $this->_explode(Security::$type(base64_decode($value), $this->key, 'decrypt'));
508
				}
509
			}
510
		}
511
		return $decrypted;
512
	}
513
 
514
/**
515
 * Implode method to keep keys are multidimensional arrays
516
 *
517
 * @param array $array Map of key and values
518
 * @return string A json encoded string.
519
 */
520
	protected function _implode(array $array) {
521
		return json_encode($array);
522
	}
523
 
524
/**
525
 * Explode method to return array from string set in CookieComponent::_implode()
526
 * Maintains reading backwards compatibility with 1.x CookieComponent::_implode().
527
 *
528
 * @param string $string A string containing JSON encoded data, or a bare string.
529
 * @return array Map of key and values
530
 */
531
	protected function _explode($string) {
532
		$first = substr($string, 0, 1);
533
		if ($first === '{' || $first === '[') {
534
			$ret = json_decode($string, true);
535
			return ($ret !== null) ? $ret : $string;
536
		}
537
		$array = array();
538
		foreach (explode(',', $string) as $pair) {
539
			$key = explode('|', $pair);
540
			if (!isset($key[1])) {
541
				return $key[0];
542
			}
543
			$array[$key[0]] = $key[1];
544
		}
545
		return $array;
546
	}
547
}