]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/blame - inc/common.php
Update the page title depending on what page you are.
[irc/unrealircd/unrealircd-webpanel.git] / inc / common.php
CommitLineData
e98b5a51 1<?php
81fe28b9 2if (version_compare(PHP_VERSION, '8.0.0', '<'))
fcd08f14 3 die("This webserver is using PHP version ".PHP_VERSION." but we require at least PHP 8.0.0.<br>".
81fe28b9
BM
4 "If you already installed PHP8 but are still seeing this error, then it means ".
5 "apache/nginx/.. is loading an older PHP version. Eg. on Debian/Ubuntu you need ".
6 "<code>apt-get install libapache2-mod-php8.2</code> (or a similar version) and ".
7 "<code>apt-get remove libapache2-mod-php7.4</code> (or a similar version). ".
8 "You may also need to choose again the PHP module to load in apache via <code>a2enmod php8.2</code>");
9
91f0829e 10define('UPATH', dirname(__DIR__));
ea90b321
BM
11
12function get_config($setting)
13{
14 GLOBAL $config;
15
16 $item = $config;
17 foreach(explode("::", $setting) as $x)
18 {
19 if (isset($item[$x]))
20 $item = $item[$x];
21 else
22 return NULL;
23 }
24 return $item;
25}
26
b98a5822 27function get_current_page_helper($name, $p, &$title)
e38f7458
BM
28{
29 if (isset($p["script"]))
30 {
31 if (($p["script"] != '') && str_ends_with($_SERVER['SCRIPT_FILENAME'],$p["script"]))
b98a5822
BM
32 {
33 // MATCH
34 if (isset($p["title"]))
35 $title = $p["title"];
36 else
37 $title = $name;
e38f7458 38 return $p;
b98a5822 39 }
e38f7458
BM
40 return null;
41 }
42 foreach ($p as $k=>$v)
43 {
b98a5822 44 $ret = get_current_page_helper($k, $v, $title);
e38f7458
BM
45 if ($ret !== null)
46 return $ret;
47 }
48 return null;
49}
50
b98a5822
BM
51/** Get current page and title */
52function get_current_page(&$title)
e38f7458
BM
53{
54 GLOBAL $pages;
55 foreach ($pages as $k=>$v)
56 {
b98a5822 57 $ret = get_current_page_helper($k, $v, $title);
e38f7458
BM
58 if ($ret !== null)
59 return $ret;
60 }
61}
62
36470548
BM
63function page_requires_no_config()
64{
65 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"install.php") ||
2dbe2544 66 str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation.php"))
36470548
BM
67 {
68 return TRUE;
69 }
70 return FALSE;
71}
72
cd1dee97
BM
73function page_requires_no_login()
74{
75 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"login/index.php") ||
76 page_requires_no_config())
77 {
78 return TRUE;
79 }
80 return FALSE;
81}
82
65fcaafb 83function read_config_file()
b41fa16f
BM
84{
85 GLOBAL $config;
f1027274 86 GLOBAL $config_transition_unreal_server;
b41fa16f 87
b41fa16f 88 $config = Array();
b41fa16f
BM
89 if (!file_exists(UPATH."/config/config.php") && file_exists(UPATH."/config.php"))
90 {
91 require_once UPATH . "/config.php";
92 require_once UPATH . "/config/compat.php";
93 }
cd1dee97 94 if ((@include(UPATH . "/config/config.php")) !== 1)
e1461204 95 return false;
f1027274
BM
96 if (isset($config['unrealircd']))
97 $config_transition_unreal_server = true;
ce25dde2
BM
98 /* Upgrade needed? */
99 $plugins_modified = false;
100 foreach ($config["plugins"] as $k=>$v)
101 {
102 if ($v == "sql_auth")
103 {
104 $config["plugins"][$k] = "sql_db";
105 $plugins_modified = true;
106 } else
107 if ($v == "file_auth")
108 {
109 $config["plugins"][$k] = "file_db";
110 $plugins_modified = true;
111 }
112 }
113 if ($plugins_modified)
114 write_config_file();
115
e1461204 116 return true;
b41fa16f 117}
ea90b321 118
65fcaafb
BM
119function read_config_db()
120{
121 GLOBAL $config;
122
5107db2f
BM
123 if (page_requires_no_config())
124 return;
125
65fcaafb
BM
126 $merge = DbSettings::get();
127 /* DB settings overwrite config.php keys: */
128 $config = array_merge($config, $merge);
129}
130
b41fa16f 131function config_is_file_item($name)
ea90b321 132{
f1027274 133 if (($name == "plugins") ||
65fcaafb 134 ($name == "mysql") ||
6b08fcb9
BM
135 ($name == "base_url") ||
136 ($name == "secrets"))
b41fa16f
BM
137 {
138 return true;
139 }
140 return false;
141}
142
143function write_config_file()
fc51fb47 144{
b41fa16f
BM
145 GLOBAL $config;
146
147 $file_settings = [];
148 foreach($config as $k=>$v)
149 {
150 if (config_is_file_item($k))
151 $file_settings[$k] = $v;
152 }
153
154 $cfg_filename = UPATH.'/config/config.php';
155 $tmpfile = UPATH.'/config/config.tmp.'.bin2hex(random_bytes(8)).'.php';
156 $fd = fopen($tmpfile, "w");
157 if (!$fd)
158 die("Could not write to temporary config file $tmpfile.<br>We need write permissions on the config/ directory!<br>");
159
160 $str = var_export($file_settings, true);
161 if ($str === null)
162 die("Error while running write_config_file() -- weird!");
163 if (!fwrite($fd, "<?php\n".
164 "/* This config file is written automatically by the UnrealIRCd webpanel.\n".
165 " * You are not really supposed to edit it manually.\n".
166 " */\n".
167 '$config = '.$str.";\n"))
168 {
169 die("Error writing to config file $tmpfile (on fwrite).<br>");
170 }
171 if (!fclose($fd))
172 die("Error writing to config file $tmpfile (on close).<br>");
173 /* Now atomically rename the file */
174 if (!rename($tmpfile, $cfg_filename))
175 die("Could not write (rename) to file ".$cfg_filename."<br>");
9f303b7c
BM
176 if (function_exists('opcache_invalidate'))
177 opcache_invalidate($cfg_filename);
b41fa16f
BM
178
179 /* Do not re-read config, as it would reinitialize config
180 * without having the DB settings read. (And it also
181 * serves no purpose)
182 */
65fcaafb 183 return true;
b41fa16f
BM
184}
185
186// XXX: handle unsetting of config items :D - explicit unset function ?
187
188function write_config($setting = null)
2dbe2544 189{
b41fa16f
BM
190 GLOBAL $config;
191
192 /* Specific request? Easy, write only this setting to the DB (never used for file) */
193 if ($setting !== null)
194 {
e1461204 195 return DbSettings::set($setting, $config[$setting]);
b41fa16f
BM
196 }
197
198 /* Otherwise write the whole config.
199 * TODO: avoid writing settings file if unneeded,
200 * as it is more noisy than db settings.
201 */
202 $db_settings = [];
203
204 foreach($config as $k=>$v)
205 {
206 if (!config_is_file_item($k))
207 $db_settings[$k] = $v;
208 }
209
210 if (!write_config_file())
211 return false;
212
213 foreach($db_settings as $k=>$v)
214 {
215 $ret = DbSettings::set($k, $v);
216 if (!$ret)
217 return $ret;
218 }
219
220 return true;
221}
222
e1461204
BM
223function get_version()
224{
225 $fd = @fopen(UPATH."/.git/FETCH_HEAD", "r");
226 if ($fd === false)
227 return "unknown";
228 $line = fgets($fd, 512);
229 fclose($fd);
230 $commit = substr($line, 0, 8);
231 return $commit; /* short git commit id */
232}
233
6b08fcb9
BM
234function generate_secrets()
235{
236 GLOBAL $config;
237
238 if (!isset($config['secrets']))
239 $config['secrets'] = Array();
240
241 if (!isset($config['secrets']['pepper']))
242 $config['secrets']['pepper'] = rtrim(base64_encode(random_bytes(16)),'=');
243
244 if (!isset($config['secrets']['key']))
245 $config['secrets']['key'] = rtrim(base64_encode(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()),'=');
246}
247
b056895f
BM
248function get_active_rpc_server()
249{
c9e0e590
BM
250 $servers = get_config("unrealircd");
251 if (empty($servers))
252 return;
b056895f
BM
253 // TODO: make user able to override this - either in user or in session
254
c9e0e590 255 foreach ($servers as $displayname=>$e)
b056895f
BM
256 {
257 if (isset($e["default"]) && $e["default"])
258 return $displayname;
259 }
260 return null;
261}
262
263/* Set a new default RPC server */
264function set_default_rpc_server($name)
265{
266 GLOBAL $config;
267
268 /* Mark all other servers as non-default */
269 foreach ($config["unrealircd"] as $n=>$e)
270 if ($n != $name)
271 $config["unrealircd"][$n]["default"] = false;
272 $config["unrealircd"][$name]["default"] = true;
273}
274
275/* Ensure at least 1 server is default */
276function set_at_least_one_default_rpc_server()
277{
278 GLOBAL $config;
279
280 $has_default_rpc_server = false;
281 foreach ($config["unrealircd"] as $name=>$e)
282 if ($e["default"])
283 $has_default_rpc_server = true;
284 if (!$has_default_rpc_server)
285 {
286 /* Make first server in the list the default */
287 foreach ($config["unrealircd"] as $name=>$e)
288 {
289 $config["unrealircd"][$name]["default"] = true;
290 break;
291 }
292 }
293}
294
a4850187
BM
295function secret_encrypt(string $text)
296{
297 GLOBAL $config;
298
299 $key = base64_decode($config['secrets']['key']);
300 $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
301 $encrypted_text = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($text, '', $nonce, $key);
302 return "secret:".rtrim(base64_encode($nonce),'=').':'.rtrim(base64_encode($encrypted_text),'='); // secret:base64(NONCE):base64(ENCRYPTEDTEXT)
303}
304
305function secret_decrypt(string $crypted)
306{
307 GLOBAL $config;
308
309 $key = base64_decode($config['secrets']['key']);
310 $d = explode(":", $crypted);
311 if (count($d) != 3)
312 return null;
313 $nonce = base64_decode($d[1]);
314 $ciphertext = base64_decode($d[2]);
315
316 $ret = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, '', $nonce, $key);
317 if ($ret === false)
318 return null;
319 return $ret;
320}
321
e1461204
BM
322function upgrade_check()
323{
324 GLOBAL $config_transition_unreal_server;
79e2577d 325 GLOBAL $config;
e1461204
BM
326
327 /* Moving of a config.php item to DB: */
328 if ($config_transition_unreal_server)
329 write_config();
330
6b08fcb9 331 /* Our own stuff may need upgrading.. */
a4850187 332 /* - generating secrets */
6b08fcb9
BM
333 if (!isset($config['secrets']))
334 {
335 generate_secrets();
336 write_config_file();
337 }
a4850187
BM
338 /* - encrypting rpc_password */
339 if (isset($config['unrealircd']) &&
340 isset($config['unrealircd']['rpc_password']) &&
341 !str_starts_with($config['unrealircd']['rpc_password'], "secret:"))
342 {
343 $ret = secret_encrypt($config['unrealircd']['rpc_password']);
344 if ($ret !== false)
345 {
346 $config['unrealircd']['rpc_password'] = $ret;
347 write_config('unrealircd');
348 }
349 }
41aad10c
BM
350 /* $config["unrealircd"] should be an array now.. */
351 if (isset($config['unrealircd']) && isset($config['unrealircd']['rpc_password']))
352 {
353 $config["unrealircd"]["default"] = true;
354 $config['unrealircd'] = [
355 "Primary" => $config['unrealircd']];
356 write_config("unrealircd");
357 }
6b08fcb9 358
e1461204
BM
359 $version = get_version();
360 if (!isset($config['webpanel_version']))
361 $config['webpanel_version'] = '';
362 if ($version != $config['webpanel_version'])
363 {
364 $versioninfo = [
365 "old_version" => $config['webpanel_version'],
366 "new_version" => $version
367 ];
6b08fcb9 368 /* And inform the hook (eg the database backends) */
e1461204
BM
369 Hook::run(HOOKTYPE_UPGRADE, $versioninfo);
370 /* And set the new version now that the upgrade is "done" */
371 $config['webpanel_version'] = $version;
372 write_config("webpanel_version");
373 }
374}
375
d3e3ec08
BM
376function panel_start_session($user = false)
377{
378 if (!isset($_SESSION))
379 {
380 session_set_cookie_params(86400); // can't set this to session_timeout due to catch-22
381 session_start();
382 }
383
384 if ($user === false)
385 {
386 $user = unreal_get_current_user();
387 if ($user === false)
388 return false;
389 }
390
cd1dee97
BM
391 $timeout = 3600;
392 if (isset($user->user_meta['session_timeout']))
393 $timeout = (INT)$user->user_meta['session_timeout'];
394
d3e3ec08
BM
395 if (!isset($_SESSION['session_timeout']))
396 $_SESSION['session_timeout'] = $timeout;
397
cd1dee97 398 $_SESSION['last-activity'] = time();
d3e3ec08
BM
399 return true;
400}
401
b41fa16f 402/* Now read the config, and redirect to install screen if we don't have it */
f1027274 403$config_transition_unreal_server = false;
65fcaafb 404if (!read_config_file())
fc51fb47 405{
b41fa16f
BM
406 if (page_requires_no_config())
407 {
408 /* Allow empty conf */
409 } else
410 if (!file_exists(UPATH."/config/config.php") && !file_exists(UPATH."/config.php"))
411 {
412 header("Location: settings/install.php");
413 die();
b41fa16f 414 }
ea90b321 415}
e1461204 416
c06c1713 417require_once UPATH . "/Classes/class-hook.php";
c4f272ad
BM
418if (!is_dir(UPATH . "/vendor"))
419 die("The vendor/ directory is missing. Most likely the admin forgot to run 'composer install'\n");
e98b5a51 420require_once UPATH . '/vendor/autoload.php';
e92763ac 421require_once UPATH . "/Classes/class-cmodes.php";
caf7fe32 422require_once UPATH . "/inc/defines.php";
d72d1923 423require_once UPATH . "/misc/strings.php";
e92763ac 424require_once UPATH . "/misc/channel-lookup-misc.php";
d72d1923
VP
425require_once UPATH . "/misc/user-lookup-misc.php";
426require_once UPATH . "/misc/server-lookup-misc.php";
427require_once UPATH . "/misc/ip-whois-misc.php";
428require_once UPATH . "/Classes/class-log.php";
429require_once UPATH . "/Classes/class-message.php";
430require_once UPATH . "/Classes/class-rpc.php";
6930484c 431require_once UPATH . "/Classes/class-paneluser.php";
d72d1923 432require_once UPATH . "/plugins.php";
1e6ffd06 433
cd1dee97
BM
434/* Do various checks and reading, except during setup step 1. */
435if (!page_requires_no_config())
436{
437 /* Now that plugins are loaded, read config from DB */
438 read_config_db();
e1461204 439
cd1dee97
BM
440 /* Check if anything needs upgrading (eg on panel version change) */
441 upgrade_check();
65fcaafb 442
cd1dee97
BM
443 /* And a check... */
444 if (!get_config("base_url"))
445 die("The base_url was not found in your config. Setup went wrong?");
446}
65fcaafb 447
5a7f0cde 448$pages = [
e38f7458
BM
449 "Overview" => ["script"=>""],
450 "Users" => ["script"=>"users/index.php"],
451 "Channels" => ["script"=>"channels/index.php"],
452 "Servers" => ["script"=>"servers/index.php"],
d6f10d25 453 "Server Bans" => [
e38f7458
BM
454 "Server Bans" => ["script" => "server-bans/index.php"],
455 "Name Bans" => ["script" => "server-bans/name-bans.php"],
456 "Ban Exceptions" => ["script" => "server-bans/ban-exceptions.php"],
d6f10d25 457 ],
e38f7458 458 "Spamfilter" => ["script" => "spamfilter.php"],
d6f10d25 459 "Tools" => [
e38f7458 460 "IP WHOIS" => ["script" => "tools/ip-whois.php","no_irc_server_required"=>true],
d6f10d25
BM
461 ],
462 "Settings" => [
e38f7458
BM
463 "Plugins" => ["script" => "settings/plugins.php","no_irc_server_required"=>true],
464 "RPC Servers" => ["script" => "settings/rpc-servers.php","no_irc_server_required"=>true],
d6f10d25 465 ],
180b8ec1 466
e38f7458 467 "News" => ["script" => "news.php","no_irc_server_required"=>true],
5a7f0cde 468];
c00c34d2 469
cd1dee97
BM
470if (!panel_start_session())
471{
472 if (!page_requires_no_login())
473 {
c51ca81c 474 if (!is_auth_provided())
ce25dde2 475 die("No authentication plugin loaded. You must load either sql_db, file_db, or a similar auth plugin!");
cd1dee97
BM
476 $current_page = $_SERVER['REQUEST_URI'];
477 header("Location: ".get_config("base_url")."login/?redirect=".urlencode($current_page));
fb06ed6c 478 die;
cd1dee97 479 }
c51ca81c 480} else {
e38f7458
BM
481 $pages["Settings"]["Accounts"] = [
482 "script" => "settings/index.php",
483 "no_irc_server_required"=>true
484 ];
1634b6ac 485 if (current_user_can(PERMISSION_MANAGE_USERS))
e38f7458
BM
486 {
487 $pages["Settings"]["Role Editor"] = [
488 "script"=>"settings/user-role-edit.php",
489 "no_irc_server_required"=>true
490 ];
491 }
c00c34d2
VP
492 $user = unreal_get_current_user();
493 if ($user)
494 {
495 /* Add logout page, if logged in */
e38f7458
BM
496 $pages["Logout"] = [
497 "script"=>"login/?logout=true",
498 "no_irc_server_required"=>true
499 ];
c00c34d2
VP
500 }
501}
c51ca81c 502
90dc8f2b
VP
503Hook::run(HOOKTYPE_NAVBAR, $pages);
504
505/* Example to add new menu item:
90dc8f2b
VP
506 *
507 * class MyPlugin
508 * {
509 *
55fd88eb
VP
510 * function __construct()
511 * {
512 * Hook::func(HOOKTYPE_NAVBAR, [$this, 'add_menu'])
513 * }
90dc8f2b 514 *
55fd88eb
VP
515 * function add_menu(&$pages) // this should pass by reference (using the & prefix)
516 * {
517 * $page_name = "My New Page";
518 * $page_link = "link/to/page.php";
519 * $pages[$page_name] = $page_link;
520 * }
90dc8f2b 521 * }
e38f7458
BM
522*/
523
b98a5822 524$current_page = get_current_page($current_page_name);