public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Rspamd] Fix map permissions for www [Web] Allow to manage global maps in UI (WIP)
b52ee0a7
data/Dockerfiles/rspamd/docker-entrypoint.sh | 8 +-
data/web/admin.php | 44 ++++
data/web/inc/ajax/regex_validation.php | 25 ++
data/web/inc/ajax/show_rspamd_global_filters.php | 3 +
data/web/inc/functions.fail2ban.inc.php | 4 +-
data/web/inc/functions.rsettings.inc.php | 160 ------------
data/web/inc/functions.rspamd.inc.php | 311 +++++++++++++++++++++++
data/web/inc/prerequisites.inc.php | 2 +-
data/web/inc/vars.inc.php | 2 +-
data/web/js/site/admin.js | 28 ++
data/web/js/site/mailbox.js | 5 +-
data/web/json_api.php | 3 +
data/web/lang/lang.de.php | 10 +-
data/web/lang/lang.en.php | 9 +-
14 files changed, 441 insertions(+), 173 deletions(-)
create mode 100644 data/web/inc/ajax/regex_validation.php
create mode 100644 data/web/inc/ajax/show_rspamd_global_filters.php
delete mode 100644 data/web/inc/functions.rsettings.inc.php
create mode 100644 data/web/inc/functions.rspamd.inc.php
Diff
diff --git a/data/Dockerfiles/rspamd/docker-entrypoint.sh b/data/Dockerfiles/rspamd/docker-entrypoint.sh
index ab5f0a10..5c9f8e49 100755
--- a/data/Dockerfiles/rspamd/docker-entrypoint.sh
+++ b/data/Dockerfiles/rspamd/docker-entrypoint.sh
@@ -8,6 +8,8 @@ touch /etc/rspamd/rspamd.conf.local \
chmod 755 /var/lib/rspamd
+addgroup --system --gid 82 www-access
+
[[ ! -f /etc/rspamd/override.d/worker-controller-password.inc ]] && echo '# Autogenerated by mailcow' > /etc/rspamd/override.d/worker-controller-password.inc
DOVECOT_V4=
@@ -49,8 +51,10 @@ touch /etc/rspamd/custom/global_mime_from_blacklist.map \
/etc/rspamd/custom/bad_words_de.map
# www-data (82) group needs to write to these files
-chown -R _rspamd:82 /etc/rspamd/custom
-chmod -R g+w /etc/rspamd/custom
+chown root:root /etc/rspamd/custom/
+chmod 0755 /etc/rspamd/custom/
+chown -R _rspamd:www-access /etc/rspamd/custom/*
+chmod -R 664 /etc/rspamd/custom/*
# Run hooks
for file in /hooks/*; do
diff --git a/data/web/admin.php b/data/web/admin.php
index b0481e5a..4e18822d 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -17,6 +17,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
<li role="presentation"><a href="#tab-routing" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['routing'];?></a></li>
<li role="presentation"><a href="#tab-sys-mails" aria-controls="tab-sys-mails" role="tab" data-toggle="tab"><?=$lang['admin']['sys_mails'];?></a></li>
<li role="presentation"><a href="#tab-mailq" aria-controls="tab-mailq" role="tab" data-toggle="tab"><?=$lang['admin']['queue_manager'];?></a></li>
+ <li role="presentation"><a href="#tab-rspamdmaps" aria-controls="tab-rspamdmaps" role="tab" data-toggle="tab"><?=$lang['admin']['rspamd_global_filters'];?></a></li>
</ul>
<div class="row">
@@ -1135,6 +1136,49 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
</div>
</div>
</div>
+
+ <div role="tabpanel" class="tab-pane" id="tab-rspamdmaps">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <?=$lang['admin']['rspamd_global_filters'];?>
+ </div>
+ <div class="panel-body">
+ <p><?=$lang['admin']['rspamd_global_filters_info'];?></p>
+ <div id="confirm_show_rspamd_global_filters" class="<?=($_SESSION['show_rspamd_global_filters'] === true) ? 'hidden' : '';?>">
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <label>
+ <input type="checkbox" id="show_rspamd_global_filters"> <?=$lang['admin']['rspamd_global_filters_agree'];?>
+ </label>
+ </div>
+ </div>
+ </div>
+ <div id="rspamd_global_filters" class="<?=($_SESSION['show_rspamd_global_filters'] !== true) ? 'hidden' : '';?>">
+ <?php
+ foreach ($RSPAMD_MAPS as $rspamd_map):
+ ?>
+ <hr>
+ <form class="form-horizontal" data-id="<?=$rspamd_map;?>" role="form" method="post">
+ <div class="form-group">
+ <label class="control-label col-sm-3" for="<?=$rspamd_map;?>"><code><?=$rspamd_map;?></code>:</label>
+ <div class="col-sm-9">
+ <textarea id="<?=$rspamd_map;?>" spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control textarea-code" rows="10" name="rspamd_map_data" required><?=file_get_contents('/rspamd_custom_maps/' . $rspamd_map);?></textarea>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="col-sm-offset-3 col-sm-9">
+ <button class="btn btn-xs btn-default validate_rspamd_regex" data-regex-map="<?=$rspamd_map;?>" href="#"><?=$lang['add']['validate'];?></button>
+ <button class="btn btn-xs btn-success submit_rspamd_regex" data-action="edit_selected" data-id="<?=$rspamd_map;?>" data-item="<?=htmlspecialchars($rspamd_map);?>" data-api-url='edit/rspamd-map' data-api-attr='{}' href="#" disabled><?=$lang['edit']['save'];?></button>
+ </div>
+ </div>
+ </form>
+ <?php
+ endforeach;
+ ?>
+ </div>
+ </div>
+ </div>
+ </div>
</div> <!-- /tab-content -->
</div> <!-- /col-md-12 -->
diff --git a/data/web/inc/ajax/regex_validation.php b/data/web/inc/ajax/regex_validation.php
new file mode 100644
index 00000000..6bd8c931
--- /dev/null
+++ b/data/web/inc/ajax/regex_validation.php
@@ -0,0 +1,25 @@
+<?php
+session_start();
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
+header('Content-Type: application/json');
+if (!isset($_SESSION['mailcow_cc_role'])) {
+ exit();
+}
+if (isset($_GET['regex'])) {
+ $regex_lines = preg_split("/(\r\n|\n|\r)/", $_GET['regex']);
+ foreach ($regex_lines as $line => $regex) {
+ if (empty($regex) || substr($regex, 0, 1) == "#") {
+ continue;
+ }
+ if (empty($regex) || substr($regex, 0, 1) != "/") {
+ echo json_encode(array('type' => 'danger', 'msg' => 'Line ' . ($line + 1) . ': Invalid regex'));
+ exit();
+ }
+ if (@preg_match($regex, 'Lorem Ipsum') === false) {
+ echo json_encode(array('type' => 'danger', 'msg' => 'Line ' . ($line + 1) . ': Invalid regex "' . $regex . '"'));
+ exit();
+ }
+ }
+ echo json_encode(array('type' => 'success', 'msg' => $lang['add']['validation_success']));
+}
+?>
diff --git a/data/web/inc/ajax/show_rspamd_global_filters.php b/data/web/inc/ajax/show_rspamd_global_filters.php
new file mode 100644
index 00000000..7c7affe8
--- /dev/null
+++ b/data/web/inc/ajax/show_rspamd_global_filters.php
@@ -0,0 +1,3 @@
+<?php
+session_start();
+$_SESSION['show_rspamd_global_filters'] = true;
\ No newline at end of file
diff --git a/data/web/inc/functions.fail2ban.inc.php b/data/web/inc/functions.fail2ban.inc.php
index c8f46b07..6ec23d34 100644
--- a/data/web/inc/functions.fail2ban.inc.php
+++ b/data/web/inc/functions.fail2ban.inc.php
@@ -32,7 +32,7 @@ function fail2ban($_action, $_data = null) {
$tmp_wl_data[] = $key;
}
if (isset($tmp_wl_data)) {
- sort($tmp_wl_data);
+ natsort($tmp_wl_data);
$f2b_options['whitelist'] = implode(PHP_EOL, $tmp_wl_data);
}
else {
@@ -48,7 +48,7 @@ function fail2ban($_action, $_data = null) {
$tmp_bl_data[] = $key;
}
if (isset($tmp_bl_data)) {
- sort($tmp_bl_data);
+ natsort($tmp_bl_data);
$f2b_options['blacklist'] = implode(PHP_EOL, $tmp_bl_data);
}
else {
diff --git a/data/web/inc/functions.rsettings.inc.php b/data/web/inc/functions.rsettings.inc.php
deleted file mode 100644
index fbf8922b..00000000
--- a/data/web/inc/functions.rsettings.inc.php
+++ /dev/null
@@ -1,160 +0,0 @@
-<?php
-function rsettings($_action, $_data = null) {
- global $pdo;
- global $lang;
- $_data_log = $_data;
- switch ($_action) {
- case 'add':
- if ($_SESSION['mailcow_cc_role'] != "admin") {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'access_denied'
- );
- return false;
- }
- $content = $_data['content'];
- $desc = $_data['desc'];
- $active = intval($_data['active']);
- if (empty($content)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'map_content_empty'
- );
- return false;
- }
- try {
- $stmt = $pdo->prepare("INSERT INTO `settingsmap` (`content`, `desc`, `active`)
- VALUES (:content, :desc, :active)");
- $stmt->execute(array(
- ':content' => $content,
- ':desc' => $desc,
- ':active' => $active
- ));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
- }
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'settings_map_added'
- );
- break;
- case 'edit':
- if ($_SESSION['mailcow_cc_role'] != "admin") {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'access_denied'
- );
- return false;
- }
- $ids = (array)$_data['id'];
- foreach ($ids as $id) {
- $is_now = rsettings('details', $id);
- if (!empty($is_now)) {
- $content = (!empty($_data['content'])) ? $_data['content'] : $is_now['content'];
- $desc = (!empty($_data['desc'])) ? $_data['desc'] : $is_now['desc'];
- $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
- }
- else {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('settings_map_invalid', $id)
- );
- continue;
- }
- $content = trim($content);
- try {
- $stmt = $pdo->prepare("UPDATE `settingsmap` SET
- `content` = :content,
- `desc` = :desc,
- `active` = :active
- WHERE `id` = :id");
- $stmt->execute(array(
- ':content' => $content,
- ':desc' => $desc,
- ':active' => $active,
- ':id' => $id
- ));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- continue;
- }
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('object_modified', htmlspecialchars($ids))
- );
- }
- break;
- case 'delete':
- if ($_SESSION['mailcow_cc_role'] != "admin") {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'access_denied'
- );
- return false;
- }
- $ids = (array)$_data['id'];
- foreach ($ids as $id) {
- try {
- $stmt = $pdo->prepare("DELETE FROM `settingsmap` WHERE `id`= :id");
- $stmt->execute(array(':id' => $id));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
- }
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('settings_map_removed', htmlspecialchars($id))
- );
- }
- break;
- case 'get':
- if ($_SESSION['mailcow_cc_role'] != "admin") {
- return false;
- }
- $settingsmaps = array();
- $stmt = $pdo->query("SELECT `id`, `desc`, `active` FROM `settingsmap`");
- $settingsmaps = $stmt->fetchAll(PDO::FETCH_ASSOC);
- return $settingsmaps;
- break;
- case 'details':
- if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
- return false;
- }
- $settingsmapdata = array();
- $stmt = $pdo->prepare("SELECT `id`,
- `desc`,
- `content`,
- `active` AS `active_int`,
- CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
- FROM `settingsmap`
- WHERE `id` = :id");
- $stmt->execute(array(':id' => $_data));
- $settingsmapdata = $stmt->fetch(PDO::FETCH_ASSOC);
- return $settingsmapdata;
- break;
- }
-}
\ No newline at end of file
diff --git a/data/web/inc/functions.rspamd.inc.php b/data/web/inc/functions.rspamd.inc.php
new file mode 100644
index 00000000..22d405a3
--- /dev/null
+++ b/data/web/inc/functions.rspamd.inc.php
@@ -0,0 +1,311 @@
+<?php
+function rsettings($_action, $_data = null) {
+ global $pdo;
+ global $lang;
+ $_data_log = $_data;
+ switch ($_action) {
+ case 'add':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $content = $_data['content'];
+ $desc = $_data['desc'];
+ $active = intval($_data['active']);
+ if (empty($content)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'map_content_empty'
+ );
+ return false;
+ }
+ try {
+ $stmt = $pdo->prepare("INSERT INTO `settingsmap` (`content`, `desc`, `active`)
+ VALUES (:content, :desc, :active)");
+ $stmt->execute(array(
+ ':content' => $content,
+ ':desc' => $desc,
+ ':active' => $active
+ ));
+ }
+ catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ return false;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'settings_map_added'
+ );
+ break;
+ case 'edit':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $ids = (array)$_data['id'];
+ foreach ($ids as $id) {
+ $is_now = rsettings('details', $id);
+ if (!empty($is_now)) {
+ $content = (!empty($_data['content'])) ? $_data['content'] : $is_now['content'];
+ $desc = (!empty($_data['desc'])) ? $_data['desc'] : $is_now['desc'];
+ $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+ }
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('settings_map_invalid', $id)
+ );
+ continue;
+ }
+ $content = trim($content);
+ try {
+ $stmt = $pdo->prepare("UPDATE `settingsmap` SET
+ `content` = :content,
+ `desc` = :desc,
+ `active` = :active
+ WHERE `id` = :id");
+ $stmt->execute(array(
+ ':content' => $content,
+ ':desc' => $desc,
+ ':active' => $active,
+ ':id' => $id
+ ));
+ }
+ catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ continue;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('object_modified', htmlspecialchars($ids))
+ );
+ }
+ break;
+ case 'delete':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $ids = (array)$_data['id'];
+ foreach ($ids as $id) {
+ try {
+ $stmt = $pdo->prepare("DELETE FROM `settingsmap` WHERE `id`= :id");
+ $stmt->execute(array(':id' => $id));
+ }
+ catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ return false;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('settings_map_removed', htmlspecialchars($id))
+ );
+ }
+ break;
+ case 'get':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ return false;
+ }
+ $settingsmaps = array();
+ $stmt = $pdo->query("SELECT `id`, `desc`, `active` FROM `settingsmap`");
+ $settingsmaps = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ return $settingsmaps;
+ break;
+ case 'details':
+ if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
+ return false;
+ }
+ $settingsmapdata = array();
+ $stmt = $pdo->prepare("SELECT `id`,
+ `desc`,
+ `content`,
+ `active` AS `active_int`,
+ CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+ FROM `settingsmap`
+ WHERE `id` = :id");
+ $stmt->execute(array(':id' => $_data));
+ $settingsmapdata = $stmt->fetch(PDO::FETCH_ASSOC);
+ return $settingsmapdata;
+ break;
+ }
+}
+function rspamd($_action, $_data = null) {
+ global $pdo;
+ global $lang;
+ global $RSPAMD_MAPS;
+ $_data_log = $_data;
+ switch ($_action) {
+ case 'add':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $content = $_data['content'];
+ $desc = $_data['desc'];
+ $active = intval($_data['active']);
+ if (empty($content)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'map_content_empty'
+ );
+ return false;
+ }
+ try {
+ $stmt = $pdo->prepare("INSERT INTO `settingsmap` (`content`, `desc`, `active`)
+ VALUES (:content, :desc, :active)");
+ $stmt->execute(array(
+ ':content' => $content,
+ ':desc' => $desc,
+ ':active' => $active
+ ));
+ }
+ catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ return false;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'settings_map_added'
+ );
+ break;
+ case 'edit':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $maps = (array)$_data['map'];
+ foreach ($maps as $map) {
+ if (!in_array($map, $RSPAMD_MAPS)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('global_map_invalid', $map)
+ );
+ continue;
+ }
+ try {
+ if (file_exists('/rspamd_custom_maps/' . $map)) {
+ $map_content = trim($_data['rspamd_map_data']);
+ $map_handle = fopen('/rspamd_custom_maps/' . $map, 'w');
+ if (!$map_handle) {
+ throw new Exception('File cannot be opened for writing.');
+ }
+ fwrite($map_handle, $map_content . PHP_EOL);
+ fclose($map_handle);
+ }
+ }
+ catch (Exception $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('global_map_write_error', htmlspecialchars($map), htmlspecialchars($e->getMessage()))
+ );
+ continue;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('object_modified', htmlspecialchars($map))
+ );
+ }
+ break;
+ case 'delete':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $ids = (array)$_data['id'];
+ foreach ($ids as $id) {
+ try {
+ $stmt = $pdo->prepare("DELETE FROM `settingsmap` WHERE `id`= :id");
+ $stmt->execute(array(':id' => $id));
+ }
+ catch (PDOException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('mysql_error', $e)
+ );
+ return false;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('settings_map_removed', htmlspecialchars($id))
+ );
+ }
+ break;
+ case 'get':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ return false;
+ }
+ $settingsmaps = array();
+ $stmt = $pdo->query("SELECT `id`, `desc`, `active` FROM `settingsmap`");
+ $settingsmaps = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ return $settingsmaps;
+ break;
+ case 'details':
+ if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
+ return false;
+ }
+ $settingsmapdata = array();
+ $stmt = $pdo->prepare("SELECT `id`,
+ `desc`,
+ `content`,
+ `active` AS `active_int`,
+ CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+ FROM `settingsmap`
+ WHERE `id` = :id");
+ $stmt->execute(array(':id' => $_data));
+ $settingsmapdata = $stmt->fetch(PDO::FETCH_ASSOC);
+ return $settingsmapdata;
+ break;
+ }
+}
\ No newline at end of file
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index 7215de28..8d4062eb 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -209,7 +209,7 @@ 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.ratelimit.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.transports.inc.php';
-require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rsettings.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rspamd.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.tls_policy_maps.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fail2ban.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.docker.inc.php';
diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php
index bea6c3cf..6d906df5 100644
--- a/data/web/inc/vars.inc.php
+++ b/data/web/inc/vars.inc.php
@@ -156,7 +156,7 @@ $MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification'] = 'hourly';
$MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format'] = 'maildir:';
// Set visible Rspamd maps in mailcow UI, do not change unless you know what you are doing
-$RSPAM_MAPS = array(
+$RSPAMD_MAPS = array(
'global_mime_from_blacklist.map',
'global_mime_from_whitelist.map',
'global_rcpt_blacklist.map',
diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js
index d909a793..1b500a18 100644
--- a/data/web/js/site/admin.js
+++ b/data/web/js/site/admin.js
@@ -3,6 +3,7 @@ var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456
jQuery(function($){
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
+ function jq(myid) {return "#" + myid.replace( /(:|\.|\[|\]|,|=|@)/g, "\\$1" );}
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
@@ -29,6 +30,33 @@ jQuery(function($){
$("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
$("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
$("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
+ $(".validate_rspamd_regex").click(function( event ) {
+ event.preventDefault();
+ var regex_map_id = $(this).data('regex-map');
+ var regex_data = $(jq(regex_map_id)).val();
+ $.ajax({
+ dataType: 'json',
+ url: "/inc/ajax/regex_validation.php",
+ type: "get",
+ data: { regex: regex_data },
+ complete: function(data) {
+ var response = (data.responseText);
+ response_obj = JSON.parse(response);
+ if (response_obj.type == "success") {
+ $('button[data-id="' + regex_map_id + '"]').attr({"disabled": false});
+ }
+ mailcow_alert_box(response_obj.msg, response_obj.type);
+ },
+ });
+ });
+ $('.textarea-code').on('keyup', function() {
+ $('.submit_rspamd_regex').attr({"disabled": true});
+ });
+ $("#show_rspamd_global_filters").click(function() {
+ $.get("inc/ajax/show_rspamd_global_filters.php");
+ $("#confirm_show_rspamd_global_filters").hide();
+ $("#rspamd_global_filters").removeClass("hidden");
+ });
$("#super_delete").click(function() { return confirm(lang.queue_ays); });
$(".refresh_table").on('click', function(e) {
e.preventDefault();
diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js
index f808b2bf..165399a5 100644
--- a/data/web/js/site/mailbox.js
+++ b/data/web/js/site/mailbox.js
@@ -133,16 +133,15 @@ $(document).ready(function() {
$(e.currentTarget).find('#sieveDataText').html('<pre style="font-size:14px;line-height:1.1">' + sieveScript + '</pre>');
});
// Disable submit button on script change
- $('#script_data').on('keyup', function() {
+ $('.textarea-code').on('keyup', function() {
$('#add_filter_btns > #add_sieve_script').attr({"disabled": true});
- $('#validation_msg').html('-');
});
// Validate script data
$("#validate_sieve").click(function( event ) {
event.preventDefault();
var script = $('#script_data').val();
$.ajax({
- dataType: 'jsonp',
+ dataType: 'json',
url: "/inc/ajax/sieve_validation.php",
type: "get",
data: { script: script },
diff --git a/data/web/json_api.php b/data/web/json_api.php
index d59cfe56..66e9f22e 100644
--- a/data/web/json_api.php
+++ b/data/web/json_api.php
@@ -1245,6 +1245,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "alias":
process_edit_return(mailbox('edit', 'alias', array_merge(array('id' => $items), $attr)));
break;
+ case "rspamd-map":
+ process_edit_return(rspamd('edit', array_merge(array('map' => $items), $attr)));
+ break;
case "app_links":
process_edit_return(customize('edit', 'app_links', $attr));
break;
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index 3451a322..60fe487c 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -71,6 +71,8 @@ $lang['danger']['private_key_error'] = "Schlüsselfehler: %s";
$lang['danger']['map_content_empty'] = "Inhalt darf nicht leer sein";
$lang['success']['settings_map_added'] = "Regel wurde gespeichert";
$lang['danger']['settings_map_invalid'] = "Regel ID %s ist ungültig";
+$lang['danger']['global_map_invalid'] = "Rspamd Map %s ist ungültig";
+$lang['danger']['global_map_write_error'] = "Kann globale Map ID %s nicht schreiben: %s";
$lang['success']['settings_map_removed'] = "Regeln wurden entfernt: %s";
$lang['danger']['invalid_host'] = "Ungültiger Host: %s";
$lang['danger']['relayhost_invalid'] = "Mapeintrag %s ist ungültig";
@@ -915,5 +917,9 @@ $lang['mailbox']['alias_domain_backupmx'] = 'Alias-Domain für Relay-Domain inak
$lang['danger']['extra_acl_invalid'] = 'Externe Absenderadresse "%s" ist ungültig';
$lang['danger']['extra_acl_invalid_domain'] = 'Externe Absenderadresse "%s" verwendet eine ungültige Domain';
-$lang['admin']['rspamd_global_filters'] = 'Globale Rspamd Filter';
-$lang['admin']['rspamd_global_filters'] = 'Globale Rspamd Filter';
+$lang['admin']['rspamd_global_filters_agree'] = "Ich werde vorsichtig sein!";
+$lang['admin']['rspamd_global_filters'] = 'Globale Filter-Maps';
+$lang['admin']['rspamd_global_filters_info'] = 'Globale Filter-Maps steuern globales White- und Blacklisting dieses Servers. Die akzeptierte Form für Einträge sind <b>ausschließlich</b> Regular Expressions.
+ Trotz rudimentärer Überprüfung der Map, kann es zu fehlerhaften Einträgen kommen, die Rspamd im schlechtesten Fall mit unvorhersehbarer Funktionalität bestraft.<br>
+ Das korrekte Format lautet "/pattern/options" (Beispiel: <code>/.+@domain\.tld/i</code>).<br>
+ Der Name der Map beschreibt die jeweilige Funktion.';
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index f108462c..f170ab1e 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -72,6 +72,8 @@ $lang['danger']['private_key_error'] = "Private key error: %s";
$lang['danger']['map_content_empty'] = "Map content cannot be empty";
$lang['success']['settings_map_added'] = "Added settings map entry";
$lang['danger']['settings_map_invalid'] = "Settings map ID %s invalid";
+$lang['danger']['global_map_invalid'] = "Global map ID %s invalid";
+$lang['danger']['global_map_write_error'] = "Could not write global map ID %s: %s";
$lang['success']['settings_map_removed'] = "Removed settings map ID %s";
$lang['danger']['invalid_host'] = "Invalid host specified: %s";
$lang['danger']['relayhost_invalid'] = "Map entry %s is invalid";
@@ -940,5 +942,8 @@ $lang['mailbox']['alias_domain_backupmx'] = 'Alias domain inactive for relay dom
$lang['danger']['extra_acl_invalid'] = 'External sender address "%s" is invalid';
$lang['danger']['extra_acl_invalid_domain'] = 'External sender "%s" uses an invalid domain';
-$lang['admin']['rspamd_global_filters'] = 'Global Rspamd filters';
-$lang['admin']['rspamd_global_filters_info'] = 'Global Rspamd filters';
+$lang['admin']['rspamd_global_filters_agree'] = "I will be careful!";
+$lang['admin']['rspamd_global_filters'] = 'Global filter maps';
+$lang['admin']['rspamd_global_filters_info'] = 'Global filter maps contain different kind of global black and whitelists. Their names explain their purpose. All content must contain valid regular expression in the format of "/pattern/options" (e.g. <code>/.+@domain\.tld/i</code>).<br>
+ Although rudimentary checks are being executed on each line of regex, Rspamds functionality can be broken, if it fails to read the syntax correctly.';
+