1: <?php
2: /**
3: * XOOPS session handler
4: *
5: * You may not change or alter any portion of this comment or credits
6: * of supporting developers from this source code or any supporting source code
7: * which is considered copyrighted (c) material of the original comment or credit authors.
8: * This program is distributed in the hope that it will be useful,
9: * but WITHOUT ANY WARRANTY; without even the implied warranty of
10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11: *
12: * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
13: * @license GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14: * @package kernel
15: * @since 2.0.0
16: * @author Kazumi Ono (AKA onokazu) http://www.myweb.ne.jp/, http://jp.xoops.org/
17: * @author Taiwen Jiang <phppp@users.sourceforge.net>
18: */
19:
20: defined('XOOPS_ROOT_PATH') || exit('Restricted access');
21:
22: /**
23: * Handler for a session
24: * @package kernel
25: *
26: * @author Kazumi Ono <onokazu@xoops.org>
27: * @author Taiwen Jiang <phppp@users.sourceforge.net>
28: * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
29: */
30: class XoopsSessionHandler
31: {
32: /**
33: * Database connection
34: *
35: * @var object
36: * @access private
37: */
38: public $db;
39:
40: /**
41: * Security checking level
42: *
43: * Possible value:
44: * 0 - no check;
45: * 1 - check browser characteristics (HTTP_USER_AGENT/HTTP_ACCEPT_LANGUAGE), to be implemented in the future now;
46: * 2 - check browser and IP A.B;
47: * 3 - check browser and IP A.B.C, recommended;
48: * 4 - check browser and IP A.B.C.D;
49: *
50: * @var int
51: * @access public
52: */
53: public $securityLevel = 3;
54:
55: protected $bitMasks = array(
56: 2 => array('v4' => 16, 'v6' => 64),
57: 3 => array('v4' => 24, 'v6' => 56),
58: 4 => array('v4' => 32, 'v6' => 128),
59: );
60:
61: /**
62: * Enable regenerate_id
63: *
64: * @var bool
65: * @access public
66: */
67: public $enableRegenerateId = true;
68:
69: /**
70: * Constructor
71: *
72: * @param XoopsDatabase $db reference to the {@link XoopsDatabase} object
73: *
74: */
75: public function __construct(XoopsDatabase $db)
76: {
77: $this->db = $db;
78: }
79:
80: /**
81: * Open a session
82: *
83: * @param string $save_path
84: * @param string $session_name
85: *
86: * @return bool
87: */
88: public function open($save_path, $session_name)
89: {
90: return true;
91: }
92:
93: /**
94: * Close a session
95: *
96: * @return bool
97: */
98: public function close()
99: {
100: $this->gc_force();
101:
102: return true;
103: }
104:
105: /**
106: * Read a session from the database
107: *
108: * @param string $sess_id ID of the session
109: *
110: * @return array Session data
111: */
112: public function read($sess_id)
113: {
114: $ip = \Xmf\IPAddress::fromRequest();
115: $sql = sprintf(
116: 'SELECT sess_data, sess_ip FROM %s WHERE sess_id = %s',
117: $this->db->prefix('session'),
118: $this->db->quoteString($sess_id)
119: );
120: // if (false != $result = $this->db->query($sql)) {
121: $result = $this->db->query($sql);
122: if (!empty($result)) {
123: if (list($sess_data, $sess_ip) = $this->db->fetchRow($result)) {
124: if ($this->securityLevel > 1) {
125: if (false === $ip->sameSubnet(
126: $sess_ip,
127: $this->bitMasks[$this->securityLevel]['v4'],
128: $this->bitMasks[$this->securityLevel]['v6']
129: )) {
130: $sess_data = '';
131: }
132: }
133:
134: return $sess_data;
135: }
136: }
137:
138: return '';
139: }
140:
141: /**
142: * Write a session to the database
143: *
144: * @param string $sess_id
145: * @param string $sess_data
146: *
147: * @return bool
148: **/
149: public function write($sess_id, $sess_data)
150: {
151: $remoteAddress = \Xmf\IPAddress::fromRequest()->asReadable();
152: $sess_id = $this->db->quoteString($sess_id);
153: $sql = sprintf(
154: 'UPDATE %s SET sess_updated = %u, sess_data = %s WHERE sess_id = %s',
155: $this->db->prefix('session'),
156: time(),
157: $this->db->quoteString($sess_data),
158: $sess_id
159: );
160: $this->db->queryF($sql);
161: if (!$this->db->getAffectedRows()) {
162: $sql = sprintf(
163: 'INSERT INTO %s (sess_id, sess_updated, sess_ip, sess_data) VALUES (%s, %u, %s, %s)',
164: $this->db->prefix('session'),
165: $sess_id,
166: time(),
167: $this->db->quote($remoteAddress),
168: $this->db->quote($sess_data)
169: );
170:
171: return $this->db->queryF($sql);
172: }
173:
174: return true;
175: }
176:
177: /**
178: * Destroy a session
179: *
180: * @param string $sess_id
181: *
182: * @return bool
183: **/
184: public function destroy($sess_id)
185: {
186: $sql = sprintf(
187: 'DELETE FROM %s WHERE sess_id = %s',
188: $this->db->prefix('session'),
189: $this->db->quoteString($sess_id)
190: );
191: if (!$result = $this->db->queryF($sql)) {
192: return false;
193: }
194:
195: return true;
196: }
197:
198: /**
199: * Garbage Collector
200: *
201: * @param int $expire Time in seconds until a session expires
202: * @return bool
203: **/
204: public function gc($expire)
205: {
206: if (empty($expire)) {
207: return true;
208: }
209:
210: $mintime = time() - (int)$expire;
211: $sql = sprintf('DELETE FROM %s WHERE sess_updated < %u', $this->db->prefix('session'), $mintime);
212:
213: return $this->db->queryF($sql);
214: }
215:
216: /**
217: * Force gc for situations where gc is registered but not executed
218: **/
219: public function gc_force()
220: {
221: if (mt_rand(1, 100) < 11) {
222: $expire = @ini_get('session.gc_maxlifetime');
223: $expire = ($expire > 0) ? $expire : 900;
224: $this->gc($expire);
225: }
226: }
227:
228: /**
229: * Update the current session id with a newly generated one
230: *
231: * To be refactored
232: *
233: * @param bool $delete_old_session
234: * @return bool
235: **/
236: public function regenerate_id($delete_old_session = false)
237: {
238: if (!$this->enableRegenerateId) {
239: $success = true;
240: } else {
241: $success = session_regenerate_id($delete_old_session);
242: }
243:
244: // Force updating cookie for session cookie
245: if ($success) {
246: $this->update_cookie();
247: }
248:
249: return $success;
250: }
251:
252: /**
253: * Update cookie status for current session
254: *
255: * To be refactored
256: * FIXME: how about $xoopsConfig['use_ssl'] is enabled?
257: *
258: * @param string $sess_id session ID
259: * @param int $expire Time in seconds until a session expires
260: * @return bool
261: **/
262: public function update_cookie($sess_id = null, $expire = null)
263: {
264: global $xoopsConfig;
265: $session_name = ($xoopsConfig['use_mysession'] && $xoopsConfig['session_name'] != '')
266: ? $xoopsConfig['session_name']
267: : session_name();
268: $session_expire = null !== $expire
269: ? (int)$expire
270: : (($xoopsConfig['use_mysession'] && $xoopsConfig['session_name'] != '')
271: ? $xoopsConfig['session_expire'] * 60
272: : ini_get('session.cookie_lifetime')
273: );
274: $session_id = empty($sess_id) ? session_id() : $sess_id;
275: setcookie(
276: $session_name,
277: $session_id,
278: $session_expire ? time() + $session_expire : 0,
279: '/',
280: XOOPS_COOKIE_DOMAIN,
281: (XOOPS_PROT === 'https://'),
282: true
283: );
284: }
285: }
286: