Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
16591 anikendra 1
<?php
2
/**
3
 * Session class for CakePHP.
4
 *
5
 * CakePHP abstracts the handling of sessions.
6
 * There are several convenient methods to access session information.
7
 * This class is the implementation of those methods.
8
 * They are mostly used by the Session Component.
9
 *
10
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
11
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * For full copyright and license information, please see the LICENSE.txt
15
 * Redistributions of files must retain the above copyright notice.
16
 *
17
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
18
 * @link          http://cakephp.org CakePHP(tm) Project
19
 * @package       Cake.Model.Datasource
20
 * @since         CakePHP(tm) v .0.10.0.1222
21
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
22
 */
23
 
24
App::uses('Hash', 'Utility');
25
App::uses('Security', 'Utility');
26
 
27
/**
28
 * Session class for CakePHP.
29
 *
30
 * CakePHP abstracts the handling of sessions. There are several convenient methods to access session information.
31
 * This class is the implementation of those methods. They are mostly used by the Session Component.
32
 *
33
 * @package       Cake.Model.Datasource
34
 */
35
class CakeSession {
36
 
37
/**
38
 * True if the Session is still valid
39
 *
40
 * @var bool
41
 */
42
	public static $valid = false;
43
 
44
/**
45
 * Error messages for this session
46
 *
47
 * @var array
48
 */
49
	public static $error = false;
50
 
51
/**
52
 * User agent string
53
 *
54
 * @var string
55
 */
56
	protected static $_userAgent = '';
57
 
58
/**
59
 * Path to where the session is active.
60
 *
61
 * @var string
62
 */
63
	public static $path = '/';
64
 
65
/**
66
 * Error number of last occurred error
67
 *
68
 * @var int
69
 */
70
	public static $lastError = null;
71
 
72
/**
73
 * Start time for this session.
74
 *
75
 * @var int
76
 */
77
	public static $time = false;
78
 
79
/**
80
 * Cookie lifetime
81
 *
82
 * @var int
83
 */
84
	public static $cookieLifeTime;
85
 
86
/**
87
 * Time when this session becomes invalid.
88
 *
89
 * @var int
90
 */
91
	public static $sessionTime = false;
92
 
93
/**
94
 * Current Session id
95
 *
96
 * @var string
97
 */
98
	public static $id = null;
99
 
100
/**
101
 * Hostname
102
 *
103
 * @var string
104
 */
105
	public static $host = null;
106
 
107
/**
108
 * Session timeout multiplier factor
109
 *
110
 * @var int
111
 */
112
	public static $timeout = null;
113
 
114
/**
115
 * Number of requests that can occur during a session time without the session being renewed.
116
 * This feature is only used when config value `Session.autoRegenerate` is set to true.
117
 *
118
 * @var int
119
 * @see CakeSession::_checkValid()
120
 */
121
	public static $requestCountdown = 10;
122
 
123
/**
124
 * Whether or not the init function in this class was already called
125
 *
126
 * @var bool
127
 */
128
	protected static $_initialized = false;
129
 
130
/**
131
 * Session cookie name
132
 *
133
 * @var string
134
 */
135
	protected static $_cookieName = null;
136
 
137
/**
138
 * Pseudo constructor.
139
 *
140
 * @param string|null $base The base path for the Session
141
 * @return void
142
 */
143
	public static function init($base = null) {
144
		static::$time = time();
145
 
146
		if (env('HTTP_USER_AGENT')) {
147
			static::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
148
		}
149
 
150
		static::_setPath($base);
151
		static::_setHost(env('HTTP_HOST'));
152
 
153
		if (!static::$_initialized) {
154
			register_shutdown_function('session_write_close');
155
		}
156
 
157
		static::$_initialized = true;
158
	}
159
 
160
/**
161
 * Setup the Path variable
162
 *
163
 * @param string|null $base base path
164
 * @return void
165
 */
166
	protected static function _setPath($base = null) {
167
		if (empty($base)) {
168
			static::$path = '/';
169
			return;
170
		}
171
		if (strpos($base, 'index.php') !== false) {
172
			$base = str_replace('index.php', '', $base);
173
		}
174
		if (strpos($base, '?') !== false) {
175
			$base = str_replace('?', '', $base);
176
		}
177
		static::$path = $base;
178
	}
179
 
180
/**
181
 * Set the host name
182
 *
183
 * @param string $host Hostname
184
 * @return void
185
 */
186
	protected static function _setHost($host) {
187
		static::$host = $host;
188
		if (strpos(static::$host, ':') !== false) {
189
			static::$host = substr(static::$host, 0, strpos(static::$host, ':'));
190
		}
191
	}
192
 
193
/**
194
 * Starts the Session.
195
 *
196
 * @return bool True if session was started
197
 */
198
	public static function start() {
199
		if (static::started()) {
200
			return true;
201
		}
202
 
203
		$id = static::id();
204
		static::_startSession();
205
 
206
		if (!$id && static::started()) {
207
			static::_checkValid();
208
		}
209
 
210
		static::$error = false;
211
		static::$valid = true;
212
		return static::started();
213
	}
214
 
215
/**
216
 * Determine if Session has been started.
217
 *
218
 * @return bool True if session has been started.
219
 */
220
	public static function started() {
221
		return isset($_SESSION) && session_id();
222
	}
223
 
224
/**
225
 * Returns true if given variable is set in session.
226
 *
227
 * @param string $name Variable name to check for
228
 * @return bool True if variable is there
229
 */
230
	public static function check($name) {
231
		if (empty($name) || !static::_hasSession() || !static::start()) {
232
			return false;
233
		}
234
 
235
		return Hash::get($_SESSION, $name) !== null;
236
	}
237
 
238
/**
239
 * Returns the session id.
240
 * Calling this method will not auto start the session. You might have to manually
241
 * assert a started session.
242
 *
243
 * Passing an id into it, you can also replace the session id if the session
244
 * has not already been started.
245
 * Note that depending on the session handler, not all characters are allowed
246
 * within the session id. For example, the file session handler only allows
247
 * characters in the range a-z A-Z 0-9 , (comma) and - (minus).
248
 *
249
 * @param string|null $id Id to replace the current session id
250
 * @return string Session id
251
 */
252
	public static function id($id = null) {
253
		if ($id) {
254
			static::$id = $id;
255
			session_id(static::$id);
256
		}
257
		if (static::started()) {
258
			return session_id();
259
		}
260
		return static::$id;
261
	}
262
 
263
/**
264
 * Removes a variable from session.
265
 *
266
 * @param string $name Session variable to remove
267
 * @return bool Success
268
 */
269
	public static function delete($name) {
270
		if (static::check($name)) {
271
			static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
272
			return !static::check($name);
273
		}
274
		return false;
275
	}
276
 
277
/**
278
 * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself.
279
 *
280
 * @param array &$old Set of old variables => values
281
 * @param array $new New set of variable => value
282
 * @return void
283
 */
284
	protected static function _overwrite(&$old, $new) {
285
		if (!empty($old)) {
286
			foreach ($old as $key => $var) {
287
				if (!isset($new[$key])) {
288
					unset($old[$key]);
289
				}
290
			}
291
		}
292
		foreach ($new as $key => $var) {
293
			$old[$key] = $var;
294
		}
295
	}
296
 
297
/**
298
 * Return error description for given error number.
299
 *
300
 * @param int $errorNumber Error to set
301
 * @return string Error as string
302
 */
303
	protected static function _error($errorNumber) {
304
		if (!is_array(static::$error) || !array_key_exists($errorNumber, static::$error)) {
305
			return false;
306
		}
307
		return static::$error[$errorNumber];
308
	}
309
 
310
/**
311
 * Returns last occurred error as a string, if any.
312
 *
313
 * @return mixed Error description as a string, or false.
314
 */
315
	public static function error() {
316
		if (static::$lastError) {
317
			return static::_error(static::$lastError);
318
		}
319
		return false;
320
	}
321
 
322
/**
323
 * Returns true if session is valid.
324
 *
325
 * @return bool Success
326
 */
327
	public static function valid() {
328
		if (static::start() && static::read('Config')) {
329
			if (static::_validAgentAndTime() && static::$error === false) {
330
				static::$valid = true;
331
			} else {
332
				static::$valid = false;
333
				static::_setError(1, 'Session Highjacking Attempted !!!');
334
			}
335
		}
336
		return static::$valid;
337
	}
338
 
339
/**
340
 * Tests that the user agent is valid and that the session hasn't 'timed out'.
341
 * Since timeouts are implemented in CakeSession it checks the current static::$time
342
 * against the time the session is set to expire. The User agent is only checked
343
 * if Session.checkAgent == true.
344
 *
345
 * @return bool
346
 */
347
	protected static function _validAgentAndTime() {
348
		$config = static::read('Config');
349
		$validAgent = (
350
			Configure::read('Session.checkAgent') === false ||
351
			isset($config['userAgent']) && static::$_userAgent === $config['userAgent']
352
		);
353
		return ($validAgent && static::$time <= $config['time']);
354
	}
355
 
356
/**
357
 * Get / Set the user agent
358
 *
359
 * @param string|null $userAgent Set the user agent
360
 * @return string Current user agent
361
 */
362
	public static function userAgent($userAgent = null) {
363
		if ($userAgent) {
364
			static::$_userAgent = $userAgent;
365
		}
366
		if (empty(static::$_userAgent)) {
367
			CakeSession::init(static::$path);
368
		}
369
		return static::$_userAgent;
370
	}
371
 
372
/**
373
 * Returns given session variable, or all of them, if no parameters given.
374
 *
375
 * @param string|null $name The name of the session variable (or a path as sent to Set.extract)
376
 * @return mixed The value of the session variable, null if session not available,
377
 *   session not started, or provided name not found in the session, false on failure.
378
 */
379
	public static function read($name = null) {
380
		if (empty($name) && $name !== null) {
381
			return null;
382
		}
383
		if (!static::_hasSession() || !static::start()) {
384
			return null;
385
		}
386
		if ($name === null) {
387
			return static::_returnSessionVars();
388
		}
389
		$result = Hash::get($_SESSION, $name);
390
 
391
		if (isset($result)) {
392
			return $result;
393
		}
394
		return null;
395
	}
396
 
397
/**
398
 * Returns all session variables.
399
 *
400
 * @return mixed Full $_SESSION array, or false on error.
401
 */
402
	protected static function _returnSessionVars() {
403
		if (!empty($_SESSION)) {
404
			return $_SESSION;
405
		}
406
		static::_setError(2, 'No Session vars set');
407
		return false;
408
	}
409
 
410
/**
411
 * Writes value to given session variable name.
412
 *
413
 * @param string|array $name Name of variable
414
 * @param string $value Value to write
415
 * @return bool True if the write was successful, false if the write failed
416
 */
417
	public static function write($name, $value = null) {
418
		if (empty($name) || !static::start()) {
419
			return false;
420
		}
421
 
422
		$write = $name;
423
		if (!is_array($name)) {
424
			$write = array($name => $value);
425
		}
426
		foreach ($write as $key => $val) {
427
			static::_overwrite($_SESSION, Hash::insert($_SESSION, $key, $val));
428
			if (Hash::get($_SESSION, $key) !== $val) {
429
				return false;
430
			}
431
		}
432
		return true;
433
	}
434
 
435
/**
436
 * Reads and deletes a variable from session.
437
 *
438
 * @param string $name The key to read and remove (or a path as sent to Hash.extract).
439
 * @return mixed The value of the session variable, null if session not available,
440
 *   session not started, or provided name not found in the session.
441
 */
442
	public static function consume($name) {
443
		if (empty($name)) {
444
			return null;
445
		}
446
		$value = static::read($name);
447
		if ($value !== null) {
448
			static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
449
		}
450
		return $value;
451
	}
452
 
453
/**
454
 * Helper method to destroy invalid sessions.
455
 *
456
 * @return void
457
 */
458
	public static function destroy() {
459
		if (!static::started()) {
460
			static::_startSession();
461
		}
462
 
463
		if (static::started()) {
464
			session_destroy();
465
		}
466
 
467
		$_SESSION = null;
468
		static::$id = null;
469
		static::$_cookieName = null;
470
	}
471
 
472
/**
473
 * Clears the session.
474
 *
475
 * Optionally also clears the session id and renews the session.
476
 *
477
 * @param bool $renew If the session should also be renewed. Defaults to true.
478
 * @return void
479
 */
480
	public static function clear($renew = true) {
481
		if (!$renew) {
482
			$_SESSION = array();
483
			return;
484
		}
485
 
486
		$_SESSION = null;
487
		static::$id = null;
488
		static::renew();
489
	}
490
 
491
/**
492
 * Helper method to initialize a session, based on CakePHP core settings.
493
 *
494
 * Sessions can be configured with a few shortcut names as well as have any number of ini settings declared.
495
 *
496
 * @return void
497
 * @throws CakeSessionException Throws exceptions when ini_set() fails.
498
 */
499
	protected static function _configureSession() {
500
		$sessionConfig = Configure::read('Session');
501
 
502
		if (isset($sessionConfig['defaults'])) {
503
			$defaults = static::_defaultConfig($sessionConfig['defaults']);
504
			if ($defaults) {
505
				$sessionConfig = Hash::merge($defaults, $sessionConfig);
506
			}
507
		}
508
		if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) {
509
			$sessionConfig['ini']['session.cookie_secure'] = 1;
510
		}
511
		if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) {
512
			$sessionConfig['cookieTimeout'] = $sessionConfig['timeout'];
513
		}
514
		if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
515
			$sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
516
		}
517
 
518
		if (!isset($sessionConfig['ini']['session.name'])) {
519
			$sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
520
		}
521
		static::$_cookieName = $sessionConfig['ini']['session.name'];
522
 
523
		if (!empty($sessionConfig['handler'])) {
524
			$sessionConfig['ini']['session.save_handler'] = 'user';
525
		} elseif (!empty($sessionConfig['session.save_path']) && Configure::read('debug')) {
526
			if (!is_dir($sessionConfig['session.save_path'])) {
527
				mkdir($sessionConfig['session.save_path'], 0775, true);
528
			}
529
		}
530
 
531
		if (!isset($sessionConfig['ini']['session.gc_maxlifetime'])) {
532
			$sessionConfig['ini']['session.gc_maxlifetime'] = $sessionConfig['timeout'] * 60;
533
		}
534
		if (!isset($sessionConfig['ini']['session.cookie_httponly'])) {
535
			$sessionConfig['ini']['session.cookie_httponly'] = 1;
536
		}
537
 
538
		if (empty($_SESSION)) {
539
			if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
540
				foreach ($sessionConfig['ini'] as $setting => $value) {
541
					if (ini_set($setting, $value) === false) {
542
						throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
543
					}
544
				}
545
			}
546
		}
547
		if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) {
548
			call_user_func_array('session_set_save_handler', $sessionConfig['handler']);
549
		}
550
		if (!empty($sessionConfig['handler']['engine'])) {
551
			$handler = static::_getHandler($sessionConfig['handler']['engine']);
552
			session_set_save_handler(
553
				array($handler, 'open'),
554
				array($handler, 'close'),
555
				array($handler, 'read'),
556
				array($handler, 'write'),
557
				array($handler, 'destroy'),
558
				array($handler, 'gc')
559
			);
560
		}
561
		Configure::write('Session', $sessionConfig);
562
		static::$sessionTime = static::$time + ($sessionConfig['timeout'] * 60);
563
	}
564
 
565
/**
566
 * Get session cookie name.
567
 *
568
 * @return string
569
 */
570
	protected static function _cookieName() {
571
		if (static::$_cookieName !== null) {
572
			return static::$_cookieName;
573
		}
574
 
575
		static::init();
576
		static::_configureSession();
577
 
578
		return static::$_cookieName = session_name();
579
	}
580
 
581
/**
582
 * Returns whether a session exists
583
 *
584
 * @return bool
585
 */
586
	protected static function _hasSession() {
587
		return static::started() || isset($_COOKIE[static::_cookieName()]);
588
	}
589
 
590
/**
591
 * Find the handler class and make sure it implements the correct interface.
592
 *
593
 * @param string $handler Handler name.
594
 * @return void
595
 * @throws CakeSessionException
596
 */
597
	protected static function _getHandler($handler) {
598
		list($plugin, $class) = pluginSplit($handler, true);
599
		App::uses($class, $plugin . 'Model/Datasource/Session');
600
		if (!class_exists($class)) {
601
			throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class));
602
		}
603
		$handler = new $class();
604
		if ($handler instanceof CakeSessionHandlerInterface) {
605
			return $handler;
606
		}
607
		throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
608
	}
609
 
610
/**
611
 * Get one of the prebaked default session configurations.
612
 *
613
 * @param string $name Config name.
614
 * @return bool|array
615
 */
616
	protected static function _defaultConfig($name) {
617
		$defaults = array(
618
			'php' => array(
619
				'cookie' => 'CAKEPHP',
620
				'timeout' => 240,
621
				'ini' => array(
622
					'session.use_trans_sid' => 0,
623
					'session.cookie_path' => static::$path
624
				)
625
			),
626
			'cake' => array(
627
				'cookie' => 'CAKEPHP',
628
				'timeout' => 240,
629
				'ini' => array(
630
					'session.use_trans_sid' => 0,
631
					'url_rewriter.tags' => '',
632
					'session.serialize_handler' => 'php',
633
					'session.use_cookies' => 1,
634
					'session.cookie_path' => static::$path,
635
					'session.save_path' => TMP . 'sessions',
636
					'session.save_handler' => 'files'
637
				)
638
			),
639
			'cache' => array(
640
				'cookie' => 'CAKEPHP',
641
				'timeout' => 240,
642
				'ini' => array(
643
					'session.use_trans_sid' => 0,
644
					'url_rewriter.tags' => '',
645
					'session.use_cookies' => 1,
646
					'session.cookie_path' => static::$path,
647
					'session.save_handler' => 'user',
648
				),
649
				'handler' => array(
650
					'engine' => 'CacheSession',
651
					'config' => 'default'
652
				)
653
			),
654
			'database' => array(
655
				'cookie' => 'CAKEPHP',
656
				'timeout' => 240,
657
				'ini' => array(
658
					'session.use_trans_sid' => 0,
659
					'url_rewriter.tags' => '',
660
					'session.use_cookies' => 1,
661
					'session.cookie_path' => static::$path,
662
					'session.save_handler' => 'user',
663
					'session.serialize_handler' => 'php',
664
				),
665
				'handler' => array(
666
					'engine' => 'DatabaseSession',
667
					'model' => 'Session'
668
				)
669
			)
670
		);
671
		if (isset($defaults[$name])) {
672
			return $defaults[$name];
673
		}
674
		return false;
675
	}
676
 
677
/**
678
 * Helper method to start a session
679
 *
680
 * @return bool Success
681
 */
682
	protected static function _startSession() {
683
		static::init();
684
		session_write_close();
685
		static::_configureSession();
686
 
687
		if (headers_sent()) {
688
			if (empty($_SESSION)) {
689
				$_SESSION = array();
690
			}
691
		} else {
692
			// For IE<=8
693
			session_cache_limiter("must-revalidate");
694
			session_start();
695
		}
696
		return true;
697
	}
698
 
699
/**
700
 * Helper method to create a new session.
701
 *
702
 * @return void
703
 */
704
	protected static function _checkValid() {
705
		$config = static::read('Config');
706
		if ($config) {
707
			$sessionConfig = Configure::read('Session');
708
 
709
			if (static::valid()) {
710
				static::write('Config.time', static::$sessionTime);
711
				if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) {
712
					$check = $config['countdown'];
713
					$check -= 1;
714
					static::write('Config.countdown', $check);
715
 
716
					if ($check < 1) {
717
						static::renew();
718
						static::write('Config.countdown', static::$requestCountdown);
719
					}
720
				}
721
			} else {
722
				$_SESSION = array();
723
				static::destroy();
724
				static::_setError(1, 'Session Highjacking Attempted !!!');
725
				static::_startSession();
726
				static::_writeConfig();
727
			}
728
		} else {
729
			static::_writeConfig();
730
		}
731
	}
732
 
733
/**
734
 * Writes configuration variables to the session
735
 *
736
 * @return void
737
 */
738
	protected static function _writeConfig() {
739
		static::write('Config.userAgent', static::$_userAgent);
740
		static::write('Config.time', static::$sessionTime);
741
		static::write('Config.countdown', static::$requestCountdown);
742
	}
743
 
744
/**
745
 * Restarts this session.
746
 *
747
 * @return void
748
 */
749
	public static function renew() {
750
		if (session_id() === '') {
751
			return;
752
		}
753
		if (isset($_COOKIE[session_name()])) {
754
			setcookie(Configure::read('Session.cookie'), '', time() - 42000, static::$path);
755
		}
756
		session_regenerate_id(true);
757
	}
758
 
759
/**
760
 * Helper method to set an internal error message.
761
 *
762
 * @param int $errorNumber Number of the error
763
 * @param string $errorMessage Description of the error
764
 * @return void
765
 */
766
	protected static function _setError($errorNumber, $errorMessage) {
767
		if (static::$error === false) {
768
			static::$error = array();
769
		}
770
		static::$error[$errorNumber] = $errorMessage;
771
		static::$lastError = $errorNumber;
772
	}
773
 
774
}