PostfixAdmin - web based virtual user administration interface for Postfix mail servers https://postfixadmin.github.io/postfixadmin/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

736 lines
27 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
  1. <?php
  2. $PALANG = [];
  3. require_once('common.php');
  4. ?>
  5. <html lang="">
  6. <head>
  7. <meta charset="utf-8">
  8. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  9. <meta name="viewport" content="width=device-width, initial-scale=1">
  10. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  11. <title>Postfix Admin - Setup</title>
  12. <link rel="shortcut icon" href="images/favicon.ico"/>
  13. <link rel="stylesheet" href="css/bootstrap-3.4.1-dist/css/bootstrap.min.css"/>
  14. <link rel="stylesheet" href="css/bootstrap.css"/>
  15. <!-- https://www.srihash.org/ -->
  16. <script src="jquery-1.12.4.min.js"
  17. integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
  18. crossorigin="anonymous"></script>
  19. <script src="css/bootstrap-3.4.1-dist/js/moment-with-locales.min.js"></script>
  20. <script src="css/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
  21. <script src="css/bootstrap-3.4.1-dist/js/bootstrap-datetimepicker.min.js"></script>
  22. </head>
  23. <body>
  24. <nav class="navbar navbar-default fixed-top">
  25. <div class="container-fluid">
  26. <div class="navbar-header">
  27. <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
  28. aria-expanded="false" aria-controls="navbar">
  29. <span class="sr-only">Toggle navigation</span>
  30. <span class="icon-bar"></span>
  31. <span class="icon-bar"></span>
  32. <span class="icon-bar"></span>
  33. </button>
  34. <a class="navbar-brand" href='main.php'><img id="login_header_logo" src="images/postbox.png"
  35. alt="Logo"/></a>
  36. </div>
  37. </div>
  38. </nav>
  39. <?php
  40. /**
  41. * Postfix Admin
  42. *
  43. * LICENSE
  44. * This source file is subject to the GPL license that is bundled with
  45. * this package in the file LICENSE.TXT.
  46. *
  47. * Further details on the project are available at http://postfixadmin.sf.net
  48. *
  49. * @version $Id$
  50. * @license GNU GPL v2 or later.
  51. *
  52. * File: setup.php
  53. * Used to help ensure a server is setup appropriately during installation/setup.
  54. */
  55. $configSetupPassword = Config::read_string('setup_password');
  56. $errors = [];
  57. $configSetupDone = false;
  58. $authenticated = false;
  59. $old_setup_password = false;
  60. if (strlen($configSetupPassword) == 73 && strpos($configSetupPassword, ':') == 32) {
  61. $old_setup_password = true;
  62. } elseif ($configSetupPassword != 'changeme' && $configSetupPassword != '') {
  63. $configSetupDone = true;
  64. $pass = safepost('setup_password', 'invalid');
  65. if ($pass != 'invalid') {
  66. if (password_verify(safepost('setup_password', 'invalid'), $configSetupPassword)) {
  67. $authenticated = true;
  68. } else {
  69. $errors['setup_login_password'] = "Password verification failed.";
  70. }
  71. }
  72. }
  73. ?>
  74. <?php
  75. $todo = '<span class="font-weight-bold text-warning">TODO</span>';
  76. $tick = ' ✅ ';
  77. ?>
  78. <div class="container">
  79. <div class="row">
  80. <h1 class="h1">Configure and Setup Postfixadmin</h1>
  81. <p>This page helps you setup PostfixAdmin. For further help see <a
  82. href="https://github.com/postfixadmin/postfixadmin/tree/master/DOCUMENTS">the documentation</a>.</p>
  83. <?php
  84. if (!isset($_SERVER['HTTPS'])) {
  85. echo "<h2 class='h2 text-danger'>Warning: connection not secure, switch to https if possible</h2>";
  86. } ?>
  87. <div class="col-12">
  88. <ul>
  89. <li>
  90. <?php
  91. if ($configSetupDone) {
  92. echo $tick . " setup_password configured";
  93. } else {
  94. echo $todo . " You need to have a setup_pasword hash configured in a <code>config.local.php</code> file";
  95. }
  96. ?>
  97. </li>
  98. <li>
  99. <?php
  100. if ($authenticated) {
  101. echo $tick . " You are logged in with the setup_password, some environment and hosting checks are displayed below.";
  102. } else {
  103. echo $todo . " You need to authenticate using the setup_password before you can perform some environment and hosting checks.";
  104. }
  105. ?>
  106. </li>
  107. </ul>
  108. <?php if (!$authenticated) { ?>
  109. <p> One you have logged in with the setup_password, this page will ... </p>
  110. <ul>
  111. <li> run some simple hosting/environment checks which may help identify problems with your
  112. environment
  113. </li>
  114. <li> create/update your database of choice,</li>
  115. <li> allow you to list / add super user accounts</li>
  116. </ul>
  117. <?php } ?>
  118. </div>
  119. </div>
  120. <?php
  121. if ($configSetupDone && !$authenticated) { ?>
  122. <div class="row">
  123. <div class="col-12">
  124. <h2 class="h2">Login with setup_password</h2>
  125. <form name="authenticate" class="col-2 form-horizontal" method="post">
  126. <div class="form-group">
  127. <label for="setup_password" class="col-sm-4 control-label">Setup password</label>
  128. <div class="col-sm-4">
  129. <input class="form-control" type="password" name="setup_password" minlength=5
  130. id="setup_password"
  131. value=""/>
  132. <?= _error_field($errors, 'setup_login_password'); ?>
  133. </div>
  134. </div>
  135. <div class="form-group">
  136. <div class="col-sm-offset-4 col-sm-4">
  137. <button class="btn btn-primary" type="submit" name="submit" value="setuppw">Login with
  138. setup_password.
  139. </button>
  140. </div>
  141. </div>
  142. </form>
  143. <p>If you've forgotten your super-admin password, you can generate a new one using the
  144. <em>Generate</em>
  145. form and update your <code>config.local.php</code></p>
  146. </div>
  147. </div>
  148. <?php
  149. } ?>
  150. <div class="row">
  151. <div class="col-12">
  152. <?php
  153. if (!$configSetupDone) {
  154. echo <<<EOF
  155. <p><strong>For a new installation, you must generate a 'setup_password' to go into your config.local.php file.</strong></p>
  156. <p>You can use the form below, or run something like the following in a shell - <code>php -r 'echo password_hash("password", PASSWORD_DEFAULT);'</code><p>
  157. EOF;
  158. }
  159. if ($old_setup_password) {
  160. echo '<p class="text-danger"><strong>Your setup_password is in an obsolete format. As of PostfixAdmin 3.3 it needs regenerating.</strong>';
  161. }
  162. if (!$authenticated || !$configSetupDone) { ?>
  163. <h2>Generate setup_password</h2>
  164. <?php
  165. $form_error = '';
  166. $result = '';
  167. if (safepost('form') === "setuppw") {
  168. $errors = [];
  169. # "setup password" form submitted
  170. if (safepost('setup_password', 'abc') != safepost('setup_password2')) {
  171. $errors['setup_password'] = "The two passwords differ!";
  172. $form_error = 'has-error';
  173. } else {
  174. $msgs = validate_password(safepost('setup_password'));
  175. if (empty($msgs)) {
  176. // form has been submitted; both fields filled in, so generate a new setup password.
  177. $hash = password_hash(safepost('setup_password'), PASSWORD_DEFAULT);
  178. $result = '<p>If you want to use the password you entered as setup password, edit config.inc.php or config.local.php and set</p>';
  179. $result .= "<pre>\$CONF['setup_password'] = '$hash';</pre><p>After adding, refresh this page and log in using it.</p>";
  180. } else {
  181. $form_error = 'has-error';
  182. $errors['setup_password'] = implode(', ', $msgs);
  183. }
  184. }
  185. }
  186. ?>
  187. <form name="setuppw" method="post" class="form-horizontal" action="setup.php">
  188. <input type="hidden" name="form" value="setuppw"/>
  189. <div class="form-group <?= $form_error ?>">
  190. <label for="setup_password" class="col-sm-4 control-label">Setup password</label>
  191. <div class="col-sm-4">
  192. <input class="form-control" type="password" name="setup_password" minlength=5
  193. id="setup_password"
  194. autocomplete="new-password"
  195. value=""/>
  196. <?= _error_field($errors, 'setup_password'); ?>
  197. </div>
  198. </div>
  199. <div class="form-group <?= $form_error ?>">
  200. <label for="setup_password2" class="col-sm-4 control-label">Setup password (again)</label>
  201. <div class="col-sm-4">
  202. <input class="form-control" type="password" name="setup_password2"
  203. minlength=5 id="setup_password2"
  204. autocomplete="new-password"
  205. value=""/>
  206. <?= _error_field($errors, 'setup_password2'); ?>
  207. </div>
  208. </div>
  209. <div class="form-group">
  210. <div class="col-sm-offset-4 col-sm-4">
  211. <button class="btn btn-primary" type="submit" name="submit" value="setuppw">Generate
  212. setup_password
  213. hash
  214. </button>
  215. </div>
  216. </div>
  217. </form>
  218. <?= $result ?>
  219. <?php
  220. } // end if(!$authenticated)?>
  221. </div>
  222. </div>
  223. <div class="row">
  224. <div clas="col-12">
  225. <h2 class="h2">Hosting Environment Check</h2>
  226. <?php
  227. $check = do_software_environment_check();
  228. if ($authenticated) {
  229. if (!empty($check['info'])) {
  230. echo "<h3>Information</h3><ul>";
  231. foreach ($check['info'] as $msg) {
  232. echo "<li>{$tick} {$msg}</li>";
  233. }
  234. echo "</ul>";
  235. }
  236. if (!empty($check['warn'])) {
  237. echo "<h3>Warnings</h3><ul>";
  238. foreach ($check['warn'] as $msg) {
  239. echo "<li class='text-warning'>⚠ {$msg}</li>";
  240. }
  241. echo "</ul>";
  242. }
  243. if (!empty($check['error'])) {
  244. echo "<h3>Errors (MUST be fixed)</h3><ul>";
  245. foreach ($check['error'] as $msg) {
  246. echo "<li class='text-danger'>⛔{$msg}</li>";
  247. }
  248. echo "</ul>";
  249. }
  250. $php_error_log = ini_get('error_log');
  251. } else {
  252. if (!empty($check['error'])) {
  253. echo '<h3 class="text-danger">Hosting Environment errors found. Login to see details.</h3>';
  254. }
  255. if (!empty($check['warn'])) {
  256. echo '<h3 class="text-warning">Hosting Environment warnings found. Login to see details.</h3>';
  257. }
  258. }
  259. ?>
  260. </div>
  261. </div>
  262. <div class="row">
  263. <div class="col-12">
  264. <h2 class="h2">Database Update</h2>
  265. <?php
  266. $db = false;
  267. try {
  268. $db = db_connect();
  269. } catch (\Exception $e) {
  270. echo "<p class='h3 text-danger'>Something went wrong while trying to connect to the database. A message should be logged - check PHP's error_log (" . ini_get('error_log') . ')</p>\n';
  271. error_log("Couldn't perform PostfixAdmin database update - failed to connect to db? " . $e->getMessage() . " Trace: " . $e->getTraceAsString());
  272. }
  273. if ($db) {
  274. echo "<p>Everything seems fine... attempting to create/update database structure</p>\n";
  275. try {
  276. require_once(dirname(__FILE__) . '/upgrade.php');
  277. } catch (\Exception $e) {
  278. if ($authenticated) {
  279. echo "<p class='h3 text-danger'>Exception message: {$e->getMessage()} - check logs!</p>";
  280. }
  281. echo "<p class='h3 text-danger'>Something went wrong while trying to apply database updates, a message should be logged - check PHP's error_log (" . ini_get('error_log') . ')</p>\n';
  282. error_log("Couldn't perform PostfixAdmin database update via upgrade.php - " . $e->getMessage() . " Trace: " . $e->getTraceAsString());
  283. }
  284. } else {
  285. echo "<h3 class='h3 text-danger'>Could not connect to database to perform updates; check PHP error log.</h3>";
  286. }
  287. ?>
  288. </div>
  289. </div>
  290. <?php
  291. if ($authenticated) {
  292. $setupMessage = '';
  293. if (safepost("submit") === "createadmin") {
  294. echo "<div class=row><div class='col-12'>";
  295. # "create admin" form submitted, make sure the correct setup password was specified.
  296. // XXX need to ensure domains table includes an 'ALL' entry.
  297. $table_domain = table_by_key('domain');
  298. $rows = db_query_all("SELECT * FROM $table_domain WHERE domain = 'ALL'");
  299. if (empty($rows)) {
  300. // all other fields should default through the schema.
  301. db_insert('domain', array('domain' => 'ALL', 'description' => '', 'transport' => ''));
  302. }
  303. $values = array(
  304. 'username' => safepost('username'),
  305. 'password' => safepost('password'),
  306. 'password2' => safepost('password2'),
  307. 'superadmin' => 1,
  308. 'domains' => array(),
  309. 'active' => 1,
  310. );
  311. list($error, $setupMessage, $errors) = create_admin($values);
  312. if ($error == 1) {
  313. $tUsername = htmlentities($values['username']);
  314. error_log("failed to add admin - " . json_encode([$error, $setupMessage, $errors]));
  315. echo "<p class='text-danger'>Admin addition failed; check field error messages or server logs.</p>";
  316. } else {
  317. // all good!.
  318. $setupMessage .= "<p>You are done with your basic setup. <b>You can now <a href='login.php'>login to PostfixAdmin</a> using the account you just created.</b></p>";
  319. }
  320. echo "</div>";
  321. }
  322. $table_admin = table_by_key('admin');
  323. $bool = db_get_boolean(true);
  324. $admins = db_query_all("SELECT * FROM $table_admin WHERE superadmin = '$bool' AND active = '$bool'");
  325. if (!empty($admins)) { ?>
  326. <div class="row">
  327. <div class="col-12">
  328. <h2 class="h2">Super admins</h2>
  329. <p>The following 'super-admin' accounts have already been added to the database.</p>
  330. <ul>
  331. <?php
  332. foreach ($admins as $row) {
  333. echo "<li>{$row['username']}</li>";
  334. }
  335. ?>
  336. </ul>
  337. </div>
  338. </div>
  339. <?php } ?>
  340. <div class="row">
  341. <div class="col-12">
  342. <h2>Add Superadmin Account</h2>
  343. <form name="create_admin" class="form-horizontal" method="post">
  344. <div class="form-group">
  345. <label for="setup_password" class="col-sm-4 control-label">Setup password</label>
  346. <div class="col-sm-4">
  347. <input class="form-control" type="password" required="required"
  348. name="setup_password"
  349. minlength=5
  350. value=""/>
  351. </div>
  352. </div>
  353. <div class="form-group">
  354. <label for="username" class="col-sm-4 control-label"><?= $PALANG['admin'] ?></label>
  355. <div class="col-sm-4">
  356. <input class="form-control" type="text" required="required" name="username"
  357. minlength=5
  358. id="username"
  359. value=""/>
  360. <?= _error_field($errors, 'username'); ?>
  361. </div>
  362. </div>
  363. <div class="form-group">
  364. <label for="password" class="col-sm-4 control-label"><?= $PALANG['password'] ?></label>
  365. <div class="col-sm-4">
  366. <input class="form-control" type="password" required=required
  367. name="password" minlength=5
  368. id="password" autocomplete="new-password"
  369. value=""/>
  370. <?= _error_field($errors, 'password'); ?>
  371. </div>
  372. </div>
  373. <div class="form-group">
  374. <label for="password2"
  375. class="col-sm-4 control-label"><?= $PALANG['password_again'] ?></label>
  376. <div class="col-sm-4">
  377. <input class="form-control" type="password" required=required
  378. name="password2" minlength=5
  379. id="password2" autocomplete="new-password"
  380. value=""/>
  381. <?= _error_field($errors, 'password2'); ?>
  382. </div>
  383. </div>
  384. <div class="form-group">
  385. <div class="col-sm-offset-4 col-sm-4">
  386. <button class="btn btn-primary" type="submit" name="submit"
  387. value="createadmin"><?= $PALANG['pAdminCreate_admin_button'] ?>
  388. </button>
  389. </div>
  390. </div>
  391. </form>
  392. </div>
  393. </div>
  394. <div class="row">
  395. <div class="col-12">
  396. <p class="text-success"><?= $setupMessage ?></p>
  397. </div>
  398. </div>
  399. <?php
  400. }
  401. ?>
  402. </div>
  403. <footer class="footer mt-5 bg-dark">
  404. <div class="container text-center">
  405. <a target="_blank" rel="noopener" href="https://github.com/postfixadmin/postfixadmin/blob/master/DOCUMENTS/">Documentation</a>
  406. //
  407. <a target="_blank" rel="noopener"
  408. href="https://github.com/postfixadmin/postfixadmin/">Postfix Admin</a>
  409. </div>
  410. </footer>
  411. </body>
  412. </html>
  413. <?php
  414. function _error_field($errors, $key) {
  415. if (!isset($errors[$key])) {
  416. return '';
  417. }
  418. return "<span style='color: #ff0000'>{$errors[$key]}</span>";
  419. }
  420. function create_admin($values) {
  421. define('POSTFIXADMIN_SETUP', 1); # avoids instant redirect to login.php after creating the admin
  422. $handler = new AdminHandler(1, 'setup.php');
  423. $formconf = $handler->webformConfig();
  424. if (!$handler->init($values['username'])) {
  425. return array(1, "", $handler->errormsg);
  426. }
  427. if (!$handler->set($values)) {
  428. return array(1, "", $handler->errormsg);
  429. }
  430. if (!$handler->save()) {
  431. return array(1, "", $handler->errormsg);
  432. }
  433. return array(
  434. 0,
  435. $handler->infomsg['success'],
  436. array(),
  437. );
  438. }
  439. /**
  440. * @return array like: ['info' => string[], 'warn' => string[], 'error' => string[] ]
  441. */
  442. function do_software_environment_check() {
  443. $CONF = Config::getInstance()->getAll();
  444. $warn = [];
  445. $error = [];
  446. $info = [];
  447. //
  448. // Check for availability functions
  449. //
  450. $f_phpversion = function_exists("phpversion");
  451. $f_apache_get_version = function_exists("apache_get_version");
  452. $m_pdo = extension_loaded("PDO");
  453. $m_pdo_mysql = extension_loaded("pdo_mysql");
  454. $m_pdo_pgsql = extension_loaded('pdo_pgsql');
  455. $m_pdo_sqlite = extension_loaded("pdo_sqlite");
  456. $f_session_start = function_exists("session_start");
  457. $f_preg_match = function_exists("preg_match");
  458. $f_mb_encode_mimeheader = function_exists("mb_encode_mimeheader");
  459. $f_imap_open = function_exists("imap_open");
  460. $file_local_config = realpath(__DIR__ . "/../config.local.php");
  461. // Fall back to looking in /etc/postfixadmin for config.local.php (Debian etc)
  462. // this check might produce a false positive if someone has a legacy PostfixAdmin installation.
  463. if (!file_exists($file_local_config) && is_dir('/etc/postfixadmin')) {
  464. if (file_exists('/etc/postfixadmin/config.local.php')) {
  465. $file_local_config = '/etc/postfixadmin/config.local.php';
  466. }
  467. }
  468. // Check for PHP version
  469. $phpversion = 'unknown-version';
  470. if ($f_phpversion) {
  471. if (version_compare(PHP_VERSION, '7.0.0', '<')) {
  472. $error[] = "Error: Depends on: PHP v7.0+. You must upgrade.";
  473. } else {
  474. $info[] = "PHP version - " . phpversion();
  475. }
  476. } else {
  477. $error[] = "Unable to check for PHP version. (PHP_VERSION not found?)";
  478. }
  479. // Check for Apache version
  480. if ($f_apache_get_version) {
  481. $info[] = "Webserver - " . apache_get_version();
  482. }
  483. $info[] = "Postfixadmin installed at - " . realpath(__DIR__);
  484. $error_log_file = ini_get('error_log');
  485. if (file_exists($error_log_file) && is_writable($error_log_file)) {
  486. $info[] = "PHP Error log (error_log) is - $error_log_file";
  487. }
  488. if (file_exists($error_log_file) && !is_writeable($error_log_file)) {
  489. $warn[] = "PHP Error log (error_log) is - $error_log_file, but is not writeable. Postfixadmin will be unable to log error(s)";
  490. }
  491. if (file_exists($file_local_config)) {
  492. $info[] = "config.local.php file found : " . realpath($file_local_config);
  493. } else {
  494. $warn[] = "Warning: config.local.php - NOT FOUND - It's Recommended to store your own settings in config.local.php instead of editing config.inc.php";
  495. }
  496. // Check if there is support for at least 1 database
  497. if (!$m_pdo and !$m_pdo_mysql and !$m_pdo_sqlite and !$m_pdo_pgsql) {
  498. $error[] = "There is no database (PDO) support in your PHP setup, you MUST install a suitable PHP PDO extension (e.g. pdo_pgsql, pdo_mysql or pdo_sqlite).";
  499. }
  500. if ($m_pdo_mysql) {
  501. $info[] = "Database - MySQL support available";
  502. } else {
  503. $info[] = "Database - MySQL (pdo_mysql) extension not found";
  504. }
  505. // PostgreSQL functions
  506. if ($m_pdo_pgsql) {
  507. $info[] = "Database - PostgreSQL support available ";
  508. } else {
  509. $warn[] = "Database - PostgreSQL (pdo_pgsql) extension not found";
  510. }
  511. if ($m_pdo_sqlite) {
  512. $info[] = "Database - SQLite support available";
  513. } else {
  514. $warn[] = "Database support - SQLite (pdo_sqlite) extension not found";
  515. }
  516. if (empty($CONF['encrypt'])) {
  517. $error[] = 'Password hashing - $CONF["encrypt"] is empty. Please check your config.inc.php / config.local.php file.';
  518. } else {
  519. $info[] = 'Password hashing - $CONF["encrypt"] = ' . $CONF['encrypt'];
  520. try {
  521. $output = pacrypt('foobar');
  522. if ($output == 'foobar') {
  523. $warn[] = "You appear to be using a cleartext \$CONF['encrypt'] setting. This is insecure. You have been warned. Your users deserve better";
  524. }
  525. $info[] = 'Password hashing - $CONF["encrypt"] - hash generation OK';
  526. } catch (\Exception $e) {
  527. $error[] = "Password Hashing - attempted to use configured encrypt backend ({$CONF['encrypt']}) triggered an error: " . $e->getMessage();
  528. if (is_writeable($error_log_file)) {
  529. $err = "Possibly helpful error_log messages - " . htmlspecialchars(
  530. implode("",
  531. array_slice(file($error_log_file), -4, 3) // last three lines, might fail miserably if error_log is large.
  532. )
  533. );
  534. $error[] = nl2br($err);
  535. }
  536. $error[] = "You will have problems logging into PostfixAdmin.";
  537. if (preg_match('/^dovecot:/', $CONF['encrypt'])) {
  538. $error[] = "Check out our Dovecot documentation at https://github.com/postfixadmin/postfixadmin/blob/master/DOCUMENTS/DOVECOT.txt, specifically around '3. Permissions'.";
  539. }
  540. }
  541. }
  542. $link = null;
  543. $error_text = null;
  544. $dsn = 'Could not generate';
  545. try {
  546. $dsn = db_connection_string();
  547. $info[] = "Database connection configured OK (using PDO $dsn)";
  548. $link = db_connect();
  549. $info[] = "Database connection - Connected OK";
  550. } catch (Exception $e) {
  551. $error[] = "Database connection string : " . $dsn;
  552. $error[] = "Problem connecting to database, check database configuration (\$CONF['database_*'] entries in config.local.php)";
  553. $error[] = $e->getMessage();
  554. }
  555. // Session functions
  556. if ($f_session_start) {
  557. $info[] = "Depends on: PHP session support - OK";
  558. } else {
  559. $error[] = "Error: Depends on: PHP session support - NOT FOUND. (FreeBSD: portinstall php$phpversion-session ?)";
  560. }
  561. // PCRE functions
  562. if ($f_preg_match) {
  563. $info[] = "Depends on: PHP pcre support - OK";
  564. } else {
  565. $error[] = "Error: Depends on: PHP pcre support - NOT FOUND. (FreeBSD: portinstall php$phpversion-pcre)";
  566. }
  567. // Multibyte functions
  568. if ($f_mb_encode_mimeheader) {
  569. $info[] = "Depends on: PHP mbstring support - OK";
  570. } else {
  571. $error[] = "Error: Depends on: PHP mbstring support - NOT FOUND. (FreeBSD: portinstall php$phpversion-mbstring?)";
  572. }
  573. // Imap functions
  574. if ($f_imap_open) {
  575. $info[] = "Optional - PHP IMAP functions - OK";
  576. } else {
  577. $warn[] = "Warning: Optional dependency 'imap' extension missing, without this you may not be able to automate creation of sub-folders for new mailboxes";
  578. }
  579. return ['error' => $error, 'warn' => $warn, 'info' => $info];
  580. }