From 8f4540d5d9f1c85fbb1c9697ccb9653f8744779d Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 10 Apr 2020 20:57:01 +0200 Subject: [PATCH] [Web] r/o API keys, Pushover integration (can be limited by ACL), other minor changes --- data/web/admin.php | 135 +- data/web/edit.php | 52 + data/web/inc/functions.inc.php | 250 ++- data/web/inc/functions.mailbox.inc.php | 4 + data/web/inc/functions.pushover.inc.php | 175 ++ data/web/inc/init_db.inc.php | 20 +- data/web/inc/prerequisites.inc.php | 1 + data/web/inc/sessions.inc.php | 6 + data/web/inc/triggers.inc.php | 19 +- data/web/js/site/admin.js | 37 +- data/web/json_api.php | 1984 ++++++++++++----------- data/web/lang/lang.de.json | 21 + data/web/lang/lang.en.json | 22 +- data/web/user.php | 55 + 14 files changed, 1670 insertions(+), 1111 deletions(-) create mode 100644 data/web/inc/functions.pushover.inc.php diff --git a/data/web/admin.php b/data/web/admin.php index 82d69b4c..8c5c606f 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -81,8 +81,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC - - + +
@@ -113,53 +113,112 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
- - API + + API -
-
-
- -
- +
+ -
-
- -
-
-
- -
-
- Read-Write - +
+
+ +
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+

+
+ + +
+
+
+
-
-
- + -
-
-

-
- - +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+

+
+ + +
+
+
+
-
diff --git a/data/web/edit.php b/data/web/edit.php index b75a2317..3cc97916 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -564,6 +564,7 @@ if (isset($_SESSION['mailcow_cc_role'])) { $mailbox = html_entity_decode(rawurldecode($_GET["mailbox"])); $result = mailbox('get', 'mailbox_details', $mailbox); $rl = ratelimit('get', 'mailbox', $mailbox); + $pushover_data = pushover('get', $mailbox); $quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox); if (!empty($result)) { ?> @@ -733,6 +734,57 @@ if (isset($_SESSION['mailcow_cc_role'])) {

+
+ +
+
+

+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+

+
+ + + +
+
+
+
+
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 69fbb3bb..74a13391 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1143,7 +1143,7 @@ function verify_tfa_login($username, $token) { } return false; } -function admin_api($action, $data = null) { +function admin_api($access, $action, $data = null) { global $pdo; global $lang; if ($_SESSION['mailcow_cc_role'] != "admin") { @@ -1154,89 +1154,177 @@ function admin_api($action, $data = null) { ); return false; } - switch ($action) { - case "edit": - $regen_key = $data['admin_api_regen_key']; - $active = (isset($data['active'])) ? 1 : 0; - $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0; - $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from'])); - foreach ($allow_from as $key => $val) { - if (empty($val)) { - continue; - } - if (!filter_var($val, FILTER_VALIDATE_IP)) { - $_SESSION['return'][] = array( - 'type' => 'warning', - 'log' => array(__FUNCTION__, $data), - 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key])) - ); - unset($allow_from[$key]); - continue; - } - } - $allow_from = implode(',', array_unique(array_filter($allow_from))); - if (empty($allow_from) && $skip_ip_check == 0) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $data), - 'msg' => 'ip_list_empty' - ); - return false; - } - $api_key = implode('-', array( - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))) - )); - $stmt = $pdo->query("SELECT `api_key` FROM `api`"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if (empty($num_results)) { - $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`) - VALUES (:api_key, :skip_ip_check, :active, :allow_from);"); - $stmt->execute(array( - ':api_key' => $api_key, - ':skip_ip_check' => $skip_ip_check, - ':active' => $active, - ':allow_from' => $allow_from - )); - } - else { - if ($skip_ip_check == 0) { - $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from ;"); - $stmt->execute(array( - ':active' => $active, - ':skip_ip_check' => $skip_ip_check, - ':allow_from' => $allow_from + switch ($access) { + case "rw": + switch ($action) { + case "edit": + $active = (isset($data['active'])) ? 1 : 0; + $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0; + $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from'])); + foreach ($allow_from as $key => $val) { + if (empty($val)) { + continue; + } + if (!filter_var($val, FILTER_VALIDATE_IP)) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $data), + 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key])) + ); + unset($allow_from[$key]); + continue; + } + } + $allow_from = implode(',', array_unique(array_filter($allow_from))); + if (empty($allow_from) && $skip_ip_check == 0) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $data), + 'msg' => 'ip_list_empty' + ); + return false; + } + $api_key = implode('-', array( + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))) )); - } - else { - $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active ;"); - $stmt->execute(array( - ':active' => $active, - ':skip_ip_check' => $skip_ip_check + $stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'rw'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if (empty($num_results)) { + $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`) + VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'rw');"); + $stmt->execute(array( + ':api_key' => $api_key, + ':skip_ip_check' => $skip_ip_check, + ':active' => $active, + ':allow_from' => $allow_from + )); + } + else { + if ($skip_ip_check == 0) { + $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'rw';"); + $stmt->execute(array( + ':active' => $active, + ':skip_ip_check' => $skip_ip_check, + ':allow_from' => $allow_from + )); + } + else { + $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'rw';"); + $stmt->execute(array( + ':active' => $active, + ':skip_ip_check' => $skip_ip_check + )); + } + } + break; + case "regen_key": + $api_key = implode('-', array( + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))) )); - } + $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'rw'"); + $stmt->execute(array( + ':api_key' => $api_key + )); + break; + case "get": + $stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'rw'"); + $apidata = $stmt->fetch(PDO::FETCH_ASSOC); + return $apidata; + break; + } + case "ro": + switch ($action) { + case "edit": + $active = (isset($data['active'])) ? 1 : 0; + $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0; + $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from'])); + foreach ($allow_from as $key => $val) { + if (empty($val)) { + continue; + } + if (!filter_var($val, FILTER_VALIDATE_IP)) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $data), + 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key])) + ); + unset($allow_from[$key]); + continue; + } + } + $allow_from = implode(',', array_unique(array_filter($allow_from))); + if (empty($allow_from) && $skip_ip_check == 0) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $data), + 'msg' => 'ip_list_empty' + ); + return false; + } + $api_key = implode('-', array( + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))) + )); + $stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'ro'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if (empty($num_results)) { + $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`) + VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'ro');"); + $stmt->execute(array( + ':api_key' => $api_key, + ':skip_ip_check' => $skip_ip_check, + ':active' => $active, + ':allow_from' => $allow_from + )); + } + else { + if ($skip_ip_check == 0) { + $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'ro';"); + $stmt->execute(array( + ':active' => $active, + ':skip_ip_check' => $skip_ip_check, + ':allow_from' => $allow_from + )); + } + else { + $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'ro';"); + $stmt->execute(array( + ':active' => $active, + ':skip_ip_check' => $skip_ip_check + )); + } + } + break; + case "regen_key": + $api_key = implode('-', array( + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))), + strtoupper(bin2hex(random_bytes(3))) + )); + $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'ro'"); + $stmt->execute(array( + ':api_key' => $api_key + )); + break; + case "get": + $stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'ro'"); + $apidata = $stmt->fetch(PDO::FETCH_ASSOC); + return $apidata; + break; } - break; - case "regen_key": - $api_key = implode('-', array( - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))), - strtoupper(bin2hex(random_bytes(3))) - )); - $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key"); - $stmt->execute(array( - ':api_key' => $api_key - )); - break; - case "get": - $stmt = $pdo->query("SELECT * FROM `api`"); - $apidata = $stmt->fetch(PDO::FETCH_ASSOC); - return $apidata; break; } $_SESSION['return'][] = array( diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 8238db31..977c1969 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -3343,6 +3343,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM `domain` WHERE `domain` = :domain"); $stmt->execute(array(':domain' => $row['domain'])); $DomainQuota = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt = $pdo->prepare("SELECT IFNULL(COUNT(`active`), 0) AS `pushover_active` FROM `pushover` WHERE `username` = :username AND `active` = 1"); + $stmt->execute(array(':username' => $_data)); + $PushoverActive = $stmt->fetch(PDO::FETCH_ASSOC); $stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain AND `username` != :username"); $stmt->execute(array(':domain' => $row['domain'], ':username' => $_data)); $MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC); @@ -3375,6 +3378,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100); $mailboxdata['messages'] = $row['messages']; $mailboxdata['spam_aliases'] = $SpamaliasUsage['sa_count']; + $mailboxdata['pushover_active'] = ($PushoverActive['pushover_active'] == 1) ? $lang['mailbox']['yes'] : $lang['mailbox']['no']; if ($mailboxdata['percent_in_use'] === '- ') { $mailboxdata['percent_class'] = "info"; } diff --git a/data/web/inc/functions.pushover.inc.php b/data/web/inc/functions.pushover.inc.php new file mode 100644 index 00000000..f0cca30a --- /dev/null +++ b/data/web/inc/functions.pushover.inc.php @@ -0,0 +1,175 @@ + 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'access_denied' + ); + return false; + } + if (!is_array($_data['username'])) { + $usernames = array(); + $usernames[] = $_data['username']; + } + else { + $usernames = $_data['username']; + } + foreach ($usernames as $username) { + if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'access_denied' + ); + continue; + } + $delete = $_data['delete']; + if ($delete == "true") { + $stmt = $pdo->prepare("DELETE FROM `pushover` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data, $_data), + 'msg' => 'pushover_settings_edited' + ); + continue; + } + $key = $_data['key']; + $token = $_data['token']; + if (!ctype_alnum($key) || strlen($key) != 30) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data, $_data), + 'msg' => 'pushover_key' + ); + continue; + } + if (!ctype_alnum($token) || strlen($token) != 30) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data, $_data), + 'msg' => 'pushover_token' + ); + continue; + } + $title = $_data['title']; + $text = $_data['text']; + $active = intval($_data['active']); + $stmt = $pdo->prepare("REPLACE INTO `pushover` (`username`, `key`, `token`, `title`, `text`, `active`) + VALUES (:username, :key, :token, :title, :text, :active)"); + $stmt->execute(array( + ':username' => $username, + ':key' => $key, + ':token' => $token, + ':title' => $title, + ':text' => $text, + ':active' => $active + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data, $_data), + 'msg' => 'pushover_settings_edited' + ); + } + break; + case 'get': + if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'access_denied' + ); + return false; + } + $stmt = $pdo->prepare("SELECT * FROM `pushover` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $_data + )); + $data = $stmt->fetch(PDO::FETCH_ASSOC); + if (empty($data)) { + return false; + } + else { + return $data; + } + break; + case 'test': + if (!isset($_SESSION['acl']['pushover']) || $_SESSION['acl']['pushover'] != "1" ) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'access_denied' + ); + return false; + } + if (!is_array($_data['username'])) { + $usernames = array(); + $usernames[] = $_data['username']; + } + else { + $usernames = $_data['username']; + } + foreach ($usernames as $username) { + if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'access_denied' + ); + continue; + } + $stmt = $pdo->prepare("SELECT * FROM `pushover` + WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username + )); + $api_data = $stmt->fetch(PDO::FETCH_ASSOC); + if (!empty($api_data)) { + $title = (!empty($api_data['title'])) ? $api_data['title'] : 'Mail'; + $text = (!empty($api_data['text'])) ? $api_data['text'] : 'You\'ve got mail 📧'; + curl_setopt_array($ch = curl_init(), array( + CURLOPT_URL => "https://api.pushover.net/1/users/validate.json", + CURLOPT_POSTFIELDS => array( + "token" => $api_data['token'], + "user" => $api_data['key'] + ), + CURLOPT_SAFE_UPLOAD => true, + CURLOPT_RETURNTRANSFER => true, + )); + $result = curl_exec($ch); + $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($httpcode == 200) { + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => sprintf('Pushover API OK (%d): %s', $httpcode, $result) + ); + } + else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => sprintf('Pushover API ERR (%d): %s', $httpcode, $result) + ); + } + } + else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data), + 'msg' => 'pushover_credentials_missing' + ); + return false; + } + } + break; + } +} \ No newline at end of file diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index f122b220..fc50971f 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo; - $db_version = "03042020_0915"; + $db_version = "09042020_1403"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -374,6 +374,7 @@ function init_db_schema() { "syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'", "eas_reset" => "TINYINT(1) NOT NULL DEFAULT '0'", "sogo_profile_reset" => "TINYINT(1) NOT NULL DEFAULT '1'", + "pushover" => "TINYINT(1) NOT NULL DEFAULT '0'", // quarantine is for quarantine actions, todo: rename "quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'", "quarantine_attachments" => "TINYINT(1) NOT NULL DEFAULT '1'", @@ -534,6 +535,7 @@ function init_db_schema() { "sogo_access" => "TINYINT(1) NOT NULL DEFAULT '1'", "app_passwds" => "TINYINT(1) NOT NULL DEFAULT '1'", "bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'", + "pushover" => "TINYINT(1) NOT NULL DEFAULT '0'", "filters" => "TINYINT(1) NOT NULL DEFAULT '1'", "ratelimit" => "TINYINT(1) NOT NULL DEFAULT '1'", "spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'", @@ -830,6 +832,22 @@ function init_db_schema() { ), "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" ), + "pushover" => array( + "cols" => array( + "username" => "VARCHAR(255) NOT NULL", + "key" => "VARCHAR(255) NOT NULL", + "token" => "VARCHAR(255) NOT NULL", + "title" => "TEXT", + "text" => "TEXT", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("username") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), "sogo_user_profile" => array( "cols" => array( "c_uid" => "VARCHAR(255) NOT NULL", diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index b61679a9..0601cf8e 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -230,6 +230,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailq.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.oauth2.inc.php'; +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.pushover.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.ratelimit.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.transports.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rspamd.inc.php'; diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index e3840d2c..683ebf17 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -57,6 +57,12 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) { $_SESSION['mailcow_cc_username'] = 'API'; $_SESSION['mailcow_cc_role'] = 'admin'; $_SESSION['mailcow_cc_api'] = true; + if ($api_return['api_key'] == 'rw') { + $_SESSION['mailcow_cc_api_access'] = 'rw'; + } + else { + $_SESSION['mailcow_cc_api_access'] = 'ro'; + } } else { $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']); diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php index 03f64f91..132e0bd2 100644 --- a/data/web/inc/triggers.inc.php +++ b/data/web/inc/triggers.inc.php @@ -100,17 +100,26 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi if (isset($_POST["reset_main_logo"])) { customize('delete', 'main_logo'); } - // API and license cannot be controlled by API + // Some actions will not be available via API if (isset($_POST["license_validate_now"])) { license('verify'); } if (isset($_POST["admin_api"])) { - admin_api('edit', $_POST); + if (isset($_POST["admin_api"]["ro"])) { + admin_api('ro', 'edit', $_POST); + } + elseif (isset($_POST["admin_api"]["rw"])) { + admin_api('rw', 'edit', $_POST); + } } - if (isset($_POST["admin_api_regen_key"])) { - admin_api('regen_key', $_POST); + if (isset($_POST["admin_api_regen_key"])) { + if (isset($_POST["admin_api_regen_key"]["ro"])) { + admin_api('ro', 'regen_key', $_POST); + } + elseif (isset($_POST["admin_api_regen_key"]["rw"])) { + admin_api('rw', 'regen_key', $_POST); + } } - // Not available via API if (isset($_POST["rspamd_ui"])) { rspamd_ui('edit', $_POST); } diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js index 32f76888..62a918df 100644 --- a/data/web/js/site/admin.js +++ b/data/web/js/site/admin.js @@ -45,6 +45,24 @@ jQuery(function($){ mailcow_alert_box('Regex OK', 'success'); $('button[data-id="' + regex_map_id + '"]').attr({"disabled": false}); } + }); + $('.btn-api-ro').click(function() { + $('#api_rw').hide() + $('#api_ro').show() + $(this).addClass('active') + $('.btn-api-rw, .btn-api-hide').removeClass('active') + }); + $('.btn-api-rw').click(function() { + $('#api_ro').hide() + $('#api_rw').show() + $(this).addClass('active') + $('.btn-api-ro, .btn-api-hide').removeClass('active') + }); + $('.btn-api-hide').click(function() { + $('#api_ro').hide() + $('#api_rw').hide() + $(this).addClass('active') + $('.btn-api-ro, .btn-api-rw').removeClass('active') }); $('.textarea-code').on('keyup', function() { $('.submit_rspamd_regex').attr({"disabled": true}); @@ -360,13 +378,22 @@ jQuery(function($){ draw_transport_maps(); draw_queue(); // API IP check toggle - $("#skip_ip_check").click(function( event ) { - $("#skip_ip_check").not(this).prop('checked', false); - if ($("#skip_ip_check:checked").length > 0) { - $('#allow_from').prop('disabled', true); + $("#skip_ip_check_ro").click(function( event ) { + $("#skip_ip_check_ro").not(this).prop('checked', false); + if ($("#skip_ip_check_ro:checked").length > 0) { + $('#allow_from_ro').prop('disabled', true); } else { - $("#allow_from").removeAttr('disabled'); + $("#allow_from_ro").removeAttr('disabled'); + } + }); + $("#skip_ip_check_rw").click(function( event ) { + $("#skip_ip_check_rw").not(this).prop('checked', false); + if ($("#skip_ip_check_rw:checked").length > 0) { + $('#allow_from_rw').prop('disabled', true); + } + else { + $("#allow_from_rw").removeAttr('disabled'); } }); // Relayhost diff --git a/data/web/json_api.php b/data/web/json_api.php index a04cb491..8490f3e4 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1,17 +1,8 @@ POST data: - { - address: {a, b, c}, (where a, b, c represent alias addresses) - active: 1 (0 or 1) - } - -delete/alias => POST data: - { - address: {a, b, c}, (where a, b, c represent alias addresses) - } - + see /api */ + header('Content-Type: application/json'); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; error_reporting(0); @@ -103,6 +94,14 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u switch ($action) { case "add": + if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username'])) { + http_response_code(403); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'API read/write access denied' + )); + exit(); + } function process_add_return($return) { $generic_failure = json_encode(array( 'type' => 'error', @@ -136,6 +135,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); exit(); } + switch ($category) { case "time_limited_alias": process_add_return(mailbox('add', 'time_limited_alias', $attr)); @@ -235,983 +235,993 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); exit(); } - switch ($category) { - case "rspamd": - switch ($object) { - case "actions": - $curl = curl_init(); - curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock'); - curl_setopt($curl, CURLOPT_URL,"http://rspamd/stat"); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - $data = curl_exec($curl); - if ($data) { - $return = array(); - $stats_array = json_decode($data, true)['actions']; - $stats_array['soft reject'] = $stats_array['soft reject'] + $stats_array['greylist']; - unset($stats_array['greylist']); - foreach ($stats_array as $action => $count) { - $return[] = array($action, $count); - } - echo json_encode($return, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); - } - elseif (!isset($data) || empty($data)) { - echo '{}'; - } - break; - } - break; - - case "domain": - switch ($object) { - case "all": - $domains = mailbox('get', 'domains'); - if (!empty($domains)) { - foreach ($domains as $domain) { - if ($details = mailbox('get', 'domain_details', $domain)) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = mailbox('get', 'domain_details', $object); - process_get_return($data); - break; - } - break; - - case "app-passwd": - switch ($object) { - case "all": - if (empty($extra)) { - $app_passwds = app_passwd('get'); - } - else { - $app_passwds = app_passwd('get', array('username' => $extra)); - } - if (!empty($app_passwds)) { - foreach ($app_passwds as $app_passwd) { - $details = app_passwd('details', array('id' => $app_passwd['id'])); - if ($details !== false) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = app_passwd('details', array('id' => $object['id'])); - process_get_return($data); - break; - } - break; - - case "mailq": - switch ($object) { - case "all": - $mailq = mailq('get'); - if (!empty($mailq)) { - echo $mailq; - } - else { - echo '{}'; - } - break; - } - break; - - case "global_filters": - $global_filters = mailbox('get', 'global_filter_details'); - switch ($object) { - case "all": - if (!empty($global_filters)) { - process_get_return($global_filters); - } - else { - echo '{}'; - } - break; - case "prefilter": - if (!empty($global_filters['prefilter'])) { - process_get_return($global_filters['prefilter']); - } - else { - echo '{}'; - } - break; - case "postfilter": - if (!empty($global_filters['postfilter'])) { - process_get_return($global_filters['postfilter']); - } - else { - echo '{}'; - } - break; - } - break; - - case "rl-domain": - switch ($object) { - case "all": - $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); - if (!empty($domains)) { - foreach ($domains as $domain) { - if ($details = ratelimit('get', 'domain', $domain)) { - $details['domain'] = $domain; - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = ratelimit('get', 'domain', $object); - process_get_return($data); - break; - } - break; - - case "rl-mbox": - switch ($object) { - case "all": - $domains = mailbox('get', 'domains'); - if (!empty($domains)) { - foreach ($domains as $domain) { - $mailboxes = mailbox('get', 'mailboxes', $domain); - if (!empty($mailboxes)) { - foreach ($mailboxes as $mailbox) { - if ($details = ratelimit('get', 'mailbox', $mailbox)) { - $details['mailbox'] = $mailbox; - $data[] = $details; - } - else { - continue; - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = ratelimit('get', 'mailbox', $object); - process_get_return($data); - break; - } - break; - - case "relayhost": - switch ($object) { - case "all": - $relayhosts = relayhost('get'); - if (!empty($relayhosts)) { - foreach ($relayhosts as $relayhost) { - if ($details = relayhost('details', $relayhost['id'])) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = relayhost('details', $object); - process_get_return($data); - break; - } - break; - - case "transport": - switch ($object) { - case "all": - $transports = transport('get'); - if (!empty($transports)) { - foreach ($transports as $transport) { - if ($details = transport('details', $transport['id'])) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = transport('details', $object); - process_get_return($data); - break; - } - break; - - case "rsetting": - switch ($object) { - case "all": - $rsettings = rsettings('get'); - if (!empty($rsettings)) { - foreach ($rsettings as $rsetting) { - if ($details = rsettings('details', $rsetting['id'])) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = rsetting('details', $object); - process_get_return($data); - break; - } - break; - - case "oauth2-client": - switch ($object) { - case "all": - $clients = oauth2('get', 'clients'); - if (!empty($clients)) { - foreach ($clients as $client) { - if ($details = oauth2('details', 'client', $client)) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = oauth2('details', 'client', $object); - process_get_return($data); - break; - } - break; - - case "logs": - switch ($object) { - case "dovecot": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('dovecot-mailcow', $extra); - } - else { - $logs = get_logs('dovecot-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "ratelimited": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('ratelimited', $extra); - } - else { - $logs = get_logs('ratelimited'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "netfilter": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('netfilter-mailcow', $extra); - } - else { - $logs = get_logs('netfilter-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "postfix": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('postfix-mailcow', $extra); - } - else { - $logs = get_logs('postfix-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "autodiscover": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('autodiscover-mailcow', $extra); - } - else { - $logs = get_logs('autodiscover-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "sogo": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('sogo-mailcow', $extra); - } - else { - $logs = get_logs('sogo-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "ui": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('mailcow-ui', $extra); - } - else { - $logs = get_logs('mailcow-ui'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "watchdog": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('watchdog-mailcow', $extra); - } - else { - $logs = get_logs('watchdog-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "acme": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('acme-mailcow', $extra); - } - else { - $logs = get_logs('acme-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "api": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('api-mailcow', $extra); - } - else { - $logs = get_logs('api-mailcow'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - case "rspamd-history": - // 0 is first record, so empty is fine - if (isset($extra)) { - $extra = preg_replace('/[^\d\-]/i', '', $extra); - $logs = get_logs('rspamd-history', $extra); - } - else { - $logs = get_logs('rspamd-history'); - } - echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; - break; - // return no route found if no case is matched - default: - http_response_code(404); - echo json_encode(array( - 'type' => 'error', - 'msg' => 'route not found' - )); - exit(); - } - break; - case "mailbox": - switch ($object) { - case "all": - if (empty($extra)) { - $domains = mailbox('get', 'domains'); - } - else { - $domains = array($extra); - } - if (!empty($domains)) { - foreach ($domains as $domain) { - $mailboxes = mailbox('get', 'mailboxes', $domain); - if (!empty($mailboxes)) { - foreach ($mailboxes as $mailbox) { - if ($details = mailbox('get', 'mailbox_details', $mailbox)) { - $data[] = $details; - } - else { - continue; - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $data = mailbox('get', 'mailbox_details', $object); - process_get_return($data); - break; - } - break; - case "syncjobs": - switch ($object) { - case "all": - $domains = mailbox('get', 'domains'); - if (!empty($domains)) { - foreach ($domains as $domain) { - $mailboxes = mailbox('get', 'mailboxes', $domain); - if (!empty($mailboxes)) { - foreach ($mailboxes as $mailbox) { - $syncjobs = mailbox('get', 'syncjobs', $mailbox); - if (!empty($syncjobs)) { - foreach ($syncjobs as $syncjob) { - if (isset($extra)) { - $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra)); - } - else { - $details = mailbox('get', 'syncjob_details', $syncjob); - } - if ($details) { - $data[] = $details; - } - else { - continue; - } - } - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $syncjobs = mailbox('get', 'syncjobs', $object); - if (!empty($syncjobs)) { - foreach ($syncjobs as $syncjob) { - if (isset($extra)) { - $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra)); - } - else { - $details = mailbox('get', 'syncjob_details', $syncjob); - } - if ($details) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - } - break; - case "active-user-sieve": - if (isset($object)) { - $sieve_filter = mailbox('get', 'active_user_sieve', $object); - if (!empty($sieve_filter)) { - $data[] = $sieve_filter; - } - } - process_get_return($data); - break; - case "filters": - switch ($object) { - case "all": - $domains = mailbox('get', 'domains'); - if (!empty($domains)) { - foreach ($domains as $domain) { - $mailboxes = mailbox('get', 'mailboxes', $domain); - if (!empty($mailboxes)) { - foreach ($mailboxes as $mailbox) { - $filters = mailbox('get', 'filters', $mailbox); - if (!empty($filters)) { - foreach ($filters as $filter) { - if ($details = mailbox('get', 'filter_details', $filter)) { - $data[] = $details; - } - else { - continue; - } - } - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - $filters = mailbox('get', 'filters', $object); - if (!empty($filters)) { - foreach ($filters as $filter) { - if ($details = mailbox('get', 'filter_details', $filter)) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - } - break; - case "bcc": - switch ($object) { - case "all": - $bcc_items = bcc('get'); - if (!empty($bcc_items)) { - foreach ($bcc_items as $bcc_item) { - if ($details = bcc('details', $bcc_item)) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - default: - $data = bcc('details', $object); - if (!empty($data)) { - $data[] = $details; - } - process_get_return($data); - break; - } - break; - case "recipient_map": - switch ($object) { - case "all": - $recipient_map_items = recipient_map('get'); - if (!empty($recipient_map_items)) { - foreach ($recipient_map_items as $recipient_map_item) { - if ($details = recipient_map('details', $recipient_map_item)) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - default: - $data = recipient_map('details', $object); - if (!empty($data)) { - $data[] = $details; - } - process_get_return($data); - break; - } - break; - case "tls-policy-map": - switch ($object) { - case "all": - $tls_policy_maps_items = tls_policy_maps('get'); - if (!empty($tls_policy_maps_items)) { - foreach ($tls_policy_maps_items as $tls_policy_maps_item) { - if ($details = tls_policy_maps('details', $tls_policy_maps_item)) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - default: - $data = tls_policy_maps('details', $object); - if (!empty($data)) { - $data[] = $details; - } - process_get_return($data); - break; - } - break; - case "policy_wl_mailbox": - switch ($object) { - default: - $data = policy('get', 'mailbox', $object)['whitelist']; - process_get_return($data); - break; - } - break; - case "policy_bl_mailbox": - switch ($object) { - default: - $data = policy('get', 'mailbox', $object)['blacklist']; - process_get_return($data); - break; - } - break; - case "policy_wl_domain": - switch ($object) { - default: - $data = policy('get', 'domain', $object)['whitelist']; - process_get_return($data); - break; - } - break; - case "policy_bl_domain": - switch ($object) { - default: - $data = policy('get', 'domain', $object)['blacklist']; - process_get_return($data); - break; - } - break; - case "time_limited_aliases": - switch ($object) { - default: - $data = mailbox('get', 'time_limited_aliases', $object); - process_get_return($data); - break; - } - break; - case "fail2ban": - switch ($object) { - default: - $data = fail2ban('get'); - process_get_return($data); - break; - } - break; - case "resource": - switch ($object) { - case "all": - $domains = mailbox('get', 'domains'); - if (!empty($domains)) { - foreach ($domains as $domain) { - $resources = mailbox('get', 'resources', $domain); - if (!empty($resources)) { - foreach ($resources as $resource) { - if ($details = mailbox('get', 'resource_details', $resource)) { - $data[] = $details; - } - else { - continue; - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - default: - $data = mailbox('get', 'resource_details', $object); - process_get_return($data); - break; - } - break; - case "fwdhost": - switch ($object) { - case "all": - process_get_return(fwdhost('get')); - break; - default: - process_get_return(fwdhost('details', $object)); - break; - } - break; - case "quarantine": - // "all" will not print details - switch ($object) { - case "all": - process_get_return(quarantine('get')); - break; - default: - process_get_return(quarantine('details', $object)); - break; - } - break; - case "alias-domain": - switch ($object) { - case "all": - $alias_domains = mailbox('get', 'alias_domains'); - if (!empty($alias_domains)) { - foreach ($alias_domains as $alias_domain) { - if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) { - $data[] = $details; - } - else { - continue; - } - } - } - process_get_return($data); - break; - default: - process_get_return(mailbox('get', 'alias_domain_details', $object)); - break; - } - break; - case "alias": - switch ($object) { - case "all": - if (empty($extra)) { - $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); - } - else { - $domains = array($extra); - } - if (!empty($domains)) { - foreach ($domains as $domain) { - $aliases = mailbox('get', 'aliases', $domain); - if (!empty($aliases)) { - foreach ($aliases as $alias) { - if ($details = mailbox('get', 'alias_details', $alias)) { - $data[] = $details; - } - else { - continue; - } - } - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - process_get_return(mailbox('get', 'alias_details', $object)); - break; - } - break; - case "domain-admin": - switch ($object) { - case "all": - $domain_admins = domain_admin('get'); - if (!empty($domain_admins)) { - foreach ($domain_admins as $domain_admin) { - if ($details = domain_admin('details', $domain_admin)) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - process_get_return(domain_admin('details', $object)); - break; - } - break; - case "admin": - switch ($object) { - case "all": - $admins = admin('get'); - if (!empty($admins)) { - foreach ($admins as $admin) { - if ($details = admin('details', $admin)) { - $data[] = $details; - } - else { - continue; - } - } - process_get_return($data); - } - else { - echo '{}'; - } - break; - - default: - process_get_return(admin('details', $object)); - break; - } - break; - case "u2f-registration": - header('Content-Type: application/javascript'); - if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) { - list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object)); - $_SESSION['regReq'] = json_encode($req); - $_SESSION['regSigs'] = json_encode($sigs); - echo 'var req = ' . json_encode($req) . ';'; - echo 'var registeredKeys = ' . json_encode($sigs) . ';'; - echo 'var appId = req.appId;'; - echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];'; - } - else { - return; - } - break; - case "u2f-authentication": - header('Content-Type: application/javascript'); - if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) { - $auth_data = $u2f->getAuthenticateData(get_u2f_registrations($object)); - $challenge = $auth_data[0]->challenge; - $appId = $auth_data[0]->appId; - foreach ($auth_data as $each) { - $key = array(); // Empty array - $key['version'] = $each->version; - $key['keyHandle'] = $each->keyHandle; - $registeredKey[] = $key; - } - $_SESSION['authReq'] = json_encode($auth_data); - echo 'var appId = "' . $appId . '";'; - echo 'var challenge = ' . json_encode($challenge) . ';'; - echo 'var registeredKeys = ' . json_encode($registeredKey) . ';'; - } - else { - return; - } - break; - case "dkim": - switch ($object) { - default: - $data = dkim('details', $object); - process_get_return($data); - break; - } - break; - case "presets": - switch ($object) { - case "rspamd": - process_get_return(presets('get', 'rspamd')); - break; - case "sieve": - process_get_return(presets('get', 'sieve')); - break; - } - break; - case "status": - switch ($object) { - case "containers": - $containers = (docker('info')); - foreach ($containers as $container => $container_info) { - $container . ' (' . $container_info['Config']['Image'] . ')'; - $containerstarttime = ($container_info['State']['StartedAt']); - $containerstate = ($container_info['State']['Status']); - $containerimage = ($container_info['Config']['Image']); - $temp[$container] = array( - 'type' => 'info', - 'container' => $container, - 'state' => $containerstate, - 'started_at' => $containerstarttime, - 'image' => $containerimage - ); - } - echo json_encode($temp, JSON_UNESCAPED_SLASHES); - break; - case "vmail": - $exec_fields_vmail = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail'); - $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields_vmail), true)); - $temp = array( - 'type' => 'info', - 'disk' => $vmail_df[0], - 'used' => $vmail_df[2], - 'total'=> $vmail_df[1], - 'used_percent' => $vmail_df[4] - ); - echo json_encode($temp, JSON_UNESCAPED_SLASHES); - break; - case "solr": - $solr_status = solr_status(); - $solr_size = ($solr_status['status']['dovecot-fts']['index']['size']); - $solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']); - if (strtolower(getenv('SKIP_SOLR')) != 'n') { - $solr_enabled = false; + if (!isset($_SESSION['pending_mailcow_cc_username'])) { + switch ($category) { + case "u2f-registration": + header('Content-Type: application/javascript'); + if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) { + list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object)); + $_SESSION['regReq'] = json_encode($req); + $_SESSION['regSigs'] = json_encode($sigs); + echo 'var req = ' . json_encode($req) . ';'; + echo 'var registeredKeys = ' . json_encode($sigs) . ';'; + echo 'var appId = req.appId;'; + echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];'; } else { - $solr_enabled = true; + return; + } + break; + case "u2f-authentication": + header('Content-Type: application/javascript'); + if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) { + $auth_data = $u2f->getAuthenticateData(get_u2f_registrations($object)); + $challenge = $auth_data[0]->challenge; + $appId = $auth_data[0]->appId; + foreach ($auth_data as $each) { + $key = array(); // Empty array + $key['version'] = $each->version; + $key['keyHandle'] = $each->keyHandle; + $registeredKey[] = $key; + } + $_SESSION['authReq'] = json_encode($auth_data); + echo 'var appId = "' . $appId . '";'; + echo 'var challenge = ' . json_encode($challenge) . ';'; + echo 'var registeredKeys = ' . json_encode($registeredKey) . ';'; + } + else { + return; + } + break; + case "rspamd": + switch ($object) { + case "actions": + $curl = curl_init(); + curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock'); + curl_setopt($curl, CURLOPT_URL,"http://rspamd/stat"); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + $data = curl_exec($curl); + if ($data) { + $return = array(); + $stats_array = json_decode($data, true)['actions']; + $stats_array['soft reject'] = $stats_array['soft reject'] + $stats_array['greylist']; + unset($stats_array['greylist']); + foreach ($stats_array as $action => $count) { + $return[] = array($action, $count); + } + echo json_encode($return, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } + elseif (!isset($data) || empty($data)) { + echo '{}'; + } + break; + } + break; + + case "domain": + switch ($object) { + case "all": + $domains = mailbox('get', 'domains'); + if (!empty($domains)) { + foreach ($domains as $domain) { + if ($details = mailbox('get', 'domain_details', $domain)) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = mailbox('get', 'domain_details', $object); + process_get_return($data); + break; + } + break; + + case "app-passwd": + switch ($object) { + case "all": + if (empty($extra)) { + $app_passwds = app_passwd('get'); + } + else { + $app_passwds = app_passwd('get', array('username' => $extra)); + } + if (!empty($app_passwds)) { + foreach ($app_passwds as $app_passwd) { + $details = app_passwd('details', array('id' => $app_passwd['id'])); + if ($details !== false) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = app_passwd('details', array('id' => $object['id'])); + process_get_return($data); + break; + } + break; + + case "mailq": + switch ($object) { + case "all": + $mailq = mailq('get'); + if (!empty($mailq)) { + echo $mailq; + } + else { + echo '{}'; + } + break; + } + break; + + case "global_filters": + $global_filters = mailbox('get', 'global_filter_details'); + switch ($object) { + case "all": + if (!empty($global_filters)) { + process_get_return($global_filters); + } + else { + echo '{}'; + } + break; + case "prefilter": + if (!empty($global_filters['prefilter'])) { + process_get_return($global_filters['prefilter']); + } + else { + echo '{}'; + } + break; + case "postfilter": + if (!empty($global_filters['postfilter'])) { + process_get_return($global_filters['postfilter']); + } + else { + echo '{}'; + } + break; + } + break; + + case "rl-domain": + switch ($object) { + case "all": + $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); + if (!empty($domains)) { + foreach ($domains as $domain) { + if ($details = ratelimit('get', 'domain', $domain)) { + $details['domain'] = $domain; + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = ratelimit('get', 'domain', $object); + process_get_return($data); + break; + } + break; + + case "rl-mbox": + switch ($object) { + case "all": + $domains = mailbox('get', 'domains'); + if (!empty($domains)) { + foreach ($domains as $domain) { + $mailboxes = mailbox('get', 'mailboxes', $domain); + if (!empty($mailboxes)) { + foreach ($mailboxes as $mailbox) { + if ($details = ratelimit('get', 'mailbox', $mailbox)) { + $details['mailbox'] = $mailbox; + $data[] = $details; + } + else { + continue; + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = ratelimit('get', 'mailbox', $object); + process_get_return($data); + break; + } + break; + + case "relayhost": + switch ($object) { + case "all": + $relayhosts = relayhost('get'); + if (!empty($relayhosts)) { + foreach ($relayhosts as $relayhost) { + if ($details = relayhost('details', $relayhost['id'])) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = relayhost('details', $object); + process_get_return($data); + break; + } + break; + + case "transport": + switch ($object) { + case "all": + $transports = transport('get'); + if (!empty($transports)) { + foreach ($transports as $transport) { + if ($details = transport('details', $transport['id'])) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = transport('details', $object); + process_get_return($data); + break; + } + break; + + case "rsetting": + switch ($object) { + case "all": + $rsettings = rsettings('get'); + if (!empty($rsettings)) { + foreach ($rsettings as $rsetting) { + if ($details = rsettings('details', $rsetting['id'])) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = rsetting('details', $object); + process_get_return($data); + break; + } + break; + + case "oauth2-client": + switch ($object) { + case "all": + $clients = oauth2('get', 'clients'); + if (!empty($clients)) { + foreach ($clients as $client) { + if ($details = oauth2('details', 'client', $client)) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = oauth2('details', 'client', $object); + process_get_return($data); + break; + } + break; + + case "logs": + switch ($object) { + case "dovecot": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('dovecot-mailcow', $extra); + } + else { + $logs = get_logs('dovecot-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "ratelimited": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('ratelimited', $extra); + } + else { + $logs = get_logs('ratelimited'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "netfilter": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('netfilter-mailcow', $extra); + } + else { + $logs = get_logs('netfilter-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "postfix": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('postfix-mailcow', $extra); + } + else { + $logs = get_logs('postfix-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "autodiscover": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('autodiscover-mailcow', $extra); + } + else { + $logs = get_logs('autodiscover-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "sogo": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('sogo-mailcow', $extra); + } + else { + $logs = get_logs('sogo-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "ui": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('mailcow-ui', $extra); + } + else { + $logs = get_logs('mailcow-ui'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "watchdog": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('watchdog-mailcow', $extra); + } + else { + $logs = get_logs('watchdog-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "acme": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('acme-mailcow', $extra); + } + else { + $logs = get_logs('acme-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "api": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('api-mailcow', $extra); + } + else { + $logs = get_logs('api-mailcow'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + case "rspamd-history": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('rspamd-history', $extra); + } + else { + $logs = get_logs('rspamd-history'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; + // return no route found if no case is matched + default: + http_response_code(404); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'route not found' + )); + exit(); + } + break; + case "mailbox": + switch ($object) { + case "all": + if (empty($extra)) { + $domains = mailbox('get', 'domains'); + } + else { + $domains = array($extra); + } + if (!empty($domains)) { + foreach ($domains as $domain) { + $mailboxes = mailbox('get', 'mailboxes', $domain); + if (!empty($mailboxes)) { + foreach ($mailboxes as $mailbox) { + if ($details = mailbox('get', 'mailbox_details', $mailbox)) { + $data[] = $details; + } + else { + continue; + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $data = mailbox('get', 'mailbox_details', $object); + process_get_return($data); + break; + } + break; + case "syncjobs": + switch ($object) { + case "all": + $domains = mailbox('get', 'domains'); + if (!empty($domains)) { + foreach ($domains as $domain) { + $mailboxes = mailbox('get', 'mailboxes', $domain); + if (!empty($mailboxes)) { + foreach ($mailboxes as $mailbox) { + $syncjobs = mailbox('get', 'syncjobs', $mailbox); + if (!empty($syncjobs)) { + foreach ($syncjobs as $syncjob) { + if (isset($extra)) { + $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra)); + } + else { + $details = mailbox('get', 'syncjob_details', $syncjob); + } + if ($details) { + $data[] = $details; + } + else { + continue; + } + } + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $syncjobs = mailbox('get', 'syncjobs', $object); + if (!empty($syncjobs)) { + foreach ($syncjobs as $syncjob) { + if (isset($extra)) { + $details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra)); + } + else { + $details = mailbox('get', 'syncjob_details', $syncjob); + } + if ($details) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + } + break; + case "active-user-sieve": + if (isset($object)) { + $sieve_filter = mailbox('get', 'active_user_sieve', $object); + if (!empty($sieve_filter)) { + $data[] = $sieve_filter; + } + } + process_get_return($data); + break; + case "filters": + switch ($object) { + case "all": + $domains = mailbox('get', 'domains'); + if (!empty($domains)) { + foreach ($domains as $domain) { + $mailboxes = mailbox('get', 'mailboxes', $domain); + if (!empty($mailboxes)) { + foreach ($mailboxes as $mailbox) { + $filters = mailbox('get', 'filters', $mailbox); + if (!empty($filters)) { + foreach ($filters as $filter) { + if ($details = mailbox('get', 'filter_details', $filter)) { + $data[] = $details; + } + else { + continue; + } + } + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + $filters = mailbox('get', 'filters', $object); + if (!empty($filters)) { + foreach ($filters as $filter) { + if ($details = mailbox('get', 'filter_details', $filter)) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + } + break; + case "bcc": + switch ($object) { + case "all": + $bcc_items = bcc('get'); + if (!empty($bcc_items)) { + foreach ($bcc_items as $bcc_item) { + if ($details = bcc('details', $bcc_item)) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + default: + $data = bcc('details', $object); + if (!empty($data)) { + $data[] = $details; + } + process_get_return($data); + break; + } + break; + case "recipient_map": + switch ($object) { + case "all": + $recipient_map_items = recipient_map('get'); + if (!empty($recipient_map_items)) { + foreach ($recipient_map_items as $recipient_map_item) { + if ($details = recipient_map('details', $recipient_map_item)) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + default: + $data = recipient_map('details', $object); + if (!empty($data)) { + $data[] = $details; + } + process_get_return($data); + break; + } + break; + case "tls-policy-map": + switch ($object) { + case "all": + $tls_policy_maps_items = tls_policy_maps('get'); + if (!empty($tls_policy_maps_items)) { + foreach ($tls_policy_maps_items as $tls_policy_maps_item) { + if ($details = tls_policy_maps('details', $tls_policy_maps_item)) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + default: + $data = tls_policy_maps('details', $object); + if (!empty($data)) { + $data[] = $details; + } + process_get_return($data); + break; + } + break; + case "policy_wl_mailbox": + switch ($object) { + default: + $data = policy('get', 'mailbox', $object)['whitelist']; + process_get_return($data); + break; + } + break; + case "policy_bl_mailbox": + switch ($object) { + default: + $data = policy('get', 'mailbox', $object)['blacklist']; + process_get_return($data); + break; + } + break; + case "policy_wl_domain": + switch ($object) { + default: + $data = policy('get', 'domain', $object)['whitelist']; + process_get_return($data); + break; + } + break; + case "policy_bl_domain": + switch ($object) { + default: + $data = policy('get', 'domain', $object)['blacklist']; + process_get_return($data); + break; + } + break; + case "time_limited_aliases": + switch ($object) { + default: + $data = mailbox('get', 'time_limited_aliases', $object); + process_get_return($data); + break; + } + break; + case "fail2ban": + switch ($object) { + default: + $data = fail2ban('get'); + process_get_return($data); + break; + } + break; + case "resource": + switch ($object) { + case "all": + $domains = mailbox('get', 'domains'); + if (!empty($domains)) { + foreach ($domains as $domain) { + $resources = mailbox('get', 'resources', $domain); + if (!empty($resources)) { + foreach ($resources as $resource) { + if ($details = mailbox('get', 'resource_details', $resource)) { + $data[] = $details; + } + else { + continue; + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + default: + $data = mailbox('get', 'resource_details', $object); + process_get_return($data); + break; + } + break; + case "fwdhost": + switch ($object) { + case "all": + process_get_return(fwdhost('get')); + break; + default: + process_get_return(fwdhost('details', $object)); + break; + } + break; + case "quarantine": + // "all" will not print details + switch ($object) { + case "all": + process_get_return(quarantine('get')); + break; + default: + process_get_return(quarantine('details', $object)); + break; + } + break; + case "alias-domain": + switch ($object) { + case "all": + $alias_domains = mailbox('get', 'alias_domains'); + if (!empty($alias_domains)) { + foreach ($alias_domains as $alias_domain) { + if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) { + $data[] = $details; + } + else { + continue; + } + } + } + process_get_return($data); + break; + default: + process_get_return(mailbox('get', 'alias_domain_details', $object)); + break; + } + break; + case "alias": + switch ($object) { + case "all": + if (empty($extra)) { + $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); + } + else { + $domains = array($extra); + } + if (!empty($domains)) { + foreach ($domains as $domain) { + $aliases = mailbox('get', 'aliases', $domain); + if (!empty($aliases)) { + foreach ($aliases as $alias) { + if ($details = mailbox('get', 'alias_details', $alias)) { + $data[] = $details; + } + else { + continue; + } + } + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + process_get_return(mailbox('get', 'alias_details', $object)); + break; + } + break; + case "domain-admin": + switch ($object) { + case "all": + $domain_admins = domain_admin('get'); + if (!empty($domain_admins)) { + foreach ($domain_admins as $domain_admin) { + if ($details = domain_admin('details', $domain_admin)) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + process_get_return(domain_admin('details', $object)); + break; + } + break; + case "admin": + switch ($object) { + case "all": + $admins = admin('get'); + if (!empty($admins)) { + foreach ($admins as $admin) { + if ($details = admin('details', $admin)) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + process_get_return(admin('details', $object)); + break; + } + break; + case "dkim": + switch ($object) { + default: + $data = dkim('details', $object); + process_get_return($data); + break; + } + break; + case "presets": + switch ($object) { + case "rspamd": + process_get_return(presets('get', 'rspamd')); + break; + case "sieve": + process_get_return(presets('get', 'sieve')); + break; + } + break; + case "status": + switch ($object) { + case "containers": + $containers = (docker('info')); + foreach ($containers as $container => $container_info) { + $container . ' (' . $container_info['Config']['Image'] . ')'; + $containerstarttime = ($container_info['State']['StartedAt']); + $containerstate = ($container_info['State']['Status']); + $containerimage = ($container_info['Config']['Image']); + $temp[$container] = array( + 'type' => 'info', + 'container' => $container, + 'state' => $containerstate, + 'started_at' => $containerstarttime, + 'image' => $containerimage + ); + } + echo json_encode($temp, JSON_UNESCAPED_SLASHES); + break; + case "vmail": + $exec_fields_vmail = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail'); + $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields_vmail), true)); + $temp = array( + 'type' => 'info', + 'disk' => $vmail_df[0], + 'used' => $vmail_df[2], + 'total'=> $vmail_df[1], + 'used_percent' => $vmail_df[4] + ); + echo json_encode($temp, JSON_UNESCAPED_SLASHES); + break; + case "solr": + $solr_status = solr_status(); + $solr_size = ($solr_status['status']['dovecot-fts']['index']['size']); + $solr_documents = ($solr_status['status']['dovecot-fts']['index']['numDocs']); + if (strtolower(getenv('SKIP_SOLR')) != 'n') { + $solr_enabled = false; + } + else { + $solr_enabled = true; + } + echo json_encode(array( + 'type' => 'info', + 'solr_enabled' => $solr_enabled, + 'solr_size' => $solr_size, + 'solr_documents' => $solr_documents + )); + break; } - echo json_encode(array( - 'type' => 'info', - 'solr_enabled' => $solr_enabled, - 'solr_size' => $solr_size, - 'solr_documents' => $solr_documents - )); break; - } break; - break; - // return no route found if no case is matched - default: - http_response_code(404); - echo json_encode(array( - 'type' => 'error', - 'msg' => 'route not found' - )); - exit(); + // return no route found if no case is matched + default: + http_response_code(404); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'route not found' + )); + exit(); + } } break; case "delete": + if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username'])) { + http_response_code(403); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'API read/write access denied' + )); + exit(); + } function process_delete_return($return) { $generic_failure = json_encode(array( 'type' => 'error', @@ -1338,6 +1348,14 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u } break; case "edit": + if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username'])) { + http_response_code(403); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'API read/write access denied' + )); + exit(); + } function process_edit_return($return) { $generic_failure = json_encode(array( 'type' => 'error', @@ -1376,6 +1394,12 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "bcc": process_edit_return(bcc('edit', array_merge(array('id' => $items), $attr))); break; + case "pushover": + process_edit_return(pushover('edit', array_merge(array('username' => $items), $attr))); + break; + case "pushover-test": + process_edit_return(pushover('test', array_merge(array('username' => $items), $attr))); + break; case "oauth2-client": process_edit_return(oauth2('edit', 'client', array_merge(array('id' => $items), $attr))); break; diff --git a/data/web/lang/lang.de.json b/data/web/lang/lang.de.json index 0f8200ef..38d29e43 100644 --- a/data/web/lang/lang.de.json +++ b/data/web/lang/lang.de.json @@ -24,6 +24,9 @@ "apps": "Apps" }, "danger": { + "pushover_token": "Pushover Token hat das falsche Format", + "pushover_key": "Pushover Key hat das falsche Format", + "pushover_credentials_missing": "Pushover Token und/oder Key fehlen", "invalid_filter_type": "Ungültiger Filtertyp", "file_open_error": "Datei kann nicht zum Schreiben geöffnet werden", "transport_dest_exists": "Transport Maps Ziel \"%s\" existiert bereits", @@ -135,6 +138,7 @@ "extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain" }, "success": { + "pushover_settings_edited": "Pushover Konfiguration gespeichert, bitte den Zugang im Anschluss verifizieren.", "global_filter_written": "Filterdatei wurde erfolreich geschrieben", "learned_ham": "ID %s wurde erfolreich als Ham gelernt", "verified_totp_login": "TOTP Anmeldung verifiziert", @@ -224,8 +228,17 @@ "quota_exceeded_scope": "Domain-Quota erschöpft: Es können nur noch unlimiterte Mailboxen in dieser Domain erstellt werden." }, "user": { + "pushover_info": "Push-Benachrichtungen werden angewendet auf alle nicht-Spam Nachrichten zugestellt an %s, einschließlich Alias-Adressen (shared, non-shared, tagged).", + "verify": "Verifizieren", + "pushover_verify": "Verbindung verifizieren", + "title": "Title", + "pushover_title": "Notification Titel", + "text": "Text", + "pushover_text": "Notification Text ({SUBJECT} entspricht dem Mail-Betreff)", + "last_mail_login": "Letzter Mail-Login", "last_mail_login": "Letzter Mail-Login", "no_last_login": "Keine letzte UI Anmeldung gespeichert", + "save": "Änderungen speichern", "generate": "generieren", "apple_connection_profile": "Apple Verbindungsprofil", "apple_connection_profile_mailonly": "Dieses Verbindungsprofil beinhaltet IMAP und SMTP Konfigurationen für ein Apple Gerät.", @@ -334,6 +347,13 @@ "spam_score_reset": "Auf Server-Standard zurücksetzen" }, "admin": { + "pushover_info": "Push-Benachrichtungen werden angewendet auf alle nicht-Spam Nachrichten zugestellt an %s, einschließlich Alias-Adressen (shared, non-shared, tagged).", + "verify": "Verifizieren", + "pushover_verify": "Verbindung verifizieren", + "title": "Title", + "pushover_title": "Notification Titel", + "text": "Text", + "pushover_text": "Notification Text ({SUBJECT} entspricht dem Mail-Betreff)", "spamfilter": "Spamfilter", "domain_s": "Domain(s)", "rspamd-com_settings": "Ein Name wird automatisch generiert. Beispielinhalte zur Einsicht stehen nachstehend bereit. Siehe auch Rspamd docs", @@ -858,6 +878,7 @@ "relayhost_wrapped_tls_info": "Bitte keine TLS-wrapped Ports verwenden (etwa SMTPS via Port 465/tcp).
\r\nDer Transport wird stattdessen STARTTLS anfordern, um TLS zu verwenden. TLS kann unter \"TLS Policy Maps\" erzwungen werden." }, "acl": { + "pushover": "Pushover", "spam_alias": "Temporäre E-Mail Aliasse", "tls_policy": "Verschlüsselungsrichtlinie", "spam_score": "Spam-Bewertung", diff --git a/data/web/lang/lang.en.json b/data/web/lang/lang.en.json index 49cfa4a5..cc8d8023 100644 --- a/data/web/lang/lang.en.json +++ b/data/web/lang/lang.en.json @@ -30,6 +30,9 @@ "unlimited_quota_acl": "Unlimited quota prohibited by ACL", "mysql_error": "MySQL error: %s", "redis_error": "Redis error: %s", + "pushover_token": "Pushover token has a wrong format", + "pushover_key": "Pushover key has a wrong format", + "pushover_credentials_missing": "Pushover token and or key missing", "unknown_tfa_method": "Unknown TFA method", "totp_verification_failed": "TOTP verification failed", "u2f_verification_failed": "U2F verification failed: %s", @@ -135,6 +138,7 @@ "extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain" }, "success": { + "pushover_settings_edited": "Pushover settings successfully set, please verify credentials.", "global_filter_written": "Filter was successfully written to file", "learned_ham": "Successfully learned ID % as ham", "verified_totp_login": "Verified TOTP login", @@ -221,11 +225,19 @@ "hash_not_found": "Hash not found or already deleted", "fuzzy_learn_error": "Fuzzy hash learn error: %s", "ip_invalid": "Skipped invalid IP: %s", - "quota_exceeded_scope": "Domain quota exceeded: Only unlimited/unrated mailboxes can be created in this domain scope." + "quota_exceeded_scope": "Domain quota exceeded: Only unlimited mailboxes can be created in this domain scope." }, "user": { + "pushover_info": "Push notification settings will apply to all clean (non-spam) mail delivered to %s including aliases (shared, non-shared, tagged).", + "verify": "Verify", + "pushover_verify": "Verify credentials", + "title": "Title", + "pushover_title": "Notification title", + "text": "Text", + "pushover_text": "Notification text ({SUBJECT} will be replaced by mail subject)", "no_last_login": "No last UI login information", "last_mail_login": "Last mail login", + "save": "Save changes", "apple_connection_profile": "Apple connection profile", "apple_connection_profile_mailonly": "This connection profile includes IMAP and SMTP configuration parameters for an Apple device.", "apple_connection_profile_complete": "This connection profile includes IMAP and SMTP parameters as well as CalDAV (calendars) and CardDAV (contacts) pathes for an Apple device.", @@ -334,6 +346,13 @@ "spam_score_reset": "Reset to server default" }, "admin": { + "pushover_info": "Push notification settings will apply to all clean (non-spam) mail delivered to %s including aliases (shared, non-shared, tagged).", + "verify": "Verify", + "pushover_verify": "Verify credentials", + "title": "Title", + "pushover_title": "Notification title", + "text": "Text", + "pushover_text": "Notification text ({SUBJECT} will be replaced by mail subject)", "spamfilter": "Spam filter", "domain": "Domain", "domain_s": "Domain/s", @@ -857,6 +876,7 @@ "relayhost_wrapped_tls_info": "Please do not use TLS-wrapped ports (mostly used on port 465).
\r\nUse any non-wrapped port and issue STARTTLS. A TLS policy to enforce TLS can be created in \"TLS policy maps\"." }, "acl": { + "pushover": "Pushover", "spam_alias": "Temporary aliases", "tls_policy": "TLS policy", "spam_score": "Spam score", diff --git a/data/web/user.php b/data/web/user.php index a312d5f4..f691b8bf 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -76,6 +76,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == ' $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; $username = $_SESSION['mailcow_cc_username']; $mailboxdata = mailbox('get', 'mailbox_details', $username); + $pushover_data = pushover('get', $username); $clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&ui=" . urlencode(strtok($_SERVER['HTTP_HOST'], ':')) . "&port=" . urlencode($autodiscover_config['caldav']['port']); if ($autodiscover_config['useEASforOutlook'] == 'yes') @@ -101,6 +102,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
  • +
  • Pushover API

  • @@ -472,6 +474,59 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
    +
    + + +
    +
    +

    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    +
    + + + +
    +
    +
    + +
    +