2 if (version_compare(PHP_VERSION
, '8.0.0', '<'))
3 die("This webserver is using PHP version ".PHP_VERSION
." but we require at least PHP 8.0.0.<br>".
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>");
10 define('UPATH', dirname(__DIR__
));
12 function get_config($setting)
17 foreach(explode("::", $setting) as $x)
27 function page_requires_no_config()
29 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"install.php") ||
30 str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation.php"))
37 function page_requires_no_login()
39 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"login/index.php") ||
40 page_requires_no_config())
47 function read_config_file()
50 GLOBAL $config_transition_unreal_server;
53 if (!file_exists(UPATH
."/config/config.php") && file_exists(UPATH
."/config.php"))
55 require_once UPATH
. "/config.php";
56 require_once UPATH
. "/config/compat.php";
58 if ((@include(UPATH
. "/config/config.php")) !== 1)
60 if (isset($config['unrealircd']))
61 $config_transition_unreal_server = true;
63 $plugins_modified = false;
64 foreach ($config["plugins"] as $k=>$v)
68 $config["plugins"][$k] = "sql_db";
69 $plugins_modified = true;
71 if ($v == "file_auth")
73 $config["plugins"][$k] = "file_db";
74 $plugins_modified = true;
77 if ($plugins_modified)
83 function read_config_db()
87 if (page_requires_no_config())
90 $merge = DbSettings
::get();
91 /* DB settings overwrite config.php keys: */
92 $config = array_merge($config, $merge);
95 function config_is_file_item($name)
97 if (($name == "plugins") ||
99 ($name == "base_url") ||
100 ($name == "secrets"))
107 function write_config_file()
112 foreach($config as $k=>$v)
114 if (config_is_file_item($k))
115 $file_settings[$k] = $v;
118 $cfg_filename = UPATH
.'/config/config.php';
119 $tmpfile = UPATH
.'/config/config.tmp.'.bin2hex(random_bytes(8)).'.php';
120 $fd = fopen($tmpfile, "w");
122 die("Could not write to temporary config file $tmpfile.<br>We need write permissions on the config/ directory!<br>");
124 $str = var_export($file_settings, true);
126 die("Error while running write_config_file() -- weird!");
127 if (!fwrite($fd, "<?php\n".
128 "/* This config file is written automatically by the UnrealIRCd webpanel.\n".
129 " * You are not really supposed to edit it manually.\n".
131 '$config = '.$str.";\n"))
133 die("Error writing to config file $tmpfile (on fwrite).<br>");
136 die("Error writing to config file $tmpfile (on close).<br>");
137 /* Now atomically rename the file */
138 if (!rename($tmpfile, $cfg_filename))
139 die("Could not write (rename) to file ".$cfg_filename."<br>");
140 if (function_exists('opcache_invalidate'))
141 opcache_invalidate($cfg_filename);
143 /* Do not re-read config, as it would reinitialize config
144 * without having the DB settings read. (And it also
150 // XXX: handle unsetting of config items :D - explicit unset function ?
152 function write_config($setting = null)
156 /* Specific request? Easy, write only this setting to the DB (never used for file) */
157 if ($setting !== null)
159 return DbSettings
::set($setting, $config[$setting]);
162 /* Otherwise write the whole config.
163 * TODO: avoid writing settings file if unneeded,
164 * as it is more noisy than db settings.
168 foreach($config as $k=>$v)
170 if (!config_is_file_item($k))
171 $db_settings[$k] = $v;
174 if (!write_config_file())
177 foreach($db_settings as $k=>$v)
179 $ret = DbSettings
::set($k, $v);
187 function get_version()
189 $fd = @fopen(UPATH
."/.git/FETCH_HEAD", "r");
192 $line = fgets($fd, 512);
194 $commit = substr($line, 0, 8);
195 return $commit; /* short git commit id */
198 function generate_secrets()
202 if (!isset($config['secrets']))
203 $config['secrets'] = Array();
205 if (!isset($config['secrets']['pepper']))
206 $config['secrets']['pepper'] = rtrim(base64_encode(random_bytes(16)),'=');
208 if (!isset($config['secrets']['key']))
209 $config['secrets']['key'] = rtrim(base64_encode(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()),'=');
212 function get_active_rpc_server()
214 $servers = get_config("unrealircd");
217 // TODO: make user able to override this - either in user or in session
219 foreach ($servers as $displayname=>$e)
221 if (isset($e["default"]) && $e["default"])
227 /* Set a new default RPC server */
228 function set_default_rpc_server($name)
232 /* Mark all other servers as non-default */
233 foreach ($config["unrealircd"] as $n=>$e)
235 $config["unrealircd"][$n]["default"] = false;
236 $config["unrealircd"][$name]["default"] = true;
239 /* Ensure at least 1 server is default */
240 function set_at_least_one_default_rpc_server()
244 $has_default_rpc_server = false;
245 foreach ($config["unrealircd"] as $name=>$e)
247 $has_default_rpc_server = true;
248 if (!$has_default_rpc_server)
250 /* Make first server in the list the default */
251 foreach ($config["unrealircd"] as $name=>$e)
253 $config["unrealircd"][$name]["default"] = true;
259 function secret_encrypt(string $text)
263 $key = base64_decode($config['secrets']['key']);
264 $nonce = \random_bytes
(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
);
265 $encrypted_text = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($text, '', $nonce, $key);
266 return "secret:".rtrim(base64_encode($nonce),'=').':'.rtrim(base64_encode($encrypted_text),'='); // secret:base64(NONCE):base64(ENCRYPTEDTEXT)
269 function secret_decrypt(string $crypted)
273 $key = base64_decode($config['secrets']['key']);
274 $d = explode(":", $crypted);
277 $nonce = base64_decode($d[1]);
278 $ciphertext = base64_decode($d[2]);
280 $ret = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, '', $nonce, $key);
286 function upgrade_check()
288 GLOBAL $config_transition_unreal_server;
291 /* Moving of a config.php item to DB: */
292 if ($config_transition_unreal_server)
295 /* Our own stuff may need upgrading.. */
296 /* - generating secrets */
297 if (!isset($config['secrets']))
302 /* - encrypting rpc_password */
303 if (isset($config['unrealircd']) &&
304 isset($config['unrealircd']['rpc_password']) &&
305 !str_starts_with($config['unrealircd']['rpc_password'], "secret:"))
307 $ret = secret_encrypt($config['unrealircd']['rpc_password']);
310 $config['unrealircd']['rpc_password'] = $ret;
311 write_config('unrealircd');
314 /* $config["unrealircd"] should be an array now.. */
315 if (isset($config['unrealircd']) && isset($config['unrealircd']['rpc_password']))
317 $config["unrealircd"]["default"] = true;
318 $config['unrealircd'] = [
319 "Primary" => $config['unrealircd']];
320 write_config("unrealircd");
323 $version = get_version();
324 if (!isset($config['webpanel_version']))
325 $config['webpanel_version'] = '';
326 if ($version != $config['webpanel_version'])
329 "old_version" => $config['webpanel_version'],
330 "new_version" => $version
332 /* And inform the hook (eg the database backends) */
333 Hook
::run(HOOKTYPE_UPGRADE
, $versioninfo);
334 /* And set the new version now that the upgrade is "done" */
335 $config['webpanel_version'] = $version;
336 write_config("webpanel_version");
340 function panel_start_session($user = false)
342 if (!isset($_SESSION))
344 session_set_cookie_params(86400); // can't set this to session_timeout due to catch-22
350 $user = unreal_get_current_user();
356 if (isset($user->user_meta
['session_timeout']))
357 $timeout = (INT)$user->user_meta
['session_timeout'];
359 if (!isset($_SESSION['session_timeout']))
360 $_SESSION['session_timeout'] = $timeout;
362 $_SESSION['last-activity'] = time();
366 /* Now read the config, and redirect to install screen if we don't have it */
367 $config_transition_unreal_server = false;
368 if (!read_config_file())
370 if (page_requires_no_config())
372 /* Allow empty conf */
374 if (!file_exists(UPATH
."/config/config.php") && !file_exists(UPATH
."/config.php"))
376 header("Location: settings/install.php");
381 require_once UPATH
. "/Classes/class-hook.php";
382 if (!is_dir(UPATH
. "/vendor"))
383 die("The vendor/ directory is missing. Most likely the admin forgot to run 'composer install'\n");
384 require_once UPATH
. '/vendor/autoload.php';
385 require_once UPATH
. "/Classes/class-cmodes.php";
386 require_once UPATH
. "/inc/defines.php";
387 require_once UPATH
. "/misc/strings.php";
388 require_once UPATH
. "/misc/channel-lookup-misc.php";
389 require_once UPATH
. "/misc/user-lookup-misc.php";
390 require_once UPATH
. "/misc/server-lookup-misc.php";
391 require_once UPATH
. "/misc/ip-whois-misc.php";
392 require_once UPATH
. "/Classes/class-log.php";
393 require_once UPATH
. "/Classes/class-message.php";
394 require_once UPATH
. "/Classes/class-rpc.php";
395 require_once UPATH
. "/Classes/class-paneluser.php";
396 require_once UPATH
. "/plugins.php";
398 /* Do various checks and reading, except during setup step 1. */
399 if (!page_requires_no_config())
401 /* Now that plugins are loaded, read config from DB */
404 /* Check if anything needs upgrading (eg on panel version change) */
408 if (!get_config("base_url"))
409 die("The base_url was not found in your config. Setup went wrong?");
415 "Channels" => "channels",
416 "Servers" => "servers",
418 "Server Bans" => "server-bans",
419 "Name Bans" => "server-bans/name-bans.php",
420 "Ban Exceptions" => "server-bans/ban-exceptions.php"
422 "Spamfilter" => "spamfilter.php",
424 "IP WHOIS" => "tools/ip-whois.php",
427 "Plugins" => "settings/plugins.php",
428 "RPC Servers" => "settings/rpc-servers.php",
431 "News" => "news.php",
434 if (!panel_start_session())
436 if (!page_requires_no_login())
438 if (!is_auth_provided())
439 die("No authentication plugin loaded. You must load either sql_db, file_db, or a similar auth plugin!");
440 $current_page = $_SERVER['REQUEST_URI'];
441 header("Location: ".get_config("base_url")."login/?redirect=".urlencode($current_page));
445 $pages["Settings"]["Accounts"] = "settings";
446 if (current_user_can(PERMISSION_MANAGE_USERS
))
447 $pages["Settings"]["Role Editor"] = "settings/user-role-edit.php";
448 $user = unreal_get_current_user();
451 /* Add logout page, if logged in */
452 $pages["Logout"] = "login/?logout=true";
456 Hook
::run(HOOKTYPE_NAVBAR
, $pages);
458 /* Example to add new menu item:
463 * function __construct()
465 * Hook::func(HOOKTYPE_NAVBAR, [$this, 'add_menu'])
468 * function add_menu(&$pages) // this should pass by reference (using the & prefix)
470 * $page_name = "My New Page";
471 * $page_link = "link/to/page.php";
472 * $pages[$page_name] = $page_link;