Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
15403 manish.sha 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
			isset($config['userAgent']) && 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 null;
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
		if (self::started()) {
446
			session_destroy();
447
		}
448
 
449
		$_SESSION = null;
450
		self::$id = null;
451
		self::$_cookieName = null;
452
	}
453
 
454
/**
455
 * Clears the session, the session id, and renews the session.
456
 *
457
 * @return void
458
 */
459
	public static function clear() {
460
		$_SESSION = null;
461
		self::$id = null;
462
		self::renew();
463
	}
464
 
465
/**
466
 * Helper method to initialize a session, based on CakePHP core settings.
467
 *
468
 * Sessions can be configured with a few shortcut names as well as have any number of ini settings declared.
469
 *
470
 * @return void
471
 * @throws CakeSessionException Throws exceptions when ini_set() fails.
472
 */
473
	protected static function _configureSession() {
474
		$sessionConfig = Configure::read('Session');
475
 
476
		if (isset($sessionConfig['defaults'])) {
477
			$defaults = self::_defaultConfig($sessionConfig['defaults']);
478
			if ($defaults) {
479
				$sessionConfig = Hash::merge($defaults, $sessionConfig);
480
			}
481
		}
482
		if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) {
483
			$sessionConfig['ini']['session.cookie_secure'] = 1;
484
		}
485
		if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) {
486
			$sessionConfig['cookieTimeout'] = $sessionConfig['timeout'];
487
		}
488
		if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
489
			$sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
490
		}
491
 
492
		if (!isset($sessionConfig['ini']['session.name'])) {
493
			$sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
494
		}
495
		self::$_cookieName = $sessionConfig['ini']['session.name'];
496
 
497
		if (!empty($sessionConfig['handler'])) {
498
			$sessionConfig['ini']['session.save_handler'] = 'user';
499
		} elseif (!empty($sessionConfig['session.save_path']) && Configure::read('debug')) {
500
			if (!is_dir($sessionConfig['session.save_path'])) {
501
				mkdir($sessionConfig['session.save_path'], 0775, true);
502
			}
503
		}
504
 
505
		if (!isset($sessionConfig['ini']['session.gc_maxlifetime'])) {
506
			$sessionConfig['ini']['session.gc_maxlifetime'] = $sessionConfig['timeout'] * 60;
507
		}
508
		if (!isset($sessionConfig['ini']['session.cookie_httponly'])) {
509
			$sessionConfig['ini']['session.cookie_httponly'] = 1;
510
		}
511
 
512
		if (empty($_SESSION)) {
513
			if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
514
				foreach ($sessionConfig['ini'] as $setting => $value) {
515
					if (ini_set($setting, $value) === false) {
516
						throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
517
					}
518
				}
519
			}
520
		}
521
		if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) {
522
			call_user_func_array('session_set_save_handler', $sessionConfig['handler']);
523
		}
524
		if (!empty($sessionConfig['handler']['engine'])) {
525
			$handler = self::_getHandler($sessionConfig['handler']['engine']);
526
			session_set_save_handler(
527
				array($handler, 'open'),
528
				array($handler, 'close'),
529
				array($handler, 'read'),
530
				array($handler, 'write'),
531
				array($handler, 'destroy'),
532
				array($handler, 'gc')
533
			);
534
		}
535
		Configure::write('Session', $sessionConfig);
536
		self::$sessionTime = self::$time + ($sessionConfig['timeout'] * 60);
537
	}
538
 
539
/**
540
 * Get session cookie name.
541
 *
542
 * @return string
543
 */
544
	protected static function _cookieName() {
545
		if (self::$_cookieName !== null) {
546
			return self::$_cookieName;
547
		}
548
 
549
		self::init();
550
		self::_configureSession();
551
 
552
		return self::$_cookieName = session_name();
553
	}
554
 
555
/**
556
 * Returns whether a session exists
557
 *
558
 * @return bool
559
 */
560
	protected static function _hasSession() {
561
		return self::started() || isset($_COOKIE[self::_cookieName()]);
562
	}
563
 
564
/**
565
 * Find the handler class and make sure it implements the correct interface.
566
 *
567
 * @param string $handler Handler name.
568
 * @return void
569
 * @throws CakeSessionException
570
 */
571
	protected static function _getHandler($handler) {
572
		list($plugin, $class) = pluginSplit($handler, true);
573
		App::uses($class, $plugin . 'Model/Datasource/Session');
574
		if (!class_exists($class)) {
575
			throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class));
576
		}
577
		$handler = new $class();
578
		if ($handler instanceof CakeSessionHandlerInterface) {
579
			return $handler;
580
		}
581
		throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
582
	}
583
 
584
/**
585
 * Get one of the prebaked default session configurations.
586
 *
587
 * @param string $name Config name.
588
 * @return bool|array
589
 */
590
	protected static function _defaultConfig($name) {
591
		$defaults = array(
592
			'php' => array(
593
				'cookie' => 'CAKEPHP',
594
				'timeout' => 240,
595
				'ini' => array(
596
					'session.use_trans_sid' => 0,
597
					'session.cookie_path' => self::$path
598
				)
599
			),
600
			'cake' => array(
601
				'cookie' => 'CAKEPHP',
602
				'timeout' => 240,
603
				'ini' => array(
604
					'session.use_trans_sid' => 0,
605
					'url_rewriter.tags' => '',
606
					'session.serialize_handler' => 'php',
607
					'session.use_cookies' => 1,
608
					'session.cookie_path' => self::$path,
609
					'session.save_path' => TMP . 'sessions',
610
					'session.save_handler' => 'files'
611
				)
612
			),
613
			'cache' => array(
614
				'cookie' => 'CAKEPHP',
615
				'timeout' => 240,
616
				'ini' => array(
617
					'session.use_trans_sid' => 0,
618
					'url_rewriter.tags' => '',
619
					'session.use_cookies' => 1,
620
					'session.cookie_path' => self::$path,
621
					'session.save_handler' => 'user',
622
				),
623
				'handler' => array(
624
					'engine' => 'CacheSession',
625
					'config' => 'default'
626
				)
627
			),
628
			'database' => array(
629
				'cookie' => 'CAKEPHP',
630
				'timeout' => 240,
631
				'ini' => array(
632
					'session.use_trans_sid' => 0,
633
					'url_rewriter.tags' => '',
634
					'session.use_cookies' => 1,
635
					'session.cookie_path' => self::$path,
636
					'session.save_handler' => 'user',
637
					'session.serialize_handler' => 'php',
638
				),
639
				'handler' => array(
640
					'engine' => 'DatabaseSession',
641
					'model' => 'Session'
642
				)
643
			)
644
		);
645
		if (isset($defaults[$name])) {
646
			return $defaults[$name];
647
		}
648
		return false;
649
	}
650
 
651
/**
652
 * Helper method to start a session
653
 *
654
 * @return bool Success
655
 */
656
	protected static function _startSession() {
657
		self::init();
658
		session_write_close();
659
		self::_configureSession();
660
 
661
		if (headers_sent()) {
662
			if (empty($_SESSION)) {
663
				$_SESSION = array();
664
			}
665
		} else {
666
			// For IE<=8
667
			session_cache_limiter("must-revalidate");
668
			session_start();
669
		}
670
		return true;
671
	}
672
 
673
/**
674
 * Helper method to create a new session.
675
 *
676
 * @return void
677
 */
678
	protected static function _checkValid() {
679
		$config = self::read('Config');
680
		if ($config) {
681
			$sessionConfig = Configure::read('Session');
682
 
683
			if (self::valid()) {
684
				self::write('Config.time', self::$sessionTime);
685
				if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) {
686
					$check = $config['countdown'];
687
					$check -= 1;
688
					self::write('Config.countdown', $check);
689
 
690
					if ($check < 1) {
691
						self::renew();
692
						self::write('Config.countdown', self::$requestCountdown);
693
					}
694
				}
695
			} else {
696
				$_SESSION = array();
697
				self::destroy();
698
				self::_setError(1, 'Session Highjacking Attempted !!!');
699
				self::_startSession();
700
				self::_writeConfig();
701
			}
702
		} else {
703
			self::_writeConfig();
704
		}
705
	}
706
 
707
/**
708
 * Writes configuration variables to the session
709
 *
710
 * @return void
711
 */
712
	protected static function _writeConfig() {
713
		self::write('Config.userAgent', self::$_userAgent);
714
		self::write('Config.time', self::$sessionTime);
715
		self::write('Config.countdown', self::$requestCountdown);
716
	}
717
 
718
/**
719
 * Restarts this session.
720
 *
721
 * @return void
722
 */
723
	public static function renew() {
724
		if (session_id() === '') {
725
			return;
726
		}
727
		if (isset($_COOKIE[session_name()])) {
728
			setcookie(Configure::read('Session.cookie'), '', time() - 42000, self::$path);
729
		}
730
		session_regenerate_id(true);
731
	}
732
 
733
/**
734
 * Helper method to set an internal error message.
735
 *
736
 * @param int $errorNumber Number of the error
737
 * @param string $errorMessage Description of the error
738
 * @return void
739
 */
740
	protected static function _setError($errorNumber, $errorMessage) {
741
		if (self::$error === false) {
742
			self::$error = array();
743
		}
744
		self::$error[$errorNumber] = $errorMessage;
745
		self::$lastError = $errorNumber;
746
	}
747
 
748
}