1<?php
2	/* libraries/session.php
3	 *
4	 * Copyright (C) by Hugo Leisink <hugo@leisink.net>
5	 * This file is part of the Banshee PHP framework
6	 * http://www.banshee-php.org/
7	 *
8	 * Don't change this file, unless you know what you are doing.
9	 */
10
11	final class session {
12		private $db = null;
13		private $settings = null;
14		private $id = null;
15		private $session_id = null;
16		private $use_database = null;
17
18		/* Constructor
19		 *
20		 * INPUT:  object database, objection settings
21		 * OUTPUT: -
22		 * ERROR:  -
23		 */
24		public function __construct($db, $settings) {
25			$this->db = $db;
26			$this->settings = $settings;
27
28			$this->use_database = ($this->settings->session_timeout >= ini_get("session.gc_maxlifetime"));
29
30			if ($this->use_database) {
31				$this->db->query("delete from sessions where expire<=now()");
32			}
33
34			$this->start();
35		}
36
37		/* Destructor
38		 *
39		 * INPUT:  -
40		 * OUTPUT: -
41		 * ERROR:  -
42		 */
43		public function __destruct() {
44			if ($this->use_database == false) {
45				session_write_close();
46				return;
47			} else if ($this->id === null) {
48				return;
49			} else if ($this->db->connected == false) {
50				return;
51			}
52
53			$session_data = array(
54				"content"    => json_encode($_SESSION),
55				"ip_address" => $_SERVER["REMOTE_ADDR"]);
56			if (is_true($this->settings->session_persistent) == false) {
57				$session_data["expire"] = date("Y-m-d H:i:s", time() + $this->settings->session_timeout);
58			}
59
60			$this->db->update("sessions", $this->id, $session_data);
61
62			$_SESSION = array();
63		}
64
65		/* Magic method get
66		 *
67		 * INPUT:  string key
68		 * OUTPUT: mixed value
69		 * ERROR:  null
70		 */
71		public function __get($key) {
72			switch ($key) {
73				case "using_database": return $this->use_database;
74			}
75
76			return null;
77		}
78
79		/* Start session
80		 *
81		 * INPUT:  -
82		 * OUTPUT: true
83		 * ERROR:  false
84		 */
85		private function start() {
86			if ($this->use_database) {
87				/* Use database
88				 */
89				$query = "select * from sessions where session_id=%s";
90
91				if (isset($_COOKIE[SESSION_NAME]) == false) {
92					/* New session
93					 */
94					if ($this->new_session() == false) {
95						return false;
96					}
97				} else if (($sessions = $this->db->execute($query, $_COOKIE[SESSION_NAME])) != false) {
98					/* Existing session
99					 */
100					$this->id = (int)$sessions[0]["id"];
101					$this->session_id = $_COOKIE[SESSION_NAME];
102					$_SESSION = json_decode($sessions[0]["content"], true);
103				} else {
104					/* Unknown session
105					 */
106					if ($this->new_session() == false) {
107						return false;
108					}
109				}
110			} else {
111				/* Use PHP's session handling
112				 */
113				session_name(SESSION_NAME);
114				if (is_true($this->settings->session_persistent)) {
115					session_set_cookie_params($this->settings->session_timeout);
116				}
117
118				if (ctype_print($_COOKIE[SESSION_NAME]) == false) {
119					unset($_COOKIE[SESSION_NAME]);
120				}
121
122				if (session_start() == false) {
123					return false;
124				}
125
126				$this->session_id = session_id();
127			}
128
129			return true;
130		}
131
132		/* Start a new session stored in the database
133		 *
134		 * INPUT;  -
135		 * OUTPUT: true
136		 * ERROR:  false
137		 */
138		private function new_session() {
139			/* Create new session id
140			 */
141			$attempts = 3;
142			$query = "select id from sessions where session_id=%s";
143
144			do {
145				if ($attempts-- == 0) {
146					return false;
147				}
148
149				$session_id = hash("sha512", random_string(128));
150
151				if (($result = $this->db->execute($query, $session_id)) === false) {
152					return false;
153				}
154			} while ($result != false);
155
156			/* Store session in database
157			 */
158			$session_data = array(
159				"id"         => null,
160				"session_id" => $session_id,
161				"content"    => null,
162				"expire"     => date("Y-m-d H:i:s", time() + $this->settings->session_timeout),
163				"user_id"    => null,
164				"ip_address" => $_SERVER["REMOTE_ADDR"],
165				"name"       => null);
166
167			if ($this->db->insert("sessions", $session_data) === false) {
168				return false;
169			}
170
171			$this->id = $this->db->last_insert_id;
172			$this->session_id = $session_id;
173
174			/* Place session id in cookie
175			 */
176			$timeout = is_true($this->settings->session_persistent) ? time() + $this->settings->session_timeout : null;
177			setcookie(SESSION_NAME, $this->session_id, $timeout, "/", "", is_true(ENFORCE_HTTPS), true);
178			$_COOKIE[SESSION_NAME] = $this->session_id;
179
180			return true;
181		}
182
183		/* Update user_id in session record
184		 *
185		 * INPUT:  int user id
186		 * OUTPUT: true
187		 * ERROR:  false
188		 */
189		public function set_user_id($user_id) {
190			if ($this->use_database == false) {
191				return true;
192			} else if ($this->id === null) {
193				return false;
194			}
195
196			$user_data = array("user_id" => (int)$user_id);
197
198			return $this->db->update("sessions", $this->id, $user_data) !== false;
199		}
200
201		/* Reset session
202		 *
203		 * INPUT:  -
204		 * OUTPUT: true
205		 * ERROR:  false
206		 */
207		public function reset() {
208			unset($_COOKIE[SESSION_NAME]);
209			$_SESSION = array();
210			if ($this->use_database) {
211				$this->db->query("delete from sessions where id=%d", $this->id);
212			} else {
213				session_unset();
214				session_destroy();
215			}
216
217			return $this->start();
218		}
219	}
220?>
221