1<?php
2	class setup_model extends model {
3		private $required_php_extensions = array("libxml", "mysqli", "xsl");
4
5		/* Determine next step
6		 */
7		public function step_to_take() {
8			$missing = $this->missing_php_extensions();
9			if (count($missing) > 0) {
10				return "php_extensions";
11			}
12
13			if ($this->db->connected == false) {
14				$db = new MySQLi_connection(DB_HOSTNAME, DB_DATABASE, DB_USERNAME, DB_PASSWORD);
15			} else {
16				$db = $this->db;
17			}
18
19			if ($db->connected == false) {
20				/* No database connection
21				 */
22				if ((DB_HOSTNAME == "localhost") && (DB_DATABASE == "monitor") && (DB_USERNAME == "monitor") && (DB_PASSWORD == "monitor")) {
23					return "db_settings";
24				} else if (strpos(DB_PASSWORD, "'") !== false) {
25					$this->output->add_system_message("A single quote is not allowed in the password!");
26					return "db_settings";
27				}
28
29				return "create_db";
30			}
31
32			$result = $db->execute("show tables like %s", "settings");
33			if (count($result) == 0) {
34				return "import_sql";
35			}
36
37			if ($this->settings->database_version < $this->latest_database_version()) {
38				return "update_db";
39			}
40
41			$result = $db->execute("select password from users where username=%s", "admin");
42			if ($result[0]["password"] == "none") {
43				return "credentials";
44			}
45
46			return "done";
47		}
48
49		/* Missing PHP extensions
50		 */
51		public function missing_php_extensions() {
52			static $missing = null;
53
54			if ($missing !== null) {
55				return $missing;
56			}
57
58			$missing = array();
59			foreach ($this->required_php_extensions as $extension) {
60				if (extension_loaded($extension) == false) {
61					array_push($missing, $extension);
62				}
63			}
64
65			return $missing;
66		}
67
68		/* Remove datase related error messages
69		 */
70		public function remove_database_errors() {
71			$errors = explode("\n", rtrim(ob_get_contents()));
72			ob_clean();
73
74			foreach ($errors as $error) {
75				if (strtolower(substr($error, 0, 14)) != "mysqli_connect") {
76					print $error;
77				}
78			}
79		}
80
81		/* Create the MySQL database
82		 */
83		public function create_database($username, $password) {
84			$db = new MySQLi_connection(DB_HOSTNAME, "mysql", $username, $password);
85
86			if ($db->connected == false) {
87				$this->output->add_message("Error connecting to database.");
88				return false;
89			}
90
91			$db->query("begin");
92
93			/* Create database
94			 */
95			$query = "create database if not exists %S character set utf8";
96			if ($db->query($query, DB_DATABASE) == false) {
97				$db->query("rollback");
98				$this->output->add_message("Error creating database.");
99				return false;
100			}
101
102			/* Create user
103			 */
104			$query = "select count(*) as count from user where User=%s";
105			if (($users = $db->execute($query, DB_USERNAME)) === false) {
106				$db->query("rollback");
107				$this->output->add_message("Error checking for user.");
108				return false;
109			}
110
111			if ($users[0]["count"] == 0) {
112				$query = "create user %s@%s identified by %s";
113				if ($db->query($query, DB_USERNAME, DB_HOSTNAME, DB_PASSWORD) == false) {
114					$db->query("rollback");
115					$this->output->add_message("Error creating user.");
116					return false;
117				}
118			} else {
119				$login_test = new MySQLi_connection(DB_HOSTNAME, DB_DATABASE, DB_USERNAME, DB_PASSWORD);
120				if ($login_test->connected == false) {
121					$db->query("rollback");
122					$this->output->add_message("Invalid credentials in settings/website.conf.");
123					return false;
124				}
125			}
126
127			/* Set access rights
128			 */
129			$rights = array(
130				"select", "insert", "update", "delete",
131				"create", "drop", "alter", "index", "lock tables",
132				"create view", "show view");
133
134			$query = "grant ".implode(", ", $rights)." on %S.* to %s@%s";
135			if ($db->query($query, DB_DATABASE, DB_USERNAME, DB_HOSTNAME) == false) {
136				$db->query("rollback");
137				$this->output->add_message("Error setting access rights.");
138				return false;
139			}
140
141			/* Commit changes
142			 */
143			$db->query("commit");
144			$db->query("flush privileges");
145			unset($db);
146
147			return true;
148		}
149
150		/* Import SQL script from file
151		 */
152		public function import_sql() {
153			if (($queries = file("../database/mysql.sql")) === false) {
154				$this->output->add_message("Can't read the database/mysql.sql file.");
155				return false;
156			}
157
158			if (($db_link = mysqli_connect(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE)) === false) {
159				$this->output->add_message("Error while connecting to the database.");
160				return false;
161			}
162
163			$query = "";
164			foreach ($queries as $line) {
165				if (($line = trim($line)) == "") {
166					continue;
167				}
168				if (substr($line, 0, 2) == "--") {
169					continue;
170				}
171
172				$query .= $line;
173				if (substr($query, -1) == ";") {
174					if (mysqli_query($db_link, $query) === false) {
175						$this->output->add_message("Error while executing query [%s].", $query);
176						return false;
177					}
178					$query = "";
179				}
180			}
181
182			mysqli_close($db_link);
183
184			$this->db->query("update users set status=%d", USER_STATUS_CHANGEPWD);
185			$this->settings->secret_website_code = random_string(32);
186
187			return true;
188		}
189
190		/* Collect latest database version from update_database() function
191		 */
192		private function latest_database_version() {
193			$old_db = $this->db;
194			$old_settings = $this->settings;
195			$this->db = new dummy_object();
196			$this->settings = new dummy_object();
197			$this->settings->database_version = 0;
198
199			$this->update_database();
200			$version = $this->settings->database_version;
201
202			unset($this->db);
203			unset($this->settings);
204			$this->db = $old_db;
205			$this->settings = $old_settings;
206
207			return $version;
208		}
209
210		/* Add setting when missing
211		 */
212		private function ensure_setting($key, $type, $value) {
213			if ($this->db->entry("settings", $key, "key") != false) {
214				return true;
215			}
216
217			$entry = array(
218				"key"   => $key,
219				"type"  => $type,
220				"value" => $value);
221			return $this->db->insert("settings", $entry) !== false;
222		}
223
224		/* Update database
225		 */
226		public function update_database() {
227			if ($this->settings->database_version < 101) {
228				$this->settings->database_version = 101;
229			}
230
231			if ($this->settings->database_version < 102) {
232				$this->ensure_setting("hiawatha_cache_enabled", "boolean", "false");
233				$this->ensure_setting("hiawatha_cache_default_time", "integer", "3600");
234				$this->ensure_setting("session_timeout", "integer", "3600");
235				$this->ensure_setting("session_persistent", "boolean", "false");
236
237				$this->settings->database_version = 102;
238			}
239
240			if ($this->settings->database_version < 103) {
241				$tables = array("cgi_statistics", "host_statistics", "server_statistics");
242				foreach ($tables as $table) {
243					$this->db->query("alter table %S add %S date not null after %S", $table, "date", "id");
244					$this->db->query("alter table %S add %S tinyint unsigned not null after %S", $table, "hour", "date");
245					$this->db->query("update %S set %S=date(%S), %S=%d", $table, "date", "timestamp_begin", "hour", 0);
246					$this->db->query("alter table %S drop %S", $table, "timestamp_begin");
247					$this->db->query("alter table %S drop %S", $table, "timestamp_end");
248				}
249
250				$query = "alter table %S add index(%S)";
251				$this->db->query($query, "cgi_statistics", "date");
252				$this->db->query($query, "cgi_statistics", "hour");
253				$this->db->query($query, "host_statistics", "date");
254				$this->db->query($query, "host_statistics", "hour");
255				$this->db->query($query, "server_statistics", "date");
256				$this->db->query($query, "server_statistics", "hour");
257
258				$this->settings->dashboard_threshold_change = 150;
259				$this->settings->dashboard_threshold_value = 5;
260				$this->settings->dashboard_page_refresh = 1;
261				$this->settings->report_alert_high = 300;
262				$this->settings->report_alert_medium = 150;
263				$this->settings->report_history_days = 15;
264				$this->settings->report_skip_normal = false;
265				$this->settings->report_use_median = true;
266
267				$this->settings->database_version = 103;
268			}
269
270			if ($this->settings->database_version < 104) {
271				$this->settings->database_version = 104;
272			}
273
274			if ($this->settings->database_version < 105) {
275				$this->db->query("alter table %S change %S %S varchar(128) character set utf8 collate utf8_general_ci not null", "sessions", "session_id", "session_id");
276
277				$query = "update settings set type=%s value=%s where %S=%s";
278				$this->db->query($query, "float", "2.5", "key", "dashboard_threshold_change");
279				$this->db->query($query, "float", "5",   "key", "report_alert_high");
280				$this->db->query($query, "float", "2",   "key", "report_alert_medium");
281
282				$this->settings->dashboard_show_weblog = true;
283
284				$this->settings->database_version = 105;
285			}
286		}
287
288		/* Set administrator password
289		 */
290		public function set_admin_credentials($post_data) {
291			$result = true;
292
293			if (valid_input($post_data["username"], VALIDATE_LETTERS, VALIDATE_NONEMPTY) == false) {
294				$this->output->add_message("The username must consist of lowercase letters.");
295				$result = false;
296			}
297
298			if ($post_data["password"] != $post_data["repeat"]) {
299				$this->output->add_message("The passwords do not match.");
300				$result = false;
301			}
302
303			if ($result == false) {
304				return false;
305			}
306
307			$password = hash_password($post_data["password"], $post_data["username"]);
308
309			$query = "update users set username=%s, password=%s, status=%d where username=%s";
310			if ($this->db->query($query, $post_data["username"], $password, USER_STATUS_ACTIVE, "admin") === false) {
311				$this->output->add_message("Error while setting password.");
312				return false;
313			}
314
315			return true;
316		}
317	}
318
319	class dummy_object {
320		private $cache = array();
321
322		public function __set($key, $value) {
323			$this->cache[$key] = $value;
324		}
325
326		public function __get($key) {
327			return $this->cache[$key];
328		}
329
330		public function __call($func, $args) {
331			return false;
332		}
333	}
334?>
335