]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/blame - settings/install.php
Run hook HOOKTYPE_PRE_FOOTER a little earlier
[irc/unrealircd/unrealircd-webpanel.git] / settings / install.php
CommitLineData
78069032 1<?php
cd1dee97
BM
2/* Log the user out if it was logged in.
3 * This is mostly for devs running the install screen and
4 * fater succeeding the first screen suddenly being logged in
5 * with old credentials/uid weirdness.
6 * Code from example #1 at https://www.php.net/manual/en/function.session-destroy.php
7 */
8session_start();
9$_SESSION = Array();
10if (ini_get("session.use_cookies")) {
11 $params = session_get_cookie_params();
12 setcookie(session_name(), '', time() - 42000,
13 $params["path"], $params["domain"],
14 $params["secure"], $params["httponly"]
15 );
16}
17session_destroy();
78069032 18
c06c1713 19require_once "../inc/common.php";
78069032 20
9680dd58 21/* Get the base url */
973acaa9 22$uri = $_SERVER['REQUEST_URI'];
9680dd58 23$tok = split($uri, "/");
2dbe2544 24$base_url = "";
9680dd58
VP
25for ($i=0; isset($tok[$i]); $i++)
26{
2dbe2544 27 if ($tok[$i] == "settings" && strstr($tok[$i + 1], "install.php"))
9680dd58 28 {
9680dd58
VP
29 if ($i)
30 {
31 for($j=0; $j < $i; $j++)
32 {
33 strcat($base_url,$tok[$j]);
34 strcat($base_url,"/");
35 }
36 }
9680dd58
VP
37 }
38}
2dbe2544
VP
39if (!strlen($base_url))
40 $base_url = "/";
41define('BASE_URL', $base_url);
42
78069032
VP
43$writable = (is_writable("../config/")) ? true: false;
44?>
45<!DOCTYPE html>
46<head>
47<div class="media">
48<div class="media-body">
49
50 <meta name="viewport" content="width=device-width, initial-scale=1">
51 <meta name="HandheldFriendly" content="true">
52
78069032
VP
53
54
55 <!-- Latest compiled and minified CSS -->
56 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
57
58<!-- Font Awesome JS -->
59<script defer src="https://use.fontawesome.com/releases/v6.2.1/js/solid.js" integrity="sha384-tzzSw1/Vo+0N5UhStP3bvwWPq+uvzCMfrN1fEFe+xBmv1C/AtVX5K0uZtmcHitFZ" crossorigin="anonymous"></script>
60<script defer src="https://use.fontawesome.com/releases/v6.2.1/js/fontawesome.js" integrity="sha384-6OIrr52G08NpOFSZdxxz1xdNSndlD4vdcf/q2myIUVO0VsqaGHJsB0RaBE01VTOY" crossorigin="anonymous"></script>
61
62<!-- Font Awesome icons -->
63<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
2dbe2544 64<script src="../js/unrealircd-admin.js"></script>
78069032
VP
65<title>UnrealIRCd Panel</title>
66<link rel="icon" type="image/x-icon" href="<?php echo get_config("base_url"); ?>img/favicon.ico">
67
68<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
69<!-- Popper.JS -->
70<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
71<!-- Bootstrap JS -->
72<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>
73</div></div>
74</head>
75
76<body role="document">
77
2dbe2544 78 <div class="container mt-4"><div class="row justify-content-center"><img src="../img/unreal.jpg" width="35px" height="35px" style="margin-right: 15px"><h3>UnrealIRCd Admin Panel Configuration and Setup</h3></div></div>
9680dd58 79<?php
78069032
VP
80
81 if (file_exists("../config/config.php"))
82 {
2dbe2544 83 ?><br><div class="container"><?php Message::Fail("You're already configured!"); ?>
78069032
VP
84 <br>
85 <a class="text-center btn btn-primary" href="<?php echo BASE_URL; ?>">Take me home!</a>
86 </div>
87 <?php
88 return;
89 }
2dbe2544 90 elseif (isset($_POST) && !empty($_POST))
9680dd58 91 {
2dbe2544
VP
92 ?><br><div class="container"><?php
93 $opts = (object)$_POST;
9680dd58 94 /* pre-load the appropriate auth plugin */
9680dd58 95 $auth_method = (isset($opts->auth_method)) ? $opts->auth_method : NULL;
2dbe2544
VP
96 $auth_method_name = NULL;
97 switch($auth_method)
98 {
ce25dde2
BM
99 case "sql_db":
100 $auth_method_name = "SQLDB";
2dbe2544 101 break;
ce25dde2
BM
102 case "file_db":
103 $auth_method_name = "FileDB";
2dbe2544
VP
104 break;
105 }
9680dd58 106 if ($auth_method)
2dbe2544 107 $am = new Plugin($auth_method);
9680dd58 108 else
2dbe2544
VP
109 {
110 Message::Fail("Invalid parameters");
111 return;
112 }
113 if ($am->error)
114 {
115 Message::Fail("Couldn't load plugin \"$auth_method\": $am->error");
116 return;
117 }
118
54b9603c
BM
119 $config["base_url"] = BASE_URL;
120 $config["plugins"] = Array("$auth_method");
ce25dde2 121 if ($auth_method == "sql_db")
2dbe2544 122 {
54b9603c
BM
123 $config["mysql"] = [
124 "host" => $opts->sql_host,
125 "database" => $opts->sql_db,
126 "username" => $opts->sql_user,
127 "password" => $opts->sql_password,
ca3c76f7 128 "table_prefix" => $opts->sql_table_prefix,
b41fa16f 129 ];
54b9603c 130 }
b41fa16f 131
474d4966
BM
132 generate_secrets();
133
54b9603c
BM
134 /* First, write only the config file */
135 write_config_file();
136
ce25dde2 137 if ($auth_method == "sql_db")
1bbc51fb 138 {
ce25dde2
BM
139 sql_db::delete_tables();
140 if (!sql_db::create_tables())
54b9603c 141 Message::Fail("Could not create SQL tables");
ce25dde2 142 } else if ($auth_method == "file_db")
57eb6560 143 {
ce25dde2 144 file_db::delete_db();
1bbc51fb 145 }
54b9603c
BM
146
147 $user = [
148 "user_name" => $opts->account_user,
149 "user_pass" => $opts->account_password,
150 "fname" => $opts->account_fname,
151 "lname" => $opts->account_lname,
152 "user_bio" => $opts->account_bio,
153 "email" => $opts->account_email
154 ];
155
156 create_new_user($user);
157 $lkup = new PanelUser($opts->account_user);
158 if (!$lkup->id)
159 {
160 Message::Fail("Could not create user");
2dbe2544
VP
161 return;
162 }
6f0e7ce4 163 $lkup->add_meta('role', 'Super-Admin');
54b9603c 164
3d4e4ec4
VP
165 /* Enable lookups on HIBP by default */
166 $config['hibp'] = true;
167
54b9603c
BM
168 /* Now, write all the config (config.php + settings in DB) */
169 write_config();
170 ?>
171 <br>
172 The configuration file has been written. Now, log in to the panel to proceed with the rest of the installation.<br><br>
173 <a class="text-center btn btn-primary" href="<?php echo BASE_URL; ?>">Let's go!</a></div>
174 <?php
175 return;
9680dd58 176 }
78069032
VP
177
178?>
9680dd58
VP
179<style>
180 table tr td {
181 font-style: italic;
182 }
183</style>
54b9603c 184<?php if (!$writable) { ?>
78069032
VP
185<div id="page1" class="container">
186 <br>
54b9603c
BM
187 The admin panel needs to be able to write the config file.<br>
188 Please run: <code>sudo chown <?php echo get_current_user(); ?> <?php echo UPATH; ?> -R</code><br>
189 And after that, refresh this webpage.<br><br>
190 If you have any questions about this, read <a href="https://www.unrealircd.org/docs/UnrealIRCd_webpanel#Permissions" target="_blank">the installation manual on permissions</a>.
78069032 191</div>
54b9603c
BM
192<?php
193 die;
194} ?>
78069032
VP
195
196<!-- Form start -->
2dbe2544 197<form method="post">
78069032 198<div id="page3" class="container">
ce25dde2 199 <h5>Database Backend</h5>
78069032 200 <br>
ce25dde2 201 Which database backend would you like to use?
78069032
VP
202 <br><br>
203 Please choose from the available options:
204 <div class="form-group">
205 <div class="form-check">
ce25dde2
BM
206 <input class="form-check-input" type="radio" name="auth_method" id="file_db_radio" value="file_db">
207 <label class="form-check-label" for="file_db_radio">
208 File-based database (Uses local files as a database, no additional setup needed)
78069032
VP
209 </label>
210 </div>
211 <div class="form-check">
ce25dde2
BM
212 <input class="form-check-input" type="radio" name="auth_method" id="sql_db_radio" value="sql_db">
213 <label class="form-check-label" for="sql_db_radio">
214 SQL Database (Requires an SQL database)
78069032
VP
215 </label>
216 </div>
217 </div>
218 <br>
219 <div id="sql_form" style="display:none">
220 Please enter your SQL information. <div id="sql_instructions" class="ml-4 btn btn-sm btn-info">View instructions</div>
221 <div class="form-group">
036c24c5
BM
222 <label for="sql_host">Hostname or IP</label>
223 <input name="sql_host" type="text" class="revalidation-needed-sql form-control" id="sql_host" aria-describedby="hostname_help" value="127.0.0.1">
78069032
VP
224 <small id="hostname_help" class="form-text text-muted">The hostname or IP address of your SQL server. You should use <code>127.0.0.1</code> for the same machine.</small>
225 </div>
226 <div class="form-group">
227 <label for="sql_db">Database name</label>
f0106ef0 228 <input name="sql_db" type="text" class="revalidation-needed-sql form-control" id="sql_db" aria-describedby="port_help">
78069032
VP
229 <small id="port_help" class="form-text text-muted">The name of the SQL database to write to and read from.</small>
230 </div>
231 <div class="form-group">
232 <label for="sql_username">Username</label>
17cca93a 233 <input name="sql_user" type="text" class="revalidation-needed-sql form-control" id="sql_user" aria-describedby="username_help" autocomplete="new-password">
78069032
VP
234 <small id="username_help" class="form-text text-muted">The name of SQL user</small>
235 </div>
236 <div class="form-group">
237 <label for="sql_password">Password</label>
17cca93a 238 <input name="sql_password" type="password" class="revalidation-needed-sql form-control" id="sql_password" autocomplete="new-password">
78069032 239 </div>
ca3c76f7
BM
240 <div class="form-group">
241 <label for="sql_table_prefix">Table prefix</label>
242 <input name="sql_table_prefix" type="text" class="revalidation-needed-sql form-control" id="sql_table_prefix" aria-describedby="sql_table_prefix_help" value="unreal_">
243 <small id="sql_table_prefix_help" class="form-text text-muted">The prefix for table names (leave blank for none)</small>
244 </div>
78069032
VP
245 </div>
246 <div class="text-center">
78069032
VP
247 <div id="page3_next" class="btn btn-primary ml-3">Next</div>
248 <div id="page3_test_connection" class="btn btn-primary ml-3" style="display: none">Test connection</div>
249 </div>
250</div>
f0106ef0 251<div id="page4" class="container" >
78069032
VP
252 <h5>Create your account</h5>
253 <br>
54b9603c 254 You need an account, let's make one.<br><br>
78069032 255 <div class="form-group">
f0106ef0 256 <label for="account_user" id="userlabel">Pick a username</label>
78069032 257 <input name="account_user" type="text" class="form-control" id="account_user" aria-describedby="username_help">
f0106ef0 258 <small id="username_help" class="form-text text-muted">Pick a username! Please make sure it's at least 3 characters long, contains no spaces, and is made of only letters and numbers.</small>
78069032
VP
259 </div>
260 <div class="form-group">
f0106ef0 261 <label for="account_password" id="passlabel">Password</label>
78069032
VP
262 <input name="account_password" type="password" class="form-control" id="account_password" aria-describedby="password_help">
263 <small id="password_help" class="form-text text-muted">Please choose a password that at least 8 characters long, contains at least one uppercase letter, one lowercase letter, one number and one symbol.</small>
264 </div>
265 <div class="form-group">
f0106ef0 266 <label for="account_password_conf" id="passconflabel">Confirm password</label>
78069032 267 <input name="account_password_conf" type="password" class="form-control" id="account_password_conf">
78069032
VP
268 </div>
269 <div class="form-group">
f0106ef0 270 <label for="account_email" id="emaillabel">Email address</label>
78069032 271 <input name="account_email" type="text" class="form-control" id="account_email" aria-describedby="email_help">
78069032
VP
272 </div>
273 <div class="form-group">
f0106ef0 274 <label for="account_fname" id="fnamelabel">First name</label>
9680dd58 275 <input name="account_fname" type="text" class="form-control" id="account_fname">
78069032
VP
276 </div>
277 <div class="form-group">
f0106ef0 278 <label for="account_lname" id="lnamelabel">Last name</label>
78069032
VP
279 <input name="account_lname" type="text" class="form-control" id="account_lname">
280 </div>
281 <div class="form-group">
f0106ef0 282 <label for="account_bio" id="biolabel">Bio</label>
78069032
VP
283 <textarea name="account_bio" type="text" class="form-control" id="account_bio"></textarea>
284 </div>
285 <div class="text-center">
286 <div id="page4_back" class="btn btn-secondary mr-3">Back</div>
9680dd58
VP
287 <button id="page4_next" type="submit" class="btn btn-primary ml-3">Submit</div>
288 </div>
289</div>
78069032 290</form>
6f2b5b4b
BM
291
292<!-- Database overwrite prompt -->
eddacbaa
BM
293<div class="modal fade" id="db_overwrite_modal" tabindex="-1" role="dialog" aria-labelledby="confirmModalCenterTitle" aria-hidden="true">
294 <div class="modal-dialog modal-dialog-centered" role="document">
295 <div class="modal-content">
296 <div class="modal-header">
297 <h5 class="modal-title" id="myModalLabel">Database already contains data</h5>
298 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
299 <span aria-hidden="true">&times;</span>
300 </button>
301 </div>
302 <div class="modal-body">
224b44c8 303 The database already exists and contains data.
eddacbaa
BM
304 If you continue then this existing data will be deleted.
305 </div>
306 <div class="modal-footer">
307 <button id="CloseButton" type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
308 <button id="ProceedButton" type="button" class="btn btn-danger" onclick="nextstep();">Continue</button>
309 </form>
310 </div>
311 </div>
312 </div>
313</div>
314
6f2b5b4b
BM
315<!-- Database error dialog -->
316<div class="modal fade" id="db_error_modal" tabindex="-1" role="dialog" aria-labelledby="confirmModalCenterTitle" aria-hidden="true">
317 <div class="modal-dialog modal-dialog-centered" role="document">
318 <div class="modal-content">
319 <div class="modal-header">
320 <h5 class="modal-title" id="myModalLabel">Database server error</h5>
321 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
322 <span aria-hidden="true">&times;</span>
323 </button>
324 </div>
325 <div class="modal-body" id="db_error_text">
326 Unable to connect to the database.
327 </div>
328 <div class="modal-footer">
329 <button id="CloseButton" type="button" class="btn btn-primary" data-dismiss="modal">Ok</button>
330 </form>
331 </div>
332 </div>
333 </div>
334</div>
335
78069032
VP
336<script>
337 let BASE_URL = '<?php echo BASE_URL; ?>';
338 let chmod_help = document.getElementById('chmod_help');
339
340 if (chmod_help)
341 chmod_help.addEventListener('click', e => {
342 window.open("https://www.unrealircd.org/docs/UnrealIRCd_webpanel#Permissions");
343 });
344
78069032 345 let page3 = document.getElementById('page3');
9680dd58 346 let page4 = document.getElementById('page4');
78069032 347
ce25dde2
BM
348 let file_db_radio = document.getElementById('file_db_radio');
349 let sql_db_radio = document.getElementById('sql_db_radio');
78069032 350 let sql_form = document.getElementById('sql_form');
036c24c5 351 let sql_host = document.getElementById('sql_host');
78069032
VP
352 let sql_db = document.getElementById('sql_db');
353 let sql_user = document.getElementById('sql_user');
354 let sql_pass = document.getElementById('sql_password');
355 let sql_test_conn = document.getElementById('page3_test_connection');
78069032
VP
356 let page3_next = document.getElementById('page3_next');
357
78069032
VP
358 let page4_back = document.getElementById('page4_back');
359 let page4_next = document.getElementById('page4_next');
78069032 360
54b9603c 361 page4.style.display = 'none';
9680dd58 362
f0106ef0
VP
363 revalidate_sql = document.querySelectorAll('.revalidation-needed-sql');
364 for (let i = 0; i < revalidate_sql.length; i++)
365 {
366 revalidate_sql[i].addEventListener('input', e => {
367 page3_next.style.display = 'none';
368 sql_test_conn.innerHTML = 'Test connection';
369 sql_test_conn.style.display = '';
370 sql_test_conn.classList.remove('disabled');
371 });
372 }
9680dd58 373
78069032 374 page3_next.addEventListener('click', e => {
224b44c8
BM
375<?php if (file_exists(UPATH.'/data/database.php')) { ?>
376 $('#db_overwrite_modal').modal();
377 e.preventDefault();
378 return false;
379<?php } ?>
78069032
VP
380 page3.style.display = 'none';
381 page4.style.display = '';
382 });
383
ce25dde2
BM
384 file_db_radio.addEventListener('click', e => {
385 if (file_db_radio.checked){
78069032
VP
386 sql_form.style.display = 'none';
387 sql_test_conn.style.display = 'none';
388 page3_next.style.display = '';
389 }
390 });
9680dd58 391
ce25dde2
BM
392 sql_db_radio.addEventListener('click', e => {
393 if (!file_db_radio.checked){
78069032
VP
394 sql_form.style.display = '';
395 sql_test_conn.style.display = '';
396 page3_next.style.display = 'none';
397 }
398 });
399
400 sql_instructions.addEventListener('click', e => {
401 window.open("https://www.unrealircd.org/docs/UnrealIRCd_webpanel#SQL_Authentication");
402 });
403
404 sql_test_conn.addEventListener('click', e => {
405 sql_test_conn.classList.add('disabled');
406 sql_test_conn.innerHTML = "Checking...";
055ded3a
BM
407 fetch(BASE_URL + 'api/installation.php', {
408 method:'POST',
409 headers: {'Content-Type':'application/x-www-form-urlencoded'},
410 body: 'method=sql&'+
411 'host='+encodeURIComponent(sql_host.value)+
412 '&database='+encodeURIComponent(sql_db.value)+
413 '&user='+encodeURIComponent(sql_user.value)+
eddacbaa
BM
414 '&password='+encodeURIComponent(sql_pass.value)+
415 '&table_prefix='+encodeURIComponent(sql_table_prefix.value)
055ded3a 416 })
78069032
VP
417 .then(response => response.json())
418 .then(data => {
419 if (data.success)
420 {
eddacbaa
BM
421 nextstep();
422 } else
423 if (data.warn)
424 {
425 $('#db_overwrite_modal').modal();
78069032
VP
426 }
427 else
428 {
429 sql_test_conn.innerHTML = "Failed!";
6f2b5b4b
BM
430 $('#db_error_text').html(data.error ? data.error : 'An error occured while connecting to the DB server');
431 $('#db_error_modal').modal();
78069032
VP
432 setTimeout(function() {
433 sql_test_conn.innerHTML = "Test connection";
434 sql_test_conn.classList.remove('disabled');
435 }, 2000);
436 }
437 })
438 .catch(error => {
439 sql_test_conn.innerHTML = "Failed!";
440 setTimeout(function() {
441 sql_test_conn.innerHTML = "Test connection";
442 sql_test_conn.classList.remove('disabled');
443 }, 2000);
444 });
f0106ef0 445 });
78069032 446
f0106ef0
VP
447 user_name_label = document.getElementById('userlabel');
448 user_name = document.getElementById('account_user');
449 user_pass_label = document.getElementById('passlabel');
450 user_pass = document.getElementById('account_password');
451 user_pass2_label = document.getElementById('passconflabel');
452 user_pass2 = document.getElementById('account_password_conf');
453 user_email_label = document.getElementById('emaillabel');
454 user_email = document.getElementById('account_email');
9680dd58
VP
455 user_fname = document.getElementById('account_fname');
456 user_lname = document.getElementById('account_lname');
457 user_bio = document.getElementById('account_bio');
f0106ef0 458
78069032
VP
459 page4_back.addEventListener('click', e => {
460 page4.style.display = 'none';
461 page3.style.display = '';
462 });
f0106ef0 463
78069032 464 page4_next.addEventListener('click', e => {
f0106ef0
VP
465
466 /* Form validation */
467 let req_not_met = ' <small style="color:red">Does not meet requirements</small>';
468 let errs = 0;
469 const regex_username = /^[a-zA-Z\d]{3,}$/;
470 if (!regex_username.test(user_name.value))
471 {
472 user_name_label.innerHTML = 'Pick a username!' + req_not_met;
473 errs++;
474 } else
475 user_name_label.innerHTML = 'Pick a username!';
476
477 let regex_pass = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/;
478 if (!regex_pass.test(user_pass.value))
479 {
480 user_pass_label.innerHTML = 'Password' + req_not_met;
481 errs++;
482 } else
483 user_pass_label.innerHTML = 'Password';
484
485 if (user_pass2.value !== user_pass.value)
486 {
487 user_pass2_label.innerHTML = 'Confirm password <small style="color:red">Passwords do not match</small>';
488 errs++;
489 } else
490 user_pass2_label.innerHTML = 'Confirm password';
491
492 const regex_email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
f0106ef0
VP
493 if (!regex_email.test(user_email.value))
494 {
495 user_email_label.innerHTML = 'Email address' + req_not_met;
496 errs++;
497 }
498 else
499 user_email_label.innerHTML = 'Email address';
500
501 if (errs)
9680dd58 502 {
54b9603c
BM
503 e.preventDefault();
504 return false;
9680dd58
VP
505 }
506
78069032 507 page4.style.display = 'none';
78069032 508 });
eddacbaa
BM
509
510 function nextstep()
511 {
512 $('#db_overwrite_modal').modal('hide');
224b44c8
BM
513 page3.style.display = 'none';
514 page4.style.display = '';
eddacbaa
BM
515 window.scrollTo(0,0);
516 }
78069032 517</script>