]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/blame - inc/common.php
Add Settings - RPC Servers (start of multi-server work).
[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
36470548
BM
27function page_requires_no_config()
28{
29 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"install.php") ||
2dbe2544 30 str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation.php"))
36470548
BM
31 {
32 return TRUE;
33 }
34 return FALSE;
35}
36
cd1dee97
BM
37function page_requires_no_login()
38{
39 if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"login/index.php") ||
40 page_requires_no_config())
41 {
42 return TRUE;
43 }
44 return FALSE;
45}
46
65fcaafb 47function read_config_file()
b41fa16f
BM
48{
49 GLOBAL $config;
f1027274 50 GLOBAL $config_transition_unreal_server;
b41fa16f 51
b41fa16f 52 $config = Array();
b41fa16f
BM
53 if (!file_exists(UPATH."/config/config.php") && file_exists(UPATH."/config.php"))
54 {
55 require_once UPATH . "/config.php";
56 require_once UPATH . "/config/compat.php";
57 }
cd1dee97 58 if ((@include(UPATH . "/config/config.php")) !== 1)
e1461204 59 return false;
f1027274
BM
60 if (isset($config['unrealircd']))
61 $config_transition_unreal_server = true;
ce25dde2
BM
62 /* Upgrade needed? */
63 $plugins_modified = false;
64 foreach ($config["plugins"] as $k=>$v)
65 {
66 if ($v == "sql_auth")
67 {
68 $config["plugins"][$k] = "sql_db";
69 $plugins_modified = true;
70 } else
71 if ($v == "file_auth")
72 {
73 $config["plugins"][$k] = "file_db";
74 $plugins_modified = true;
75 }
76 }
77 if ($plugins_modified)
78 write_config_file();
79
e1461204 80 return true;
b41fa16f 81}
ea90b321 82
65fcaafb
BM
83function read_config_db()
84{
85 GLOBAL $config;
86
5107db2f
BM
87 if (page_requires_no_config())
88 return;
89
65fcaafb
BM
90 $merge = DbSettings::get();
91 /* DB settings overwrite config.php keys: */
92 $config = array_merge($config, $merge);
93}
94
b41fa16f 95function config_is_file_item($name)
ea90b321 96{
f1027274 97 if (($name == "plugins") ||
65fcaafb 98 ($name == "mysql") ||
6b08fcb9
BM
99 ($name == "base_url") ||
100 ($name == "secrets"))
b41fa16f
BM
101 {
102 return true;
103 }
104 return false;
105}
106
107function write_config_file()
fc51fb47 108{
b41fa16f
BM
109 GLOBAL $config;
110
111 $file_settings = [];
112 foreach($config as $k=>$v)
113 {
114 if (config_is_file_item($k))
115 $file_settings[$k] = $v;
116 }
117
118 $cfg_filename = UPATH.'/config/config.php';
119 $tmpfile = UPATH.'/config/config.tmp.'.bin2hex(random_bytes(8)).'.php';
120 $fd = fopen($tmpfile, "w");
121 if (!$fd)
122 die("Could not write to temporary config file $tmpfile.<br>We need write permissions on the config/ directory!<br>");
123
124 $str = var_export($file_settings, true);
125 if ($str === null)
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".
130 " */\n".
131 '$config = '.$str.";\n"))
132 {
133 die("Error writing to config file $tmpfile (on fwrite).<br>");
134 }
135 if (!fclose($fd))
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>");
9f303b7c
BM
140 if (function_exists('opcache_invalidate'))
141 opcache_invalidate($cfg_filename);
b41fa16f
BM
142
143 /* Do not re-read config, as it would reinitialize config
144 * without having the DB settings read. (And it also
145 * serves no purpose)
146 */
65fcaafb 147 return true;
b41fa16f
BM
148}
149
150// XXX: handle unsetting of config items :D - explicit unset function ?
151
152function write_config($setting = null)
2dbe2544 153{
b41fa16f
BM
154 GLOBAL $config;
155
156 /* Specific request? Easy, write only this setting to the DB (never used for file) */
157 if ($setting !== null)
158 {
e1461204 159 return DbSettings::set($setting, $config[$setting]);
b41fa16f
BM
160 }
161
162 /* Otherwise write the whole config.
163 * TODO: avoid writing settings file if unneeded,
164 * as it is more noisy than db settings.
165 */
166 $db_settings = [];
167
168 foreach($config as $k=>$v)
169 {
170 if (!config_is_file_item($k))
171 $db_settings[$k] = $v;
172 }
173
174 if (!write_config_file())
175 return false;
176
177 foreach($db_settings as $k=>$v)
178 {
179 $ret = DbSettings::set($k, $v);
180 if (!$ret)
181 return $ret;
182 }
183
184 return true;
185}
186
e1461204
BM
187function get_version()
188{
189 $fd = @fopen(UPATH."/.git/FETCH_HEAD", "r");
190 if ($fd === false)
191 return "unknown";
192 $line = fgets($fd, 512);
193 fclose($fd);
194 $commit = substr($line, 0, 8);
195 return $commit; /* short git commit id */
196}
197
6b08fcb9
BM
198function generate_secrets()
199{
200 GLOBAL $config;
201
202 if (!isset($config['secrets']))
203 $config['secrets'] = Array();
204
205 if (!isset($config['secrets']['pepper']))
206 $config['secrets']['pepper'] = rtrim(base64_encode(random_bytes(16)),'=');
207
208 if (!isset($config['secrets']['key']))
209 $config['secrets']['key'] = rtrim(base64_encode(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()),'=');
210}
211
a4850187
BM
212function secret_encrypt(string $text)
213{
214 GLOBAL $config;
215
216 $key = base64_decode($config['secrets']['key']);
217 $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
218 $encrypted_text = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($text, '', $nonce, $key);
219 return "secret:".rtrim(base64_encode($nonce),'=').':'.rtrim(base64_encode($encrypted_text),'='); // secret:base64(NONCE):base64(ENCRYPTEDTEXT)
220}
221
222function secret_decrypt(string $crypted)
223{
224 GLOBAL $config;
225
226 $key = base64_decode($config['secrets']['key']);
227 $d = explode(":", $crypted);
228 if (count($d) != 3)
229 return null;
230 $nonce = base64_decode($d[1]);
231 $ciphertext = base64_decode($d[2]);
232
233 $ret = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, '', $nonce, $key);
234 if ($ret === false)
235 return null;
236 return $ret;
237}
238
e1461204
BM
239function upgrade_check()
240{
241 GLOBAL $config_transition_unreal_server;
79e2577d 242 GLOBAL $config;
e1461204
BM
243
244 /* Moving of a config.php item to DB: */
245 if ($config_transition_unreal_server)
246 write_config();
247
6b08fcb9 248 /* Our own stuff may need upgrading.. */
a4850187 249 /* - generating secrets */
6b08fcb9
BM
250 if (!isset($config['secrets']))
251 {
252 generate_secrets();
253 write_config_file();
254 }
a4850187
BM
255 /* - encrypting rpc_password */
256 if (isset($config['unrealircd']) &&
257 isset($config['unrealircd']['rpc_password']) &&
258 !str_starts_with($config['unrealircd']['rpc_password'], "secret:"))
259 {
260 $ret = secret_encrypt($config['unrealircd']['rpc_password']);
261 if ($ret !== false)
262 {
263 $config['unrealircd']['rpc_password'] = $ret;
264 write_config('unrealircd');
265 }
266 }
41aad10c
BM
267 /* $config["unrealircd"] should be an array now.. */
268 if (isset($config['unrealircd']) && isset($config['unrealircd']['rpc_password']))
269 {
270 $config["unrealircd"]["default"] = true;
271 $config['unrealircd'] = [
272 "Primary" => $config['unrealircd']];
273 write_config("unrealircd");
274 }
6b08fcb9 275
e1461204
BM
276 $version = get_version();
277 if (!isset($config['webpanel_version']))
278 $config['webpanel_version'] = '';
279 if ($version != $config['webpanel_version'])
280 {
281 $versioninfo = [
282 "old_version" => $config['webpanel_version'],
283 "new_version" => $version
284 ];
6b08fcb9 285 /* And inform the hook (eg the database backends) */
e1461204
BM
286 Hook::run(HOOKTYPE_UPGRADE, $versioninfo);
287 /* And set the new version now that the upgrade is "done" */
288 $config['webpanel_version'] = $version;
289 write_config("webpanel_version");
290 }
291}
292
d3e3ec08
BM
293function panel_start_session($user = false)
294{
295 if (!isset($_SESSION))
296 {
297 session_set_cookie_params(86400); // can't set this to session_timeout due to catch-22
298 session_start();
299 }
300
301 if ($user === false)
302 {
303 $user = unreal_get_current_user();
304 if ($user === false)
305 return false;
306 }
307
cd1dee97
BM
308 $timeout = 3600;
309 if (isset($user->user_meta['session_timeout']))
310 $timeout = (INT)$user->user_meta['session_timeout'];
311
d3e3ec08
BM
312 if (!isset($_SESSION['session_timeout']))
313 $_SESSION['session_timeout'] = $timeout;
314
cd1dee97 315 $_SESSION['last-activity'] = time();
d3e3ec08
BM
316 return true;
317}
318
b41fa16f 319/* Now read the config, and redirect to install screen if we don't have it */
f1027274 320$config_transition_unreal_server = false;
65fcaafb 321if (!read_config_file())
fc51fb47 322{
b41fa16f
BM
323 if (page_requires_no_config())
324 {
325 /* Allow empty conf */
326 } else
327 if (!file_exists(UPATH."/config/config.php") && !file_exists(UPATH."/config.php"))
328 {
329 header("Location: settings/install.php");
330 die();
b41fa16f 331 }
ea90b321 332}
e1461204 333
c06c1713 334require_once UPATH . "/Classes/class-hook.php";
c4f272ad
BM
335if (!is_dir(UPATH . "/vendor"))
336 die("The vendor/ directory is missing. Most likely the admin forgot to run 'composer install'\n");
e98b5a51 337require_once UPATH . '/vendor/autoload.php';
e92763ac 338require_once UPATH . "/Classes/class-cmodes.php";
caf7fe32 339require_once UPATH . "/inc/defines.php";
d72d1923 340require_once UPATH . "/misc/strings.php";
e92763ac 341require_once UPATH . "/misc/channel-lookup-misc.php";
d72d1923
VP
342require_once UPATH . "/misc/user-lookup-misc.php";
343require_once UPATH . "/misc/server-lookup-misc.php";
344require_once UPATH . "/misc/ip-whois-misc.php";
345require_once UPATH . "/Classes/class-log.php";
346require_once UPATH . "/Classes/class-message.php";
347require_once UPATH . "/Classes/class-rpc.php";
6930484c 348require_once UPATH . "/Classes/class-paneluser.php";
d72d1923 349require_once UPATH . "/plugins.php";
1e6ffd06 350
cd1dee97
BM
351/* Do various checks and reading, except during setup step 1. */
352if (!page_requires_no_config())
353{
354 /* Now that plugins are loaded, read config from DB */
355 read_config_db();
e1461204 356
cd1dee97
BM
357 /* Check if anything needs upgrading (eg on panel version change) */
358 upgrade_check();
65fcaafb 359
cd1dee97
BM
360 /* And a check... */
361 if (!get_config("base_url"))
362 die("The base_url was not found in your config. Setup went wrong?");
363}
65fcaafb 364
5a7f0cde 365$pages = [
69f605af
BM
366 "Overview" => "",
367 "Users" => "users",
368 "Channels" => "channels",
369 "Servers" => "servers",
d6f10d25
BM
370 "Server Bans" => [
371 "Server Bans" => "server-bans",
372 "Name Bans" => "server-bans/name-bans.php",
373 "Ban Exceptions" => "server-bans/ban-exceptions.php"
374 ],
69f605af 375 "Spamfilter" => "spamfilter.php",
d6f10d25
BM
376 "Tools" => [
377 "IP WHOIS" => "tools/ip-whois.php",
378 ],
379 "Settings" => [
380 "Plugins" => "settings/plugins.php",
41aad10c 381 "RPC Servers" => "settings/rpc-servers.php",
d6f10d25 382 ],
180b8ec1 383
c00c34d2 384 "News" => "news.php",
5a7f0cde 385];
c00c34d2 386
cd1dee97
BM
387if (!panel_start_session())
388{
389 if (!page_requires_no_login())
390 {
c51ca81c 391 if (!is_auth_provided())
ce25dde2 392 die("No authentication plugin loaded. You must load either sql_db, file_db, or a similar auth plugin!");
cd1dee97
BM
393 $current_page = $_SERVER['REQUEST_URI'];
394 header("Location: ".get_config("base_url")."login/?redirect=".urlencode($current_page));
fb06ed6c 395 die;
cd1dee97 396 }
c51ca81c 397} else {
6210ec48 398 $pages["Settings"]["Accounts"] = "settings";
1634b6ac
VP
399 if (current_user_can(PERMISSION_MANAGE_USERS))
400 $pages["Settings"]["Role Editor"] = "settings/user-role-edit.php";
c00c34d2
VP
401 $user = unreal_get_current_user();
402 if ($user)
403 {
404 /* Add logout page, if logged in */
405 $pages["Logout"] = "login/?logout=true";
406 }
407}
c51ca81c 408
90dc8f2b
VP
409Hook::run(HOOKTYPE_NAVBAR, $pages);
410
411/* Example to add new menu item:
90dc8f2b
VP
412 *
413 * class MyPlugin
414 * {
415 *
55fd88eb
VP
416 * function __construct()
417 * {
418 * Hook::func(HOOKTYPE_NAVBAR, [$this, 'add_menu'])
419 * }
90dc8f2b 420 *
55fd88eb
VP
421 * function add_menu(&$pages) // this should pass by reference (using the & prefix)
422 * {
423 * $page_name = "My New Page";
424 * $page_link = "link/to/page.php";
425 * $pages[$page_name] = $page_link;
426 * }
90dc8f2b
VP
427 * }
428*/