Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12345 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 $base The base path for the Session
141
 * @return void
142
 */
143
	public static function init($base = null) {
144
		self::$time = time();
145
 
146
		if (env('HTTP_USER_AGENT')) {
147
			self::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
148
		}
149
 
150
		self::_setPath($base);
151
		self::_setHost(env('HTTP_HOST'));
152
 
153
		if (!self::$_initialized) {
154
			register_shutdown_function('session_write_close');
155
		}
156
 
157
		self::$_initialized = true;
158
	}
159
 
160
/**
161
 * Setup the Path variable
162
 *
163
 * @param string $base base path
164
 * @return void
165
 */
166
	protected static function _setPath($base = null) {
167
		if (empty($base)) {
168
			self::$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
		self::$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
		self::$host = $host;
188
		if (strpos(self::$host, ':') !== false) {
189
			self::$host = substr(self::$host, 0, strpos(self::$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 (self::started()) {
200
			return true;
201
		}
202
 
203
		$id = self::id();
204
		self::_startSession();
205
 
206
		if (!$id && self::started()) {
207
			self::_checkValid();
208
		}
209
 
210
		self::$error = false;
211
		self::$valid = true;
212
		return self::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 = null) {
231
		if (empty($name) || !self::_hasSession() || !self::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 $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
			self::$id = $id;
255
			session_id(self::$id);
256
		}
257
		if (self::started()) {
258
			return session_id();
259
		}
260
		return self::$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 (self::check($name)) {
271
			self::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
272
			return !self::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(self::$error) || !array_key_exists($errorNumber, self::$error)) {
305
			return false;
306
		}
307
		return self::$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 (self::$lastError) {
317
			return self::_error(self::$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 (self::start() && self::read('Config')) {
329
			if (self::_validAgentAndTime() && self::$error === false) {
330
				self::$valid = true;
331
			} else {
332
				self::$valid = false;
333
				self::_setError(1, 'Session Highjacking Attempted !!!');
334
			}
335
		}
336
		return self::$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 self::$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 = self::read('Config');
349
		$validAgent = (
350
			Configure::read('Session.checkAgent') === false ||
351
			self::$_userAgent == $config['userAgent']
352
		);
353
		return ($validAgent && self::$time <= $config['time']);
354
	}
355
 
356
/**
357
 * Get / Set the user agent
358
 *
359
 * @param string $userAgent Set the user agent
360
 * @return string Current user agent
361
 */
362
	public static function userAgent($userAgent = null) {
363
		if ($userAgent) {
364
			self::$_userAgent = $userAgent;
365
		}
366
		if (empty(self::$_userAgent)) {
367
			CakeSession::init(self::$path);
368
		}
369
		return self::$_userAgent;
370
	}
371
 
372
/**
373
 * Returns given session variable, or all of them, if no parameters given.
374
 *
375
 * @param string|array $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.
378
 */
379
	public static function read($name = null) {
380
		if (empty($name) && $name !== null) {
381
			return false;
382
		}
383
		if (!self::_hasSession() || !self::start()) {
384
			return null;
385
		}
386
		if ($name === null) {
387
			return self::_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
		self::_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) || !self::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
			self::_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
 * Helper method to destroy invalid sessions.
437
 *
438
 * @return void
439
 */
440
	public static function destroy() {
441
		if (!self::started()) {
442
			self::_startSession();
443
		}
444
 
445
		session_destroy();
446
 
447
		$_SESSION = null;
448
		self::$id = null;
449
		self::$_cookieName = null;
450
	}
451
 
452
/**
453
 * Clears the session, the session id, and renews the session.
454
 *
455
 * @return void
456
 */
457
	public static function clear() {
458
		$_SESSION = null;
459
		self::$id = null;
460
		self::renew();
461
	}
462
 
463
/**
464
 * Helper method to initialize a session, based on CakePHP core settings.
465
 *
466
 * Sessions can be configured with a few shortcut names as well as have any number of ini settings declared.
467
 *
468
 * @return void
469
 * @throws CakeSessionException Throws exceptions when ini_set() fails.
470
 */
471
	protected static function _configureSession() {
472
		$sessionConfig = Configure::read('Session');
473
 
474
		if (isset($sessionConfig['defaults'])) {
475
			$defaults = self::_defaultConfig($sessionConfig['defaults']);
476
			if ($defaults) {
477
				$sessionConfig = Hash::merge($defaults, $sessionConfig);
478
			}
479
		}
480
		if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) {
481
			$sessionConfig['ini']['session.cookie_secure'] = 1;
482
		}
483
		if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) {
484
			$sessionConfig['cookieTimeout'] = $sessionConfig['timeout'];
485
		}
486
		if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
487
			$sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
488
		}
489
 
490
		if (!isset($sessionConfig['ini']['session.name'])) {
491
			$sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
492
		}
493
		self::$_cookieName = $sessionConfig['ini']['session.name'];
494
 
495
		if (!empty($sessionConfig['handler'])) {
496
			$sessionConfig['ini']['session.save_handler'] = 'user';
497
		}
498
		if (!isset($sessionConfig['ini']['session.gc_maxlifetime'])) {
499
			$sessionConfig['ini']['session.gc_maxlifetime'] = $sessionConfig['timeout'] * 60;
500
		}
501
		if (!isset($sessionConfig['ini']['session.cookie_httponly'])) {
502
			$sessionConfig['ini']['session.cookie_httponly'] = 1;
503
		}
504
 
505
		if (empty($_SESSION)) {
506
			if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
507
				foreach ($sessionConfig['ini'] as $setting => $value) {
508
					if (ini_set($setting, $value) === false) {
509
						throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
510
					}
511
				}
512
			}
513
		}
514
		if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) {
515
			call_user_func_array('session_set_save_handler', $sessionConfig['handler']);
516
		}
517
		if (!empty($sessionConfig['handler']['engine'])) {
518
			$handler = self::_getHandler($sessionConfig['handler']['engine']);
519
			session_set_save_handler(
520
				array($handler, 'open'),
521
				array($handler, 'close'),
522
				array($handler, 'read'),
523
				array($handler, 'write'),
524
				array($handler, 'destroy'),
525
				array($handler, 'gc')
526
			);
527
		}
528
		Configure::write('Session', $sessionConfig);
529
		self::$sessionTime = self::$time + ($sessionConfig['timeout'] * 60);
530
	}
531
 
532
/**
533
 * Get session cookie name.
534
 *
535
 * @return string
536
 */
537
	protected static function _cookieName() {
538
		if (self::$_cookieName !== null) {
539
			return self::$_cookieName;
540
		}
541
 
542
		self::init();
543
		self::_configureSession();
544
 
545
		return self::$_cookieName = session_name();
546
	}
547
 
548
/**
549
 * Returns whether a session exists
550
 *
551
 * @return bool
552
 */
553
	protected static function _hasSession() {
554
		return self::started() || isset($_COOKIE[self::_cookieName()]);
555
	}
556
 
557
/**
558
 * Find the handler class and make sure it implements the correct interface.
559
 *
560
 * @param string $handler Handler name.
561
 * @return void
562
 * @throws CakeSessionException
563
 */
564
	protected static function _getHandler($handler) {
565
		list($plugin, $class) = pluginSplit($handler, true);
566
		App::uses($class, $plugin . 'Model/Datasource/Session');
567
		if (!class_exists($class)) {
568
			throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class));
569
		}
570
		$handler = new $class();
571
		if ($handler instanceof CakeSessionHandlerInterface) {
572
			return $handler;
573
		}
574
		throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
575
	}
576
 
577
/**
578
 * Get one of the prebaked default session configurations.
579
 *
580
 * @param string $name Config name.
581
 * @return bool|array
582
 */
583
	protected static function _defaultConfig($name) {
584
		$defaults = array(
585
			'php' => array(
586
				'cookie' => 'CAKEPHP',
587
				'timeout' => 240,
588
				'ini' => array(
589
					'session.use_trans_sid' => 0,
590
					'session.cookie_path' => self::$path
591
				)
592
			),
593
			'cake' => array(
594
				'cookie' => 'CAKEPHP',
595
				'timeout' => 240,
596
				'ini' => array(
597
					'session.use_trans_sid' => 0,
598
					'url_rewriter.tags' => '',
599
					'session.serialize_handler' => 'php',
600
					'session.use_cookies' => 1,
601
					'session.cookie_path' => self::$path,
602
					'session.save_path' => TMP . 'sessions',
603
					'session.save_handler' => 'files'
604
				)
605
			),
606
			'cache' => array(
607
				'cookie' => 'CAKEPHP',
608
				'timeout' => 240,
609
				'ini' => array(
610
					'session.use_trans_sid' => 0,
611
					'url_rewriter.tags' => '',
612
					'session.use_cookies' => 1,
613
					'session.cookie_path' => self::$path,
614
					'session.save_handler' => 'user',
615
				),
616
				'handler' => array(
617
					'engine' => 'CacheSession',
618
					'config' => 'default'
619
				)
620
			),
621
			'database' => array(
622
				'cookie' => 'CAKEPHP',
623
				'timeout' => 240,
624
				'ini' => array(
625
					'session.use_trans_sid' => 0,
626
					'url_rewriter.tags' => '',
627
					'session.use_cookies' => 1,
628
					'session.cookie_path' => self::$path,
629
					'session.save_handler' => 'user',
630
					'session.serialize_handler' => 'php',
631
				),
632
				'handler' => array(
633
					'engine' => 'DatabaseSession',
634
					'model' => 'Session'
635
				)
636
			)
637
		);
638
		if (isset($defaults[$name])) {
639
			return $defaults[$name];
640
		}
641
		return false;
642
	}
643
 
644
/**
645
 * Helper method to start a session
646
 *
647
 * @return bool Success
648
 */
649
	protected static function _startSession() {
650
		self::init();
651
		session_write_close();
652
		self::_configureSession();
653
 
654
		if (headers_sent()) {
655
			if (empty($_SESSION)) {
656
				$_SESSION = array();
657
			}
658
		} else {
659
			// For IE<=8
660
			session_cache_limiter("must-revalidate");
661
			session_start();
662
		}
663
		return true;
664
	}
665
 
666
/**
667
 * Helper method to create a new session.
668
 *
669
 * @return void
670
 */
671
	protected static function _checkValid() {
672
		$config = self::read('Config');
673
		if ($config) {
674
			$sessionConfig = Configure::read('Session');
675
 
676
			if (self::valid()) {
677
				self::write('Config.time', self::$sessionTime);
678
				if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) {
679
					$check = $config['countdown'];
680
					$check -= 1;
681
					self::write('Config.countdown', $check);
682
 
683
					if ($check < 1) {
684
						self::renew();
685
						self::write('Config.countdown', self::$requestCountdown);
686
					}
687
				}
688
			} else {
689
				$_SESSION = array();
690
				self::destroy();
691
				self::_setError(1, 'Session Highjacking Attempted !!!');
692
				self::_startSession();
693
				self::_writeConfig();
694
			}
695
		} else {
696
			self::_writeConfig();
697
		}
698
	}
699
 
700
/**
701
 * Writes configuration variables to the session
702
 *
703
 * @return void
704
 */
705
	protected static function _writeConfig() {
706
		self::write('Config.userAgent', self::$_userAgent);
707
		self::write('Config.time', self::$sessionTime);
708
		self::write('Config.countdown', self::$requestCountdown);
709
	}
710
 
711
/**
712
 * Restarts this session.
713
 *
714
 * @return void
715
 */
716
	public static function renew() {
717
		if (!session_id()) {
718
			return;
719
		}
720
		if (isset($_COOKIE[session_name()])) {
721
			setcookie(Configure::read('Session.cookie'), '', time() - 42000, self::$path);
722
		}
723
		session_regenerate_id(true);
724
	}
725
 
726
/**
727
 * Helper method to set an internal error message.
728
 *
729
 * @param int $errorNumber Number of the error
730
 * @param string $errorMessage Description of the error
731
 * @return void
732
 */
733
	protected static function _setError($errorNumber, $errorMessage) {
734
		if (self::$error === false) {
735
			self::$error = array();
736
		}
737
		self::$error[$errorNumber] = $errorMessage;
738
		self::$lastError = $errorNumber;
739
	}
740
 
741
}