Subversion-Projekte lars-tiefland.ci

Revision

Revision 1257 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 3.0.0
36
 * @filesource
37
*/
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * CodeIgniter Session Files Driver
42
 *
43
 * @package	CodeIgniter
44
 * @subpackage	Libraries
45
 * @category	Sessions
46
 * @author	Andrey Andreev
47
 * @link	https://codeigniter.com/user_guide/libraries/sessions.html
48
 */
49
class CI_Session_files_driver extends CI_Session_driver implements SessionHandlerInterface {
50
 
51
	/**
52
	 * Save path
53
	 *
54
	 * @var	string
55
	 */
56
	protected $_save_path;
57
 
58
	/**
59
	 * File handle
60
	 *
61
	 * @var	resource
62
	 */
63
	protected $_file_handle;
64
 
65
	/**
66
	 * File name
67
	 *
68
	 * @var	resource
69
	 */
70
	protected $_file_path;
71
 
72
	/**
73
	 * File new flag
74
	 *
75
	 * @var	bool
76
	 */
77
	protected $_file_new;
78
 
79
	// ------------------------------------------------------------------------
80
 
81
	/**
82
	 * Class constructor
83
	 *
84
	 * @param	array	$params	Configuration parameters
85
	 * @return	void
86
	 */
87
	public function __construct(&$params)
88
	{
89
		parent::__construct($params);
90
 
91
		if (isset($this->_config['save_path']))
92
		{
93
			$this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\');
94
			ini_set('session.save_path', $this->_config['save_path']);
95
		}
96
		else
97
		{
98
			log_message('debug', 'Session: "sess_save_path" is empty; using "session.save_path" value from php.ini.');
99
			$this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\');
100
		}
101
	}
102
 
103
	// ------------------------------------------------------------------------
104
 
105
	/**
106
	 * Open
107
	 *
108
	 * Sanitizes the save_path directory.
109
	 *
110
	 * @param	string	$save_path	Path to session files' directory
111
	 * @param	string	$name		Session cookie name
112
	 * @return	bool
113
	 */
114
	public function open($save_path, $name)
115
	{
116
		if ( ! is_dir($save_path))
117
		{
118
			if ( ! mkdir($save_path, 0700, TRUE))
119
			{
120
				throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created.");
121
			}
122
		}
123
		elseif ( ! is_writable($save_path))
124
		{
125
			throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not writable by the PHP process.");
126
		}
127
 
128
		$this->_config['save_path'] = $save_path;
129
		$this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR
130
			.$name // we'll use the session cookie name as a prefix to avoid collisions
131
			.($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : '');
132
 
133
		return $this->_success;
134
	}
135
 
136
	// ------------------------------------------------------------------------
137
 
138
	/**
139
	 * Read
140
	 *
141
	 * Reads session data and acquires a lock
142
	 *
143
	 * @param	string	$session_id	Session ID
144
	 * @return	string	Serialized session data
145
	 */
146
	public function read($session_id)
147
	{
148
		// This might seem weird, but PHP 5.6 introduces session_reset(),
149
		// which re-reads session data
150
		if ($this->_file_handle === NULL)
151
		{
152
			// Just using fopen() with 'c+b' mode would be perfect, but it is only
153
			// available since PHP 5.2.6 and we have to set permissions for new files,
154
			// so we'd have to hack around this ...
155
			if (($this->_file_new = ! file_exists($this->_file_path.$session_id)) === TRUE)
156
			{
157
				if (($this->_file_handle = fopen($this->_file_path.$session_id, 'w+b')) === FALSE)
158
				{
159
					log_message('error', "Session: File '".$this->_file_path.$session_id."' doesn't exist and cannot be created.");
160
					return $this->_failure;
161
				}
162
			}
163
			elseif (($this->_file_handle = fopen($this->_file_path.$session_id, 'r+b')) === FALSE)
164
			{
165
				log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'.");
166
				return $this->_failure;
167
			}
168
 
169
			if (flock($this->_file_handle, LOCK_EX) === FALSE)
170
			{
171
				log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'.");
172
				fclose($this->_file_handle);
173
				$this->_file_handle = NULL;
174
				return $this->_failure;
175
			}
176
 
177
			// Needed by write() to detect session_regenerate_id() calls
178
			$this->_session_id = $session_id;
179
 
180
			if ($this->_file_new)
181
			{
182
				chmod($this->_file_path.$session_id, 0600);
183
				$this->_fingerprint = md5('');
184
				return '';
185
			}
186
		}
187
		// We shouldn't need this, but apparently we do ...
188
		// See https://github.com/bcit-ci/CodeIgniter/issues/4039
189
		elseif ($this->_file_handle === FALSE)
190
		{
191
			return $this->_failure;
192
		}
193
		else
194
		{
195
			rewind($this->_file_handle);
196
		}
197
 
198
		$session_data = '';
199
		for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += strlen($buffer))
200
		{
201
			if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE)
202
			{
203
				break;
204
			}
205
 
206
			$session_data .= $buffer;
207
		}
208
 
209
		$this->_fingerprint = md5($session_data);
210
		return $session_data;
211
	}
212
 
213
	// ------------------------------------------------------------------------
214
 
215
	/**
216
	 * Write
217
	 *
218
	 * Writes (create / update) session data
219
	 *
220
	 * @param	string	$session_id	Session ID
221
	 * @param	string	$session_data	Serialized session data
222
	 * @return	bool
223
	 */
224
	public function write($session_id, $session_data)
225
	{
226
		// If the two IDs don't match, we have a session_regenerate_id() call
227
		// and we need to close the old handle and open a new one
228
		if ($session_id !== $this->_session_id && ($this->close() === $this->_failure OR $this->read($session_id) === $this->_failure))
229
		{
230
			return $this->_failure;
231
		}
232
 
233
		if ( ! is_resource($this->_file_handle))
234
		{
235
			return $this->_failure;
236
		}
237
		elseif ($this->_fingerprint === md5($session_data))
238
		{
239
			return ( ! $this->_file_new && ! touch($this->_file_path.$session_id))
240
				? $this->_failure
241
				: $this->_success;
242
		}
243
 
244
		if ( ! $this->_file_new)
245
		{
246
			ftruncate($this->_file_handle, 0);
247
			rewind($this->_file_handle);
248
		}
249
 
250
		if (($length = strlen($session_data)) > 0)
251
		{
252
			for ($written = 0; $written < $length; $written += $result)
253
			{
254
				if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE)
255
				{
256
					break;
257
				}
258
			}
259
 
260
			if ( ! is_int($result))
261
			{
262
				$this->_fingerprint = md5(substr($session_data, 0, $written));
263
				log_message('error', 'Session: Unable to write data.');
264
				return $this->_failure;
265
			}
266
		}
267
 
268
		$this->_fingerprint = md5($session_data);
269
		return $this->_success;
270
	}
271
 
272
	// ------------------------------------------------------------------------
273
 
274
	/**
275
	 * Close
276
	 *
277
	 * Releases locks and closes file descriptor.
278
	 *
279
	 * @return	bool
280
	 */
281
	public function close()
282
	{
283
		if (is_resource($this->_file_handle))
284
		{
285
			flock($this->_file_handle, LOCK_UN);
286
			fclose($this->_file_handle);
287
 
288
			$this->_file_handle = $this->_file_new = $this->_session_id = NULL;
289
		}
290
 
291
		return $this->_success;
292
	}
293
 
294
	// ------------------------------------------------------------------------
295
 
296
	/**
297
	 * Destroy
298
	 *
299
	 * Destroys the current session.
300
	 *
301
	 * @param	string	$session_id	Session ID
302
	 * @return	bool
303
	 */
304
	public function destroy($session_id)
305
	{
306
		if ($this->close() === $this->_success)
307
		{
308
			if (file_exists($this->_file_path.$session_id))
309
			{
310
				$this->_cookie_destroy();
311
				return unlink($this->_file_path.$session_id)
312
					? $this->_success
313
					: $this->_failure;
314
			}
315
 
316
			return $this->_success;
317
		}
318
		elseif ($this->_file_path !== NULL)
319
		{
320
			clearstatcache();
321
			if (file_exists($this->_file_path.$session_id))
322
			{
323
				$this->_cookie_destroy();
324
				return unlink($this->_file_path.$session_id)
325
					? $this->_success
326
					: $this->_failure;
327
			}
328
 
329
			return $this->_success;
330
		}
331
 
332
		return $this->_failure;
333
	}
334
 
335
	// ------------------------------------------------------------------------
336
 
337
	/**
338
	 * Garbage Collector
339
	 *
340
	 * Deletes expired sessions
341
	 *
342
	 * @param	int 	$maxlifetime	Maximum lifetime of sessions
343
	 * @return	bool
344
	 */
345
	public function gc($maxlifetime)
346
	{
347
		if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE)
348
		{
349
			log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'.");
350
			return $this->_failure;
351
		}
352
 
353
		$ts = time() - $maxlifetime;
354
 
355
		$pattern = sprintf(
356
			'/^%s[0-9a-f]{%d}$/',
357
			preg_quote($this->_config['cookie_name'], '/'),
358
			($this->_config['match_ip'] === TRUE ? 72 : 40)
359
		);
360
 
361
		while (($file = readdir($directory)) !== FALSE)
362
		{
363
			// If the filename doesn't match this pattern, it's either not a session file or is not ours
364
			if ( ! preg_match($pattern, $file)
365
				OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)
366
				OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE
367
				OR $mtime > $ts)
368
			{
369
				continue;
370
			}
371
 
372
			unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file);
373
		}
374
 
375
		closedir($directory);
376
 
377
		return $this->_success;
378
	}
379
 
380
}