2 if (ini_get('output_buffering') == true) {
3 ini_set('output_buffering', 'off');
5 if (ini_get('zlib.output_compression') == true) {
6 ini_set('zlib.output_compression', 'off');
9 function check_requirements()
11 if (version_compare(PHP_VERSION
, '8.0.0', '<'))
13 die("This webserver is using PHP version ".PHP_VERSION
." but we require at least PHP 8.0.0.<br>".
14 "If you already installed PHP8 but are still seeing this error, then it means ".
15 "apache/nginx/.. is loading an older PHP version. Eg. on Debian/Ubuntu you need ".
16 "<code>apt-get install libapache2-mod-php8.2</code> (or a similar version) and ".
17 "<code>apt-get remove libapache2-mod-php7.4</code> (or a similar version). ".
18 "You may also need to choose again the PHP module to load in apache via <code>a2enmod php8.2</code>");
21 $loaded_extensions = get_loaded_extensions();
22 $required_extensions = ["mbstring", "sodium"];
23 $missing_extensions = [];
24 foreach ($required_extensions as $mod)
25 if (!in_array($mod, $loaded_extensions))
26 $missing_extensions[] = $mod;
28 if (count($missing_extensions) > 0)
30 $text = "<html>The following PHP module(s) need to be loaded:<br>\n<ul>\n";
31 $cmd = 'apt-get install';
32 foreach($missing_extensions as $mod)
34 $text .= "<li>$mod</li>\n";
38 $text .= "You need to install
/enable these PHP packages
and restart the webserver
.<br
>".
39 "If you are on Debian
/Ubuntu then run
<code
>$cmd</code
> ".
40 "and restart your
webserver (eg
: <code
>systemctl restart apache2
</code
> for apache
).";
45 check_requirements(); /* very early !! */
47 define('UPATH', dirname(__DIR__));
49 function get_config($setting)
54 foreach(explode("::", $setting) as $x)
64 function get_current_page_helper($name, $p, &$title)
66 if (isset($p["script
"]))
68 if (($p["script
"] != '') && str_ends_with($_SERVER['SCRIPT_FILENAME'],$p["script
"]))
71 if (isset($p["title
"]))
79 foreach ($p as $k=>$v)
81 $ret = get_current_page_helper($k, $v, $title);
88 /** Get current page and title */
89 function get_current_page(&$title)
92 foreach ($pages as $k=>$v)
94 $ret = get_current_page_helper($k, $v, $title);
100 function page_requires_no_config()
102 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"install
.php
") ||
103 str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation
.php
"))
110 function page_requires_no_login()
112 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"login
/index
.php
") ||
113 page_requires_no_config())
120 function read_config_file()
123 GLOBAL $config_transition_unreal_server;
126 if (!file_exists(UPATH."/config
/config
.php
") && file_exists(UPATH."/config
.php
"))
128 require_once UPATH . "/config
.php
";
129 require_once UPATH . "/config
/compat
.php
";
131 if ((@include(UPATH . "/config
/config
.php
")) !== 1)
133 if (isset($config['unrealircd']))
134 $config_transition_unreal_server = true;
135 /* Upgrade needed? */
136 $plugins_modified = false;
137 foreach ($config["plugins
"] as $k=>$v)
139 if ($v == "sql_auth
")
141 $config["plugins
"][$k] = "sql_db
";
142 $plugins_modified = true;
144 if ($v == "file_auth
")
146 $config["plugins
"][$k] = "file_db
";
147 $plugins_modified = true;
150 if ($plugins_modified)
156 function read_config_db()
160 if (page_requires_no_config())
163 $merge = DbSettings::get();
164 /* DB settings overwrite config.php keys: */
165 $config = array_merge($config, $merge);
168 function config_is_file_item($name)
170 if (($name == "plugins
") ||
171 ($name == "mysql
") ||
172 ($name == "base_url
") ||
173 ($name == "secrets
"))
180 function write_config_file()
185 foreach($config as $k=>$v)
187 if (config_is_file_item($k))
188 $file_settings[$k] = $v;
191 $cfg_filename = UPATH.'/config/config.php';
192 $tmpfile = UPATH.'/config/config.tmp.'.bin2hex(random_bytes(8)).'.php';
193 $fd = fopen($tmpfile, "w
");
195 die("Could not write to temporary config file
$tmpfile.<br
>We need write permissions on the config
/ directory
!<br
>");
197 $str = var_export($file_settings, true);
199 die("Error
while running
write_config_file() -- weird
!");
200 if (!fwrite($fd, "<?php\n
".
201 "/* This config file is written automatically by the UnrealIRCd webpanel.\n".
202 " * You are not really supposed to edit it manually.\n".
204 '$config = '.$str.";\n"))
206 die("Error writing to config file
$tmpfile (on fwrite
).<br
>");
209 die("Error writing to config file
$tmpfile (on close
).<br
>");
210 /* Now atomically rename the file */
211 if (!rename($tmpfile, $cfg_filename))
212 die("Could not
write (rename
) to file
".$cfg_filename."<br
>");
213 if (function_exists('opcache_invalidate'))
214 opcache_invalidate($cfg_filename);
216 /* Do not re-read config, as it would reinitialize config
217 * without having the DB settings read. (And it also
223 // XXX: handle unsetting of config items :D - explicit unset function ?
225 function write_config($setting = null)
229 /* Specific request? Easy, write only this setting to the DB (never used for file) */
230 if ($setting !== null)
232 return DbSettings::set($setting, $config[$setting]);
235 /* Otherwise write the whole config.
236 * TODO: avoid writing settings file if unneeded,
237 * as it is more noisy than db settings.
241 foreach($config as $k=>$v)
243 if (!config_is_file_item($k))
244 $db_settings[$k] = $v;
247 if (!write_config_file())
250 foreach($db_settings as $k=>$v)
252 $ret = DbSettings::set($k, $v);
260 function get_version()
262 $fd = @fopen(UPATH."/.git
/FETCH_HEAD
", "r
");
265 $line = fgets($fd, 512);
267 $commit = substr($line, 0, 8);
268 return $commit; /* short git commit id */
271 function generate_secrets()
275 if (!isset($config['secrets']))
276 $config['secrets'] = Array();
278 if (!isset($config['secrets']['pepper']))
279 $config['secrets']['pepper'] = rtrim(base64_encode(random_bytes(16)),'=');
281 if (!isset($config['secrets']['key']))
282 $config['secrets']['key'] = rtrim(base64_encode(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()),'=');
285 function get_active_rpc_server()
287 $servers = get_config("unrealircd
");
290 // TODO: make user able to override this - either in user or in session
292 foreach ($servers as $displayname=>$e)
294 if (isset($e["default"]) && $e["default"])
300 /* Set a new default RPC server */
301 function set_default_rpc_server($name)
305 /* Mark all other servers as non-default */
306 foreach ($config["unrealircd
"] as $n=>$e)
308 $config["unrealircd
"][$n]["default"] = false;
309 $config["unrealircd
"][$name]["default"] = true;
312 /* Ensure at least 1 server is default */
313 function set_at_least_one_default_rpc_server()
317 $has_default_rpc_server = false;
318 foreach ($config["unrealircd
"] as $name=>$e)
320 $has_default_rpc_server = true;
321 if (!$has_default_rpc_server)
323 /* Make first server in the list the default */
324 foreach ($config["unrealircd
"] as $name=>$e)
326 $config["unrealircd
"][$name]["default"] = true;
332 function secret_encrypt(string $text)
336 $key = base64_decode($config['secrets']['key']);
337 $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
338 $encrypted_text = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($text, '', $nonce, $key);
339 return "secret
:".rtrim(base64_encode($nonce),'=').':'.rtrim(base64_encode($encrypted_text),'='); // secret:base64(NONCE):base64(ENCRYPTEDTEXT)
342 function secret_decrypt(string $crypted)
346 $key = base64_decode($config['secrets']['key']);
347 $d = explode(":", $crypted);
350 $nonce = base64_decode($d[1]);
351 $ciphertext = base64_decode($d[2]);
353 $ret = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, '', $nonce, $key);
359 function upgrade_check()
361 GLOBAL $config_transition_unreal_server;
364 /* Moving of a config.php item to DB: */
365 if ($config_transition_unreal_server)
368 /* Our own stuff may need upgrading.. */
369 /* - generating secrets */
370 if (!isset($config['secrets']))
375 /* - encrypting rpc_password */
376 if (isset($config['unrealircd']) &&
377 isset($config['unrealircd']['rpc_password']) &&
378 !str_starts_with($config['unrealircd']['rpc_password'], "secret
:"))
380 $ret = secret_encrypt($config['unrealircd']['rpc_password']);
383 $config['unrealircd']['rpc_password'] = $ret;
384 write_config('unrealircd');
387 /* $config["unrealircd
"] should be an array now.. */
388 if (isset($config['unrealircd']) && isset($config['unrealircd']['rpc_password']))
390 $config["unrealircd
"]["default"] = true;
391 $config['unrealircd'] = [
392 "Primary
" => $config['unrealircd']];
393 write_config("unrealircd
");
396 $version = get_version();
397 if (!isset($config['webpanel_version']))
398 $config['webpanel_version'] = '';
399 if ($version != $config['webpanel_version'])
402 "old_version
" => $config['webpanel_version'],
403 "new_version
" => $version
405 /* And inform the hook (eg the database backends) */
406 Hook::run(HOOKTYPE_UPGRADE, $versioninfo);
407 /* And set the new version now that the upgrade is "done
" */
408 $config['webpanel_version'] = $version;
409 write_config("webpanel_version
");
413 function panel_start_session($user = false)
415 if (!isset($_SESSION))
417 session_set_cookie_params(86400); // can't set this to session_timeout due to catch-22
423 $user = unreal_get_current_user();
429 if (isset($user->user_meta['session_timeout']))
430 $timeout = (INT)$user->user_meta['session_timeout'];
432 if (!isset($_SESSION['session_timeout']))
433 $_SESSION['session_timeout'] = $timeout;
435 $_SESSION['last-activity'] = time();
439 /* Now read the config, and redirect to install screen if we don't have it */
440 $config_transition_unreal_server = false;
441 if (!read_config_file())
443 if (page_requires_no_config())
445 /* Allow empty conf */
447 if (!file_exists(UPATH."/config
/config
.php
") && !file_exists(UPATH."/config
.php
"))
449 header("Location
: settings
/install
.php
");
454 require_once UPATH . "/Classes
/class-hook
.php
";
455 if (!is_dir(UPATH . "/vendor
"))
456 die("The vendor
/ directory is missing
. Most likely the admin forgot to run
'composer install'\n
");
457 require_once UPATH . '/vendor/autoload.php';
458 require_once UPATH . "/Classes
/class-cmodes
.php
";
459 require_once UPATH . "/inc
/defines
.php
";
460 require_once UPATH . "/misc
/strings
.php
";
461 require_once UPATH . "/misc
/channel
-lookup
-misc
.php
";
462 require_once UPATH . "/misc
/user
-lookup
-misc
.php
";
463 require_once UPATH . "/misc
/server
-lookup
-misc
.php
";
464 require_once UPATH . "/misc
/ip
-whois
-misc
.php
";
465 require_once UPATH . "/Classes
/class-log
.php
";
466 require_once UPATH . "/Classes
/class-message
.php
";
467 require_once UPATH . "/Classes
/class-rpc
.php
";
468 require_once UPATH . "/Classes
/class-paneluser
.php
";
469 require_once UPATH . "/Classes
/class-notes
.php
";
470 require_once UPATH . "/Classes
/class-plugins
.php
";
472 /* Do various checks and reading, except during setup step 1. */
473 if (!page_requires_no_config())
475 /* Now that plugins are loaded, read config from DB */
478 /* Check if anything needs upgrading (eg on panel version change) */
482 if (!get_config("base_url
"))
483 die("The base_url was not found in your config
. Setup went wrong
?");
487 "Overview
" => ["script
"=>""],
488 "Users
" => ["script
"=>"users
/index
.php
"],
489 "Channels
" => ["script
"=>"channels
/index
.php
"],
490 "Servers
" => ["script
"=>"servers
/index
.php
"],
492 "Server Bans
" => ["script
" => "server
-bans
/index
.php
"],
493 "Name Bans
" => ["script
" => "server
-bans
/name
-bans
.php
"],
494 "Ban Exceptions
" => ["script
" => "server
-bans
/ban
-exceptions
.php
"],
496 "Spamfilter
" => ["script
" => "server
-bans
/spamfilter
.php
"],
497 "Logs
" => ["script
" => "logs
/index
.php
"],
499 "IP WHOIS
" => ["script
" => "tools
/ip
-whois
.php
","no_irc_server_required
"=>true],
502 "General Settings
" => ["script
" => "settings
/general
.php
"],
503 "RPC Servers
" => ["script
" => "settings
/rpc
-servers
.php
","no_irc_server_required
"=>true],
508 if (!panel_start_session())
510 if (!page_requires_no_login())
512 if (!is_auth_provided())
513 die("No authentication plugin loaded
. You must load either sql_db
, file_db
, or a similar auth plugin
!");
514 $current_page = $_SERVER['REQUEST_URI'];
515 header("Location
: ".get_config("base_url
")."login
/?redirect
=".urlencode($current_page));
519 $pages["Settings
"]["Accounts
"] = [
520 "script
" => "settings
/index
.php
",
521 "no_irc_server_required
"=>true
523 if (current_user_can(PERMISSION_MANAGE_USERS))
525 $pages["Settings
"]["Role Editor
"] = [
526 "script
"=>"settings
/user
-role
-edit
.php
",
527 "no_irc_server_required
"=>true
530 if (current_user_can(PERMISSION_MANAGE_PLUGINS))
532 $pages["Settings
"]["Plugins
"] = ["script
" => "settings
/plugins
.php
"];
534 $user = unreal_get_current_user();
537 /* Add logout page, if logged in */
539 "script
"=>"login
/?logout
=true",
540 "no_irc_server_required
"=>true
545 Hook::run(HOOKTYPE_NAVBAR, $pages);
547 /* Example to add new menu item:
552 * function __construct()
554 * Hook::func(HOOKTYPE_NAVBAR, [$this, 'add_menu'])
557 * function add_menu(&$pages) // this should pass by reference (using the & prefix)
559 * $page_name = "My
New Page
";
560 * $page_link = "link
/to
/page
.php
";
561 * $pages[$page_name] = $page_link;
566 $current_page = get_current_page($current_page_name);
569 global $rightClickMenu;
573 "onclick
" => "copy_to_clipboard(window
.getSelection().toString())",
574 "icon
" => "fa
-clipboard
"
578 "onclick
" => "paste_from_clipboard()",
579 "icon
" => "fa
-paint
-brush
",
584 Hook::run(HOOKTYPE_RIGHTCLICK_MENU, $rightClickMenu);