1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 | <?php
if ( !class_exists('FSO') )
die('Framework SuperObject base class must be loaded to use its modules!');
/**
* Database-based sessions
*/
class Session extends FSO_Module
{
// Database to use for the sessions
private $db = null;
// Encryption modules
private $data_crypt, $cookie_crypt;
// Configuration
private $lifetime;
// Current data
private $ip_address, $user_agent;
private $cookie = array();
private $data = array();
// Garbage collection probability (/100) - chance that old sessions will be removed
private $gc_prob = 5;
public function __construct(&$db, $sess_name, $cookie_key, $lifetime = null, $path = null, $domain = null)
{
parent::__construct();
// Database
$this->db =& $db;
// Module parameters
$domain = is_null($domain)
? (
(($pos = strpos($_SERVER['HTTP_HOST'], ':')) !== FALSE)
? substr($_SERVER['HTTP_HOST'], 0, $pos)
: $_SERVER['HTTP_HOST']
)
: $domain;
$path = is_null($path) ? '/' : $path;
$this->lifetime = $lifetime = is_null($lifetime) ? ini_get('session.gc_maxlifetime') : $lifetime;
// User information
$ip_address = $_SERVER['REMOTE_ADDR'];
$user_agent = substr($_SERVER['HTTP_USER_AGENT'], 0, 64);
// Cookie encryption
$this->cookie_crypt =& FSO::make_module('encrypt', array(md5($cookie_key)));
// Try and load an existing session, remove it if its invalid
if ( isset($_COOKIE[$sess_name]) )
{
$cookie = $this->cookie_crypt->decode_var($_COOKIE[$sess_name]);
if ( !empty($cookie) )
{
// Find the session in the database
$sess = $this->FSO->db->query('SELECT * FROM `session` WHERE session_id = ?',
$cookie['sid'])->fetch(PDO::FETCH_ASSOC);
// Check that the session exists, hasn't expired, and matches the stored info
if ( !$sess || $ip_address != $sess['ip_address'] ||
$user_agent != $sess['user_agent'] ||
$sess['last_activity'] < time() - $lifetime )
{
$this->FSO->db->query('DELETE FROM `session` WHERE session_id = ?', $cookie['sid']);
}
else
{
$this->cookie = $cookie;
$this->data_crypt =& FSO::make_module('encrypt', array($cookie['key']));
$this->data = $this->data_crypt->decode_var($sess['data']);
}
}
}
// Create a new session if one doesn't already exist
if ( empty($this->cookie) )
{
$this->cookie = array('sid' => $this->new_sid(), 'key' => $this->new_key());
$this->data_crypt =& FSO::make_module('encrypt', array($this->cookie['key']));
$this->data = array();
setcookie($sess_name, $this->cookie_crypt->encode_var($this->cookie),
time() + $lifetime, $path, $domain);
$this->FSO->db->query('INSERT INTO `session` (session_id, ip_address, user_agent, last_activity, data)
VALUES (?, ?, ?, ?, ?)', $this->cookie['sid'], $ip_address, $user_agent, time(),
$this->data_crypt->encode_var($this->data));
}
// Link to the superglobal
$_SESSION =& $this->data;
// Garbage collection
$this->gc();
}
/**
* Destructor
*
* Save current session information at the end of the script.
*
* @return void
*/
public function __destruct()
{
$this->FSO->db->query('UPDATE `session` SET last_activity = ?, data = ? WHERE session_id = ?',
time(), $this->data_crypt->encode_var($this->data), $this->cookie['sid']);
}
/**
* Create a new data encryption key
*
* @return string
*/
private function new_key()
{
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=_+[]{};\'#:@~`,./<>?';
$string = '';
for ( $i = 16; $i > 0; $i-- )
{
$string .= $pool[mt_rand(0, strlen($pool) - 1)];
}
return md5($string);
}
/**
* Create a new session ID
*
* Abuse the MySQL UUID() function to create a new unique ID for a session
*
* @return string
*/
private function new_sid()
{
return $this->FSO->db->query('SELECT UUID()')->fetchColumn(0);
}
/**
* Garbage collection
*
* Delete old sessions
*
* @return void
*/
private function gc()
{
srand(time());
if ((rand() % 100) < $this->gc_prob)
{
$this->FSO->db->query('DELETE FROM `session` WHERE last_activity < ?',
time() - $this->lifetime);
}
}
/**
* Member overrides
*
* Override $obj->var type calls to use the data array
*/
public function __get($key) { return $this->data[$key]; }
public function __set($key, $value) { $this->data[$key] = $value; }
public function __isset($key) { $this->data[$key] = $value; }
public function __unset($key) { unset($this->data[$key]); }
}
?>
|