blob: 3d6fa63227337e0b9a25c5b1f31c2dd4e110c911 [file] [log] [blame]
Andrey Andreev47a47fb2014-05-31 16:08:30 +03001<?php
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.2.4 or newer
6 *
7 * NOTICE OF LICENSE
8 *
9 * Licensed under the Open Software License version 3.0
10 *
11 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
19 * @package CodeIgniter
20 * @author Andrey Andreev
21 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
22 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
23 * @link http://codeigniter.com
24 * @since Version 3.0
25 * @filesource
26 */
27defined('BASEPATH') OR exit('No direct script access allowed');
28
29/**
30 * CodeIgniter Session Files Driver
31 *
32 * @package CodeIgniter
33 * @subpackage Libraries
34 * @category Sessions
35 * @author Andrey Andreev
36 * @link http://codeigniter.com/user_guide/libraries/sessions.html
37 */
38class CI_Session_files_driver extends CI_Session_driver implements SessionHandlerInterface {
39
40 /**
41 * Save path
42 *
43 * @var string
44 */
45 protected $_save_path;
46
47 /**
48 * File handle
49 *
50 * @var resource
51 */
52 protected $_file_handle;
53
54 /**
55 * File name
56 *
57 * @var resource
58 */
59 protected $_file_path;
60
61 /**
62 * File new flag
63 *
64 * @var bool
65 */
66 protected $_file_new;
67
68 // ------------------------------------------------------------------------
69
70 /**
71 * Class constructor
72 *
73 * @param array $params Configuration parameters
74 * @return void
75 */
76 public function __construct(&$params)
77 {
78 parent::__construct($params);
79
Andrey Andreevdfb39be2014-10-06 01:50:14 +030080 if (isset($this->_config['save_path']))
Andrey Andreev47a47fb2014-05-31 16:08:30 +030081 {
Andrey Andreevdfb39be2014-10-06 01:50:14 +030082 $this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\');
83 ini_set('session.save_path', $this->_config['save_path']);
Andrey Andreev47a47fb2014-05-31 16:08:30 +030084 }
85 else
86 {
Andrey Andreevdfb39be2014-10-06 01:50:14 +030087 $this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\');
Andrey Andreev47a47fb2014-05-31 16:08:30 +030088 }
89 }
90
91 // ------------------------------------------------------------------------
92
93 public function open($save_path, $name)
94 {
95 if ( ! is_dir($save_path) && ! mkdir($save_path, 0700, TRUE))
96 {
Andrey Andreevdfb39be2014-10-06 01:50:14 +030097 log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created.");
Andrey Andreev47a47fb2014-05-31 16:08:30 +030098 return FALSE;
99 }
100
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300101 $this->_config['save_path'] = $save_path;
102 $this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300103 .$name // we'll use the session cookie name as a prefix to avoid collisions
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300104 .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : '');
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300105
106 return TRUE;
107 }
108
109 // ------------------------------------------------------------------------
110
111 public function read($session_id)
112 {
113 // This might seem weird, but PHP 5.6 introduces session_reset(),
114 // which re-reads session data
115 if ($this->_file_handle === NULL)
116 {
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300117 // Just using fopen() with 'c+b' mode would be perfect, but it is only
118 // available since PHP 5.2.6 and we have to set permissions for new files,
119 // so we'd have to hack around this ...
Andrey Andreev7474a672014-10-31 23:35:32 +0200120 if (($this->_file_new = ! file_exists($this->_file_path.$session_id)) === TRUE)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300121 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200122 if (($this->_file_handle = fopen($this->_file_path.$session_id, 'w+b')) === FALSE)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300123 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200124 log_message('error', "Session: File '".$this->_file_path.$session_id."' doesn't exist and cannot be created.");
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300125 return FALSE;
126 }
127 }
Andrey Andreev7474a672014-10-31 23:35:32 +0200128 elseif (($this->_file_handle = fopen($this->_file_path.$session_id, 'r+b')) === FALSE)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300129 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200130 log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'.");
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300131 return FALSE;
132 }
133
134 if (flock($this->_file_handle, LOCK_EX) === FALSE)
135 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200136 log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'.");
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300137 fclose($this->_file_handle);
138 $this->_file_handle = NULL;
139 return FALSE;
140 }
141
Andrey Andreev7474a672014-10-31 23:35:32 +0200142 // Needed by write() to detect session_regenerate_id() calls
143 $this->_session_id = $session_id;
144
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300145 if ($this->_file_new)
146 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200147 chmod($this->_file_path.$session_id, 0600);
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300148 $this->_fingerprint = md5('');
149 return '';
150 }
151 }
152 else
153 {
154 rewind($this->_file_handle);
155 }
156
157 $session_data = '';
Andrey Andreev7474a672014-10-31 23:35:32 +0200158 for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += strlen($buffer))
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300159 {
160 if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE)
161 {
162 break;
163 }
164
165 $session_data .= $buffer;
166 }
167
168 $this->_fingerprint = md5($session_data);
169 return $session_data;
170 }
171
172 public function write($session_id, $session_data)
173 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200174 // If the two IDs don't match, we have a session_regenerate_id() call
175 // and we need to close the old handle and open a new one
176 if ($session_id !== $this->_session_id && ( ! $this->close() OR $this->read($session_id) === FALSE))
177 {
178 return FALSE;
179 }
180
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300181 if ( ! is_resource($this->_file_handle))
182 {
183 return FALSE;
184 }
185 elseif ($this->_fingerprint === md5($session_data))
186 {
187 return ($this->_file_new)
188 ? TRUE
Andrey Andreev7474a672014-10-31 23:35:32 +0200189 : touch($this->_file_path.$session_id);
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300190 }
191
192 if ( ! $this->_file_new)
193 {
194 ftruncate($this->_file_handle, 0);
195 rewind($this->_file_handle);
196 }
197
Andrey Andreev5995e082014-06-03 15:33:51 +0300198 if (($length = strlen($session_data)) > 0)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300199 {
Andrey Andreev5995e082014-06-03 15:33:51 +0300200 for ($written = 0; $written < $length; $written += $result)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300201 {
Andrey Andreev5995e082014-06-03 15:33:51 +0300202 if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE)
203 {
204 break;
205 }
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300206 }
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300207
Andrey Andreev5995e082014-06-03 15:33:51 +0300208 if ( ! is_int($result))
209 {
210 $this->_fingerprint = md5(substr($session_data, 0, $written));
211 log_message('error', 'Session: Unable to write data.');
212 return FALSE;
213 }
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300214 }
215
216 $this->_fingerprint = md5($session_data);
217 return TRUE;
218 }
219
220 // ------------------------------------------------------------------------
221
222 public function close()
223 {
224 if (is_resource($this->_file_handle))
225 {
226 flock($this->_file_handle, LOCK_UN);
227 fclose($this->_file_handle);
228
Andrey Andreev7474a672014-10-31 23:35:32 +0200229 $this->_file_handle = $this->_file_new = $this->_session_id = NULL;
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300230 return TRUE;
231 }
232
Andrey Andreev7474a672014-10-31 23:35:32 +0200233 return TRUE;
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300234 }
235
236 // ------------------------------------------------------------------------
237
238 public function destroy($session_id)
239 {
240 if ($this->close())
241 {
Andrey Andreev7474a672014-10-31 23:35:32 +0200242 return unlink($this->_file_path.$session_id) && $this->_cookie_destroy();
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300243 }
244 elseif ($this->_file_path !== NULL)
245 {
246 clearstatcache();
Andrey Andreev7474a672014-10-31 23:35:32 +0200247 return file_exists($this->_file_path.$session_id)
248 ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy())
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300249 : TRUE;
250 }
251
252 return FALSE;
253 }
254
255 // ------------------------------------------------------------------------
256
257 public function gc($maxlifetime)
258 {
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300259 if ( ! is_dir($this->_config['save_path']) OR ($files = scandir($this->_config['save_path'])) === FALSE)
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300260 {
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300261 log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'.");
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300262 return FALSE;
263 }
264
265 $ts = time() - $maxlifetime;
266
267 foreach ($files as $file)
268 {
269 // If the filename doesn't match this pattern, it's either not a session file or is not ours
270 if ( ! preg_match('/(?:[0-9a-f]{32})?[0-9a-f]{40}$/i', $file)
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300271 OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)
Andrey Andreevc5519ce2014-10-26 11:57:20 +0200272 OR ($mtime = fileatime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300273 OR $mtime > $ts)
274 {
275 continue;
276 }
277
Andrey Andreevdfb39be2014-10-06 01:50:14 +0300278 unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file);
Andrey Andreev47a47fb2014-05-31 16:08:30 +0300279 }
280
281 return TRUE;
282 }
283
284}
285
286/* End of file Session_files_driver.php */
287/* Location: ./system/libraries/Session/drivers/Session_files_driver.php */