[Watchdog] Use stackoverflow.com for DNS check

[Git] Ignore mail_plugins*
[Dovecot] Read mail_plugins from dynamically generated file
[Dovecot] Encrypt FTS
[Dovecot] Add break_imap_seach option to Solr
[Web] Add ability to send quarantine notification mails
[Web] Minor style fixes
[Web] Add new MAILBOX_DEFAULT_ATTRIBUTES (doc updates, anyone? :-( )
[Web] Use rcpt_smtp if rcpt_mime is not set
[Web] Other minor fixes
master
andryyy 2019-01-29 00:20:39 +01:00
parent f493d3a957
commit 07392b7437
No known key found for this signature in database
GPG Key ID: 8EC34FF2794E25EF
35 changed files with 389 additions and 82 deletions

1
.gitignore vendored
View File

@ -23,6 +23,7 @@ data/conf/nginx/*.conf
data/conf/nginx/*.custom
data/conf/nginx/*.bak
data/conf/dovecot/acl_anyone
data/conf/dovecot/mail_plugins*
data/conf/dovecot/extra.conf
data/conf/rspamd/custom/*
data/conf/portainer/

View File

@ -143,7 +143,7 @@ unbound_checks() {
cat /dev/null > /tmp/unbound-mailcow
host_ip=$(get_container_ip unbound-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H google.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H stackoverflow.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
DNSSEC=$(dig com +dnssec | egrep 'flags:.+ad')
if [[ -z ${DNSSEC} ]]; then
echo "DNSSEC failure" 2>> /tmp/unbound-mailcow 1>&2

View File

@ -20,7 +20,7 @@ disable_plaintext_auth = yes
login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
mail_home = /var/vmail/%d/%n
mail_location = maildir:~/
mail_plugins = quota acl zlib listescape mail_crypt mail_crypt_acl mail_log notify fts fts_solr
mail_plugins = </usr/local/etc/dovecot/mail_plugins
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
mail_attachment_dir = /var/attachments
mail_attachment_min_size = 128k
@ -290,12 +290,12 @@ userdb {
skip = found
}
protocol imap {
mail_plugins = </usr/local/etc/dovecot/mail_plugins_imap
imap_metadata = yes
mail_plugins = quota imap_quota imap_acl acl zlib imap_zlib imap_sieve listescape mail_crypt mail_crypt_acl notify mail_log fts fts_solr
}
mail_attribute_dict = file:%h/dovecot-attributes
protocol lmtp {
mail_plugins = quota sieve acl zlib listescape mail_crypt mail_crypt_acl fts fts_solr
mail_plugins = </usr/local/etc/dovecot/mail_plugins_lmtp
auth_socket_path = /usr/local/var/run/dovecot/auth-master
}
protocol sieve {
@ -308,7 +308,10 @@ plugin {
acl = vfile
fts = solr
fts_autoindex = yes
fts_solr = url=http://solr:8983/solr/dovecot/
fts_solr = break-imap-search url=http://solr:8983/solr/dovecot/
fts_index_fs = crypt:set_prefix=fscrypt_index:posix:
fscrypt_index_public_key = </mail_crypt/ecpubkey.pem
fscrypt_index_private_key = </mail_crypt/ecprivkey.pem
quota = dict:Userquota::proxy::sqlquota
quota_rule2 = Trash:storage=+100%%
sieve = /var/vmail/sieve/%u.sieve

View File

@ -7,6 +7,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
$tfa_data = get_tfa();
?>
<div class="container">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a></li>
<li role="presentation"><a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a></li>
@ -15,6 +16,8 @@ $tfa_data = get_tfa();
<li role="presentation"><a href="#tab-mailq" aria-controls="tab-mailq" role="tab" data-toggle="tab"><?=$lang['admin']['queue_manager'];?></a></li>
</ul>
<div class="row">
<div class="col-md-12">
<div class="tab-content" style="padding-top:20px">
<div role="tabpanel" class="tab-pane active" id="tab-access">
<div class="panel panel-danger">
@ -272,8 +275,6 @@ $tfa_data = get_tfa();
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-config">
<div class="row">
<div id="sidebar-admin" class="col-sm-2 hidden-xs">
@ -631,7 +632,28 @@ $tfa_data = get_tfa();
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="release_format"><?=$lang['admin']['quarantine_release_format'];?></label>
<label for="sender"><?=$lang['admin']['quarantine_notification_sender'];?>:</label>
<input type="text" class="form-control" name="sender" value="<?=$q_data['sender'];?>" placeholder="quarantine@localhost">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="subject"><?=$lang['admin']['quarantine_notification_subject'];?>:</label>
<input type="text" class="form-control" name="subject" value="<?=$q_data['subject'];?>" placeholder="Spam Quarantine Notification">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<label for="html"><?=$lang['admin']['quarantine_notification_html'];?></label>
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code" rows="20" name="html"><?=$q_data['html'];?></textarea>
<br>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="release_format"><?=$lang['admin']['quarantine_release_format'];?>:</label>
<select data-width="100%" name="release_format" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
<option <?=($q_data['release_format'] == 'raw') ? 'selected' : null;?> value="raw"><?=$lang['admin']['quarantine_release_format_raw'];?></option>
<option <?=($q_data['release_format'] == 'attachment') ? 'selected' : null;?> value="attachment"><?=$lang['admin']['quarantine_release_format_att'];?></option>
@ -640,7 +662,7 @@ $tfa_data = get_tfa();
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="exclude_domains"><?=$lang['admin']['quarantine_exclude_domains'];?></label><br />
<label for="exclude_domains"><?=$lang['admin']['quarantine_exclude_domains'];?>:</label><br />
<select data-width="100%" name="exclude_domains" class="selectpicker" title="<?=$lang['tfa']['select'];?>" multiple>
<?php
foreach (array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')) as $domain):
@ -663,7 +685,7 @@ $tfa_data = get_tfa();
<div class="panel-heading">Rspamd settings map</div>
<div class="panel-body">
<legend>Active settings map</legend>
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="settings_map" name="settings_map" readonly><?=file_get_contents('http://nginx:8081/settings.php');?></textarea>
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code" rows="20" name="settings_map" readonly><?=file_get_contents('http://nginx:8081/settings.php');?></textarea>
<hr>
<?php $rsettings = rsettings('get'); ?>
<form class="form" data-id="rsettings" role="form" method="post">
@ -970,6 +992,9 @@ $tfa_data = get_tfa();
</div>
</div>
</div> <!-- /tab-content -->
</div> <!-- /col-md-12 -->
</div> <!-- /row -->
</div> <!-- /container -->
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/admin.php';

View File

@ -24,6 +24,11 @@ body.modal-open {
overflow-y:scroll;
padding-right: inherit !important;
}
@media (min-width: 992px) {
.container {
width: 80%;
}
}
.mass-actions-admin {
user-select: none;
padding:10px 0 10px 0;
@ -60,11 +65,6 @@ body.modal-open {
.nav-tabs>li>a {
z-index: 1;
}
#settings_map {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
font-size:9pt;
background:transparent;
}
.table-condensed .input-sm {
width: 100%!important;
}

View File

@ -40,5 +40,4 @@ table.footable>tbody>tr.footable-empty>td {
}
tbody {
font-size:14px;
color:#333;
}

View File

@ -19,8 +19,8 @@
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
src: local('Source Sans Pro Bold Italic'), local('SourceSansPro-BoldIt'), url('../fonts/SourceSansPro-BoldIt.woff2') format('woff2');
font-weight: 300;
src: local('Source Sans Pro Italic'), local('SourceSansPro-Italic'), url('../fonts/SourceSansPro-Italic.woff2') format('woff2');
}
#maxmsgsize { min-width: 80px; }
#slider1 .slider-selection {
@ -42,6 +42,10 @@
.btn {
text-transform: none;
}
.textarea-code {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
background:transparent !important;
}
.navbar-nav {
margin: 0;
}
@ -157,4 +161,4 @@ nav .glyphicon {
}
.full-width-select {
width: 100%!important;
}
}

View File

@ -1 +1 @@
div.numberedtextarea-wrapper{position:relative}div.numberedtextarea-wrapper textarea{display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.numberedtextarea-line-numbers{position:absolute;top:0;left:0;right:0;bottom:0;width:50px;border-right:none;color:rgba(0,0,0,.4);overflow:hidden}div.numberedtextarea-number{padding-right:6px;text-align:right}textarea#script_data{font-family:Monospace}
div.numberedtextarea-wrapper{position:relative}div.numberedtextarea-wrapper textarea{display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.numberedtextarea-line-numbers{position:absolute;top:0;left:0;right:0;bottom:0;width:50px;border-right:none;color:rgba(0,0,0,.4);overflow:hidden}div.numberedtextarea-number{padding-right:6px;text-align:right}

View File

@ -35,3 +35,17 @@ table.footable>tbody>tr.footable-empty>td {
.inputMissingAttr {
border-color: #FF4136;
}
.dot-danger {
height: 10px;
width: 10px;
background-color: #ff4136;
border-radius: 50%;
display: inline-block;
}
.dot-neutral {
height: 10px;
width: 10px;
background-color: #d4d4d4;
border-radius: 50%;
display: inline-block;
}

View File

@ -49,6 +49,20 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</div>
<hr>
<div class="form-group">
<label class="control-label col-sm-2" for="private_"><?=$lang['edit']['private_comment'];?></label>
<div class="col-sm-10">
<input maxlength="160" class="form-control" type="text" name="private_comment" value="<?=htmlspecialchars($result['private_comment']);?>" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="public_comment"><?=$lang['edit']['public_comment'];?></label>
<div class="col-sm-10">
<input maxlength="160" class="form-control" type="text" name="public_comment" value="<?=htmlspecialchars($result['public_comment']);?>" />
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
@ -1165,7 +1179,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="script_data">Script:</label>
<div class="col-sm-10">
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="20" id="script_data" name="script_data" required><?=$result['script_data'];?></textarea>
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control textarea-code" rows="20" id="script_data" name="script_data" required><?=$result['script_data'];?></textarea>
</div>
</div>
<div class="form-group">

Binary file not shown.

View File

@ -74,6 +74,9 @@ if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
}
}
if (isset($_GET['att'])) {
if ($_SESSION['acl']['quarantine_attachments'] == 0) {
exit(json_encode('Forbidden'));
}
$dl_id = intval($_GET['att']);
$dl_filename = $data['attachments'][$dl_id][0];
if (!is_dir($tmpdir . $dl_filename) && file_exists($tmpdir . $dl_filename)) {

View File

@ -611,6 +611,8 @@ function edit_user_account($_data) {
function user_get_alias_details($username) {
global $lang;
global $pdo;
$data['direct_aliases'] = false;
$data['shared_aliases'] = false;
if ($_SESSION['mailcow_cc_role'] == "user") {
$username = $_SESSION['mailcow_cc_username'];
}
@ -618,7 +620,7 @@ function user_get_alias_details($username) {
return false;
}
$data['address'] = $username;
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `shared_aliases` FROM `alias`
$stmt = $pdo->prepare("SELECT `address` AS `shared_aliases`, `public_comment` FROM `alias`
WHERE `goto` REGEXP :username_goto
AND `address` NOT LIKE '@%'
AND `goto` != :username_goto2
@ -630,9 +632,10 @@ function user_get_alias_details($username) {
));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['shared_aliases'] = $row['shared_aliases'];
$data['shared_aliases'][] = $row['shared_aliases'];
}
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(`address` SEPARATOR ', ') AS `direct_aliases` FROM `alias`
$stmt = $pdo->prepare("SELECT `address` AS `direct_aliases`, `public_comment` FROM `alias`
WHERE `goto` = :username_goto
AND `address` NOT LIKE '@%'
AND `address` != :username_address");
@ -640,21 +643,19 @@ function user_get_alias_details($username) {
array(
':username_goto' => $username,
':username_address' => $username
));
));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['direct_aliases'][] = $row['direct_aliases'];
$data['direct_aliases'][$row['direct_aliases']]['public_comment'] = htmlspecialchars($row['public_comment']);
}
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', ') AS `ad_alias` FROM `mailbox`
$stmt = $pdo->prepare("SELECT CONCAT(local_part, '@', alias_domain) AS `ad_alias`, `alias_domain` FROM `mailbox`
LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
WHERE `username` = :username ;");
$stmt->execute(array(':username' => $username));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['direct_aliases'][] = $row['ad_alias'];
$data['direct_aliases'][$row['ad_alias']]['public_comment'] = '↪ ' . $row['alias_domain'];
}
$data['direct_aliases'] = implode(', ', array_filter($data['direct_aliases']));
$data['direct_aliases'] = empty($data['direct_aliases']) ? '&#10008;' : $data['direct_aliases'];
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';");
$stmt->execute(array(':username' => $username));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);

View File

@ -453,6 +453,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$goto_null = intval($_data['goto_null']);
$goto_spam = intval($_data['goto_spam']);
$goto_ham = intval($_data['goto_ham']);
$private_comment = $_data['private_comment'];
$public_comment = $_data['public_comment'];
if (strlen($private_comment) > 160 | strlen($public_comment) > 160){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'comment_too_long'
);
return false;
}
if (empty($addresses[0])) {
$_SESSION['return'][] = array(
'type' => 'danger',
@ -593,10 +603,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `public_comment`, `private_comment`, `goto`, `domain`, `active`)
VALUES (:address, :goto, :domain, :active)");
if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
$stmt->execute(array(
':address' => '@'.$domain,
':public_comment' => $public_comment,
':private_comment' => $private_comment,
':address' => '@'.$domain,
':goto' => $goto,
':domain' => $domain,
@ -606,6 +619,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
else {
$stmt->execute(array(
':address' => $address,
':public_comment' => $public_comment,
':private_comment' => $private_comment,
':goto' => $goto,
':domain' => $domain,
':active' => $active
@ -753,7 +768,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'tls_enforce_in' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in'])),
'tls_enforce_out' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'])),
'sogo_access' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'])),
'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format'])
'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format']),
'quarantine_notification' => strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification'])
)
);
if (!is_valid_domain_name($domain)) {
@ -1148,6 +1164,65 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
}
break;
case 'quarantine_notification':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
if (!isset($_SESSION['acl']['quarantine_notification']) || $_SESSION['acl']['quarantine_notification'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
foreach ($usernames as $username) {
if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$is_now = mailbox('get', 'quarantine_notification', $username);
if (!empty($is_now)) {
$quarantine_notification = (isset($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (!in_array($quarantine_notification, array('never', 'hourly', 'daily', 'weekly'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("UPDATE `mailbox`
SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', :quarantine_notification)
WHERE `username` = :username");
$stmt->execute(array(
':quarantine_notification' => $quarantine_notification,
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('mailbox_modified', $username)
);
}
break;
case 'spam_score':
if (!is_array($_data['username'])) {
$usernames = array();
@ -1587,6 +1662,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$goto_null = (isset($_data['goto_null'])) ? intval($_data['goto_null']) : 0;
$goto_spam = (isset($_data['goto_spam'])) ? intval($_data['goto_spam']) : 0;
$goto_ham = (isset($_data['goto_ham'])) ? intval($_data['goto_ham']) : 0;
$public_comment = (isset($_data['public_comment'])) ? $_data['public_comment'] : $is_now['public_comment'];
$private_comment = (isset($_data['private_comment'])) ? $_data['private_comment'] : $is_now['private_comment'];
$goto = (!empty($_data['goto'])) ? $_data['goto'] : $is_now['goto'];
$address = (!empty($_data['address'])) ? $_data['address'] : $is_now['address'];
}
@ -1703,11 +1780,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!empty($goto)) {
$stmt = $pdo->prepare("UPDATE `alias` SET
`address` = :address,
`public_comment` = :public_comment,
`private_comment` = :private_comment,
`goto` = :goto,
`active`= :active
WHERE `id` = :id");
$stmt->execute(array(
':address' => $address,
':public_comment' => $public_comment,
':private_comment' => $private_comment,
':goto' => $goto,
':active' => $active,
':id' => $id
@ -2367,6 +2448,22 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'tls_enforce_out' => $attrs['tls_enforce_out']
);
break;
case 'quarantine_notification':
$attrs = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
}
else {
$_data = $_SESSION['mailcow_cc_username'];
}
$stmt = $pdo->prepare("SELECT `attributes` FROM `mailbox` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$attrs = $stmt->fetch(PDO::FETCH_ASSOC);
$attrs = json_decode($attrs['attributes'], true);
return $attrs['quarantine_notification'];
break;
case 'filters':
$filters = array();
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
@ -2699,6 +2796,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`domain`,
`goto`,
`address`,
`public_comment`,
`private_comment`,
`active` as `active_int`,
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
`created`,
@ -2722,6 +2821,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
$aliasdata['id'] = $row['id'];
$aliasdata['domain'] = $row['domain'];
$aliasdata['public_comment'] = $row['public_comment'];
$aliasdata['private_comment'] = $row['private_comment'];
$aliasdata['domain'] = $row['domain'];
$aliasdata['goto'] = $row['goto'];
$aliasdata['address'] = $row['address'];
(!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;

View File

@ -81,12 +81,18 @@ function quarantine($_action, $_data = null) {
$release_format = 'raw';
}
$max_size = $_data['max_size'];
$subject = $_data['subject'];
$sender = $_data['sender'];
$html = $_data['html'];
$exclude_domains = (array)$_data['exclude_domains'];
try {
$redis->Set('Q_RETENTION_SIZE', intval($retention_size));
$redis->Set('Q_MAX_SIZE', intval($max_size));
$redis->Set('Q_EXCLUDE_DOMAINS', json_encode($exclude_domains));
$redis->Set('Q_RELEASE_FORMAT', $release_format);
$redis->Set('Q_SENDER', $sender);
$redis->Set('Q_SUBJECT', $subject);
$redis->Set('Q_HTML', $html);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
@ -393,7 +399,7 @@ function quarantine($_action, $_data = null) {
break;
case 'get':
if ($_SESSION['mailcow_cc_role'] == "user") {
$stmt = $pdo->prepare('SELECT `id`, `qid`, `subject`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine` WHERE `rcpt` = :mbox');
$stmt = $pdo->prepare('SELECT `id`, `qid`, `subject`, LOCATE("VIRUS_FOUND", `symbols`) AS `virus_flag`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine` WHERE `rcpt` = :mbox');
$stmt->execute(array(':mbox' => $_SESSION['mailcow_cc_username']));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
@ -401,7 +407,7 @@ function quarantine($_action, $_data = null) {
}
}
elseif ($_SESSION['mailcow_cc_role'] == "admin") {
$stmt = $pdo->query('SELECT `id`, `qid`, `subject`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine`');
$stmt = $pdo->query('SELECT `id`, `qid`, `subject`, LOCATE("VIRUS_FOUND", `symbols`) AS `virus_flag`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine`');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$q_meta[] = $row;
@ -410,7 +416,7 @@ function quarantine($_action, $_data = null) {
else {
$domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
foreach ($domains as $domain) {
$stmt = $pdo->prepare('SELECT `id`, `qid`, `subject`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine` WHERE `rcpt` REGEXP :domain');
$stmt = $pdo->prepare('SELECT `id`, `qid`, `subject`, LOCATE("VIRUS_FOUND", `symbols`) AS `virus_flag`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantine` WHERE `rcpt` REGEXP :domain');
$stmt->execute(array(':domain' => '@' . $domain . '$'));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
@ -428,6 +434,12 @@ function quarantine($_action, $_data = null) {
$settings['max_size'] = $redis->Get('Q_MAX_SIZE');
$settings['retention_size'] = $redis->Get('Q_RETENTION_SIZE');
$settings['release_format'] = $redis->Get('Q_RELEASE_FORMAT');
$settings['subject'] = $redis->Get('Q_SUBJECT');
$settings['sender'] = $redis->Get('Q_SENDER');
$settings['html'] = htmlspecialchars($redis->Get('Q_HTML'));
if (empty($settings['html'])) {
$settings['html'] = htmlspecialchars(file_get_contents("/templates/quarantine.tpl"));
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "17012019_0717";
$db_version = "27012019_1217";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -137,6 +137,8 @@ function init_db_schema() {
"domain" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"private_comment" => "TEXT",
"public_comment" => "TEXT",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
@ -237,6 +239,7 @@ function init_db_schema() {
"rcpt" => "VARCHAR(255)",
"msg" => "LONGTEXT",
"domain" => "VARCHAR(255)",
"notified" => "TINYINT(1) NOT NULL DEFAULT '0'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"user" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
),
@ -316,6 +319,8 @@ function init_db_schema() {
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"sogo_profile_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_attachments" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_notification" => "TINYINT(1) NOT NULL DEFAULT '1'",
),
"keys" => array(
"primary" => array(
@ -991,6 +996,7 @@ DELIMITER ;';
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.force_pw_update', \"0\") WHERE JSON_EXTRACT(`attributes`, '$.force_pw_update') IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.sogo_access', \"1\") WHERE JSON_EXTRACT(`attributes`, '$.sogo_access') IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.mailbox_format', \"maildir:\") WHERE JSON_EXTRACT(`attributes`, '$.mailbox_format') IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', \"never\") WHERE JSON_EXTRACT(`attributes`, '$.quarantine_notification') IS NULL;");
foreach($tls_options as $tls_user => $tls_options) {
$stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.tls_enforce_in', :tls_enforce_in),
`attributes` = JSON_SET(`attributes`, '$.tls_enforce_out', :tls_enforce_out)

View File

@ -131,6 +131,9 @@ $DOCKER_TIMEOUT = 60;
// Anonymize IPs logged via UI
$ANONYMIZE_IPS = true;
// MAILBOX_DEFAULT_ATTRIBUTES define default attributes for new mailboxes
// These settings will not change existing mailboxes
// Force incoming TLS for new mailboxes by default
$MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in'] = false;
@ -143,6 +146,9 @@ $MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'] = false;
// Force password change on next login (only allows login to mailcow UI)
$MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true;
// Send notification when quarantine is not empty (never, hourly, daily, weekly)
$MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification'] = 'never';
// Default mailbox format, should not be changed unless you know exactly, what you do, keep the trailing ":"
// Check dovecot.conf for further changes (e.g. shared namespace)
$MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format'] = 'maildir:';

View File

@ -54,7 +54,7 @@ jQuery(function($){
function draw_domain_admins() {
ft_domainadmins = FooTable.init('#domainadminstable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"sorted": true,"name":"username","title":lang.username,"style":{"width":"250px"}},
{"name":"selected_domains","title":lang.admin_domains,"breakpoints":"xs sm"},
{"name":"tfa_active","title":"TFA", "filterable": false,"style":{"maxWidth":"80px","width":"80px"}},
@ -82,7 +82,7 @@ jQuery(function($){
function draw_admins() {
ft_admins = FooTable.init('#adminstable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"sorted": true,"name":"usr","title":lang.username,"style":{"width":"250px"}},
{"name":"tfa_active","title":"TFA", "filterable": false,"style":{"maxWidth":"80px","width":"80px"}},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
@ -109,7 +109,7 @@ jQuery(function($){
function draw_fwd_hosts() {
ft_forwardinghoststable = FooTable.init('#forwardinghoststable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"host","type":"text","title":lang.host,"style":{"width":"250px"}},
{"name":"source","title":lang.source,"breakpoints":"xs sm"},
{"name":"keep_spam","title":lang.spamfilter, "type": "text","style":{"maxWidth":"80px","width":"80px"}},
@ -134,7 +134,7 @@ jQuery(function($){
function draw_relayhosts() {
ft_relayhoststable = FooTable.init('#relayhoststable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
{"name":"hostname","type":"text","title":lang.host,"style":{"width":"250px"}},
{"name":"username","title":lang.username,"breakpoints":"xs sm"},
@ -161,7 +161,7 @@ jQuery(function($){
function draw_transport_maps() {
ft_relayhoststable = FooTable.init('#transportstable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
{"name":"destination","type":"text","title":lang.destination,"style":{"width":"250px"}},
{"name":"nexthop","type":"text","title":lang.nexthop,"style":{"width":"250px"}},
@ -188,7 +188,7 @@ jQuery(function($){
function draw_queue() {
ft_queuetable = FooTable.init('#queuetable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"queue_id","type":"text","title":"QID","style":{"width":"50px"}},
{"name":"queue_name","type":"text","title":"Queue","style":{"width":"120px"}},
{"name":"arrival_time","sorted": true,"direction": "DESC","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.arrival_time,"style":{"width":"170px"}},

View File

@ -193,7 +193,6 @@ $(document).ready(function() {
}
if ($(this).attr("max")) {
if (Number($(this).val()) > Number($(this).attr("max"))) {
alert($(this).attr("max"))
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
@ -314,4 +313,4 @@ $(document).ready(function() {
$('#ConfirmDeleteModal').modal('hide');
});
});
});
});

View File

@ -381,11 +381,13 @@ jQuery(function($){
function drawChart() {
var data = google.visualization.arrayToDataTable(graphdata);
var body_font_color = $('body').css("color");
var options = {
is3D: true,
sliceVisibilityThreshold: 0,
pieSliceText: 'percentage',
backgroundColor: { fill:'transparent' },
legend: {textStyle: {color: body_font_color}},
chartArea: {
left: 0,
right: 0,
@ -416,7 +418,7 @@ jQuery(function($){
{"name":"unix_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
{"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "rcpt_mime","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "rcpt","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
{"name": "action","title": "Action","style": {"minwidth": 82}},
{"name": "score","title": "Score","style": {"maxWidth": 110},},
@ -460,7 +462,12 @@ jQuery(function($){
function process_table_data(data, table) {
if (table == 'rspamd_history') {
$.each(data, function (i, item) {
item.rcpt_mime = item.rcpt_mime.join(",&#8203;");
if (item.rcpt_mime != "") {
item.rcpt = item.rcpt_mime.join(", ");
}
else {
item.rcpt = item.rcpt_smtp.join(", ");
}
Object.keys(item.symbols).map(function(key) {
var sym = item.symbols[key];
if (sym.score <= 0) {

View File

@ -22,8 +22,6 @@ $(document).ready(function() {
$('#textarea_alias_goto').prop('disabled', true);
}
$("#script_data").numberedtextarea({allowTabChar: true});
$("#mailbox-password-warning-close").click(function( event ) {
$('#mailbox-passwd-hidden-info').addClass('hidden');
$('#mailbox-passwd-form-groups').removeClass('hidden');

View File

@ -141,8 +141,6 @@ $(document).ready(function() {
var sieveScript = $(e.relatedTarget).data('sieve-script');
$(e.currentTarget).find('#sieveDataText').html('<pre style="font-size:14px;line-height:1.1">' + sieveScript + '</pre>');
});
// Set line numbers for textarea
$("#script_data").numberedtextarea({allowTabChar: true});
// Disable submit button on script change
$('#script_data').on('keyup', function() {
$('#add_filter_btns > #add_sieve_script').attr({"disabled": true});
@ -712,6 +710,8 @@ jQuery(function($){
{"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}},
{"name":"goto","title":lang.target_address},
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
{"name":"public_comment","title":lang.public_comment,"breakpoints":"all"},
{"name":"private_comment","title":lang.private_comment,"breakpoints":"all"},
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
@ -731,6 +731,8 @@ jQuery(function($){
'</div>';
item.chkbox = '<input type="checkbox" data-id="alias" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
item.goto = escapeHtml(item.goto.replace(/,/g, " "));
item.public_comment = escapeHtml(item.public_comment);
item.private_comment = escapeHtml(item.private_comment);
if (item.is_catch_all == 1) {
item.address = '<div class="label label-default">Catch-All</div> ' + escapeHtml(item.address);
}

View File

@ -161,7 +161,8 @@ $(document).ready(function() {
$(document).on("keydown", disableF5);
}
});
// Textarea line numbers
$(".textarea-code").numberedtextarea({allowTabChar: true});
// trigger container restart
$('#RestartContainer').on('show.bs.modal', function(e) {
var container = $(e.relatedTarget).data('container');

View File

@ -1,22 +1,23 @@
// Base64 functions
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
jQuery(function($){
acl_data = JSON.parse(acl);
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
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 draw_quarantine_table() {
ft_quarantinetable = FooTable.init('#quarantinetable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"id","type":"ID","filterable": false,"sorted": true,"direction":"DESC","title":"ID","style":{"width":"50px"}},
{"name":"qid","type":"text","title":lang.qid,"style":{"width":"125px"}},
{"name":"sender","style":{"word-break":"break-all"},"title":lang.sender,"breakpoints":"xs sm"},
{"name":"rcpt","title":lang.rcpt, "type": "text"},
{"name":"subject","title":"Subject", "type": "text"},
{"name":"qid","breakpoints":"all","type":"text","title":lang.qid,"style":{"width":"125px"}},
{"name":"sender","title":lang.sender},
{"name":"rcpt","title":lang.rcpt, "breakpoints":"xs sm md", "type": "text"},
{"name":"virus","title":lang.danger, "type": "text"},
{"name":"subject","title":lang.subj, "type": "text"},
{"name":"created","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.received,"style":{"width":"170px"}},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
],
"rows": $.ajax({
dataType: 'json',
@ -28,15 +29,26 @@ jQuery(function($){
success: function (data) {
$.each(data, function (i, item) {
if (item.subject === null) {
item.subject = '<i>no preview</i>';
}
else {
item.subject = '';
} else {
item.subject = escapeHtml(item.subject);
}
if (item.virus_flag > 0) {
item.virus = '<span class="dot-danger"></span>';
} else {
item.virus = '<span class="dot-neutral"></span>';
}
if (acl_data.login_as === 1) {
item.action = '<div class="btn-group">' +
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><span class="glyphicon glyphicon-modal-window"></span> ' + lang.show_item + '</a>' +
'<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
}
else {
item.action = '<div class="btn-group">' +
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><span class="glyphicon glyphicon-modal-window"></span> ' + lang.show_item + '</a>' +
'</div>';
}
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
});
}

View File

@ -1152,6 +1152,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "tls_policy":
process_edit_return(mailbox('edit', 'tls_policy', array_merge(array('username' => $items), $attr)));
break;
case "quarantine_notification":
process_edit_return(mailbox('edit', 'quarantine_notification', array_merge(array('username' => $items), $attr)));
break;
case "qitem":
process_edit_return(quarantine('edit', array_merge(array('id' => $items), $attr)));
break;

View File

@ -652,8 +652,8 @@ $lang['admin']['no_active_bans'] = "Žádná aktivní blokování";
$lang['admin']['quarantine'] = "Karanténa";
$lang['admin']['quarantine_retention_size'] = "Počet zadržených zpráv na poštovní schránku<br />0 znamená <b>neaktivní</b>!";
$lang['admin']['quarantine_max_size'] = "Maximální velikost v MiB (větší prvky budou smazány)<br />0 <b>neznamená</b> neomezeno!";
$lang['admin']['quarantine_exclude_domains'] = "Vyloučené domény a doménové aliasy:";
$lang['admin']['quarantine_release_format'] = "Formát propuštěných položek:";
$lang['admin']['quarantine_exclude_domains'] = "Vyloučené domény a doménové aliasy";
$lang['admin']['quarantine_release_format'] = "Formát propuštěných položek";
$lang['admin']['quarantine_release_format_raw'] = "Nezměněný originál";
$lang['admin']['quarantine_release_format_att'] = "Jako příloha";

View File

@ -378,6 +378,14 @@ $lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s u. Alias-Dom.
$lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen';
$lang['edit']['kind'] = 'Art';
$lang['edit']['resource'] = 'Ressource';
$lang['edit']['public_comment'] = 'Öffentlicher Kommentar';
$lang['mailbox']['public_comment'] = 'Öffentlicher Kommentar';
$lang['edit']['private_comment'] = 'Privater Kommentar';
$lang['mailbox']['private_comment'] = 'Privater Kommentar';
$lang['edit']['comment_info'] = 'Ein privater Kommentar ist für den Benutzer nicht einsehbar. Ein öffentlicher Kommentar wird als Tooltip im Interface des Benutzers angezeigt.';
$lang['add']['public_comment'] = 'Öffentlicher Kommentar';
$lang['add']['private_comment'] = 'Privater Kommentar';
$lang['add']['comment_info'] = 'Ein privater Kommentar ist für den Benutzer nicht einsehbar. Ein öffentlicher Kommentar wird als Tooltip im Interface des Benutzers angezeigt.';
$lang['acl']['spam_alias'] = 'Temporäre E-Mail Aliasse';
$lang['acl']['tls_policy'] = 'Verschlüsselungsrichtlinie';
@ -630,9 +638,15 @@ $lang['admin']['queue_unban'] = "Unban einreihen";
$lang['admin']['no_active_bans'] = "Keine aktiven Bans";
$lang['admin']['quarantine'] = "Quarantäne";
$lang['admin']['quarantine_retention_size'] = "Rückhaltungen pro Mailbox<br />0 bedeutet <b>inaktiv</b>!";
$lang['admin']['quarantine_max_size'] = "Maximale Größe in MiB (größere Elemente werden verworfen)<br />0 bedeutet <b>nicht</b> unlimitert!";
$lang['admin']['quarantine_exclude_domains'] = "Domains und Alias-Domains ausschließen:";
$lang['admin']['quarantine_retention_size'] = "Rückhaltungen pro Mailbox:<br><small>0 bedeutet <b>inaktiv</b>.</small>";
$lang['admin']['quarantine_max_size'] = "Maximale Größe in MiB (größere Elemente werden verworfen):<br><small>0 bedeutet <b>nicht</b> unlimitert.</small>";
$lang['admin']['quarantine_exclude_domains'] = "Domains und Alias-Domains ausschließen";
$lang['admin']['quarantine_notification_sender'] = "Benachrichtigungs-E-Mail Absender";
$lang['admin']['quarantine_notification_subject'] = "Benachrichtigungs-E-Mail Betreff";
$lang['admin']['quarantine_notification_html'] = "Benachrichtigungs-E-Mail Inhalt:<br><small>Leer lassen, um Standard-Template wiederherzustellen.</small>";
$lang['admin']['quarantine_release_format'] = "Format freigegebener Mails";
$lang['admin']['quarantine_release_format_raw'] = "Unverändertes Original";
$lang['admin']['quarantine_release_format_att'] = "Als Anhang";
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
@ -676,6 +690,7 @@ $lang['edit']['spam_policy'] = "Hinzufügen und Entfernen von Einträgen in Whit
$lang['edit']['spam_alias'] = "Anpassen temporärer Alias-Adressen";
$lang['danger']['img_tmp_missing'] = "Grafik konnte nicht validiert werden: Erstellung temporärer Datei fehlgeschlagen";
$lang['danger']['comment_too_long'] = "Kommentarfeld darf maximal 160 Zeichen enthalten";
$lang['danger']['img_invalid'] = "Grafik konnte nicht validiert werden";
$lang['danger']['invalid_mime_type'] = "Grafik konnte nicht validiert werden: Ungültiger MIME-Type";
$lang['success']['upload_success'] = "Datei wurde erfolgreich hochgeladen";

View File

@ -392,7 +392,14 @@ $lang['edit']['multiple_bookings'] = 'Multiple bookings';
$lang['edit']['kind'] = 'Kind';
$lang['edit']['resource'] = 'Resource';
$lang['edit']['relayhost'] = 'Sender-dependent transports';
$lang['edit']['public_comment'] = 'Public comment';
$lang['mailbox']['public_comment'] = 'Public comment';
$lang['edit']['private_comment'] = 'Private comment';
$lang['mailbox']['private_comment'] = 'Private comment';
$lang['edit']['comment_info'] = 'A private comment is not visible to the user, while a public comment is shown as tooltip when hovering it in a users overview';
$lang['add']['public_comment'] = 'Public comment';
$lang['add']['private_comment'] = 'Private comment';
$lang['add']['comment_info'] = 'A private comment is not visible to the user, while a public comment is shown as tooltip when hovering it in a users overview';
$lang['acl']['spam_alias'] = 'Temporary aliases';
$lang['acl']['tls_policy'] = 'TLS policy';
$lang['acl']['spam_score'] = 'Spam score';
@ -666,13 +673,15 @@ $lang['admin']['queue_unban'] = "queue unban";
$lang['admin']['no_active_bans'] = "No active bans";
$lang['admin']['quarantine'] = "Quarantine";
$lang['admin']['quarantine_retention_size'] = "Retentions per mailbox<br />0 indicates <b>inactive</b>!";
$lang['admin']['quarantine_max_size'] = "Maximum size in MiB (larger elements are discarded)<br />0 does <b>not</b> indicate unlimited!";
$lang['admin']['quarantine_exclude_domains'] = "Exclude domains and alias-domains:";
$lang['admin']['quarantine_release_format'] = "Format of released items:";
$lang['admin']['quarantine_retention_size'] = "Retentions per mailbox:<br><small>0 indicates <b>inactive</b>.</small>";
$lang['admin']['quarantine_max_size'] = "Maximum size in MiB (larger elements are discarded):<br><small>0 does <b>not</b> indicate unlimited.</small>";
$lang['admin']['quarantine_exclude_domains'] = "Exclude domains and alias-domains";
$lang['admin']['quarantine_release_format'] = "Format of released items";
$lang['admin']['quarantine_release_format_raw'] = "Unmodified original";
$lang['admin']['quarantine_release_format_att'] = "As attachment";
$lang['admin']['quarantine_notification_sender'] = "Notification email sender";
$lang['admin']['quarantine_notification_subject'] = "Notification email subject";
$lang['admin']['quarantine_notification_html'] = "Notification email template:<br><small>Leave empty to restore default template.</small>";
$lang['admin']['ui_texts'] = "UI labels and texts";
$lang['admin']['help_text'] = "Override help text below login mask (HTML allowed)";
$lang['admin']['title_name'] = '"mailcow UI" website title';
@ -699,6 +708,7 @@ $lang['user']['spam_score_reset'] = "Reset to server default";
$lang['edit']['spam_policy'] = "Add or remove items to white-/blacklist";
$lang['edit']['spam_alias'] = "Create or change time limited alias addresses";
$lang['danger']['comment_too_long'] = "Comment too long, max 160 chars allowed";
$lang['danger']['img_tmp_missing'] = "Cannot validate image file: Temporary file not found";
$lang['danger']['img_invalid'] = "Cannot validate image file";
$lang['danger']['invalid_mime_type'] = "Invalid mime type";

View File

@ -638,8 +638,8 @@ $lang['admin']['no_active_bans'] = "Geen actieve verbanningen";
$lang['admin']['quarantine'] = "Quarantaine";
$lang['admin']['quarantine_retention_size'] = "Maximale retenties per postvak<br />Gebruik 0 om deze functionaliteit <b>uit te zetten</b>.";
$lang['admin']['quarantine_max_size'] = "Maximale grootte in MiB (mail die de limiet overschrijdt zal worden verwijderd)<br />0 betekent <b>niet</b> onbeperkt!";
$lang['admin']['quarantine_exclude_domains'] = "Sluit domeinen en aliasdomeinen uit:";
$lang['admin']['quarantine_release_format'] = "Vrijgegeven items worden verstuurd als:";
$lang['admin']['quarantine_exclude_domains'] = "Sluit domeinen en aliasdomeinen uit";
$lang['admin']['quarantine_release_format'] = "Vrijgegeven items worden verstuurd als";
$lang['admin']['quarantine_release_format_raw'] = "Origineel";
$lang['admin']['quarantine_release_format_att'] = "Bijlage";

View File

@ -1,5 +1,6 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];

View File

@ -572,7 +572,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="script_data">Script:</label>
<div class="col-sm-10">
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="script_data" name="script_data" required></textarea>
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code" rows="20" id="script_data" name="script_data" required></textarea>
</div>
</div>
<div class="form-group">

View File

@ -25,11 +25,17 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<label for="qid_detail_text_from_html"><h4><?=$lang['quarantine']['text_from_html_content'];?>:</h4></label>
<pre id="qid_detail_text_from_html"></pre>
</div>
<?php
if ($_SESSION['acl']['quarantine_attachments'] == 1):
?>
<div class="form-group">
<label for="qid_detail_atts"><h4><?=$lang['quarantine']['atts'];?>:</h4></label>
<div id="qid_detail_atts">-</div>
</div>
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine'];?>">
<?php
endif;
?>
<div class="btn-group dropup" data-acl="<?=$_SESSION['acl']['quarantine'];?>">
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['quarantine']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a data-action="edit_selected" data-id="qitems_single" data-item="" data-api-url='edit/qitem' data-api-attr='{"action":"release"}' href="#"><?=$lang['quarantine']['release'];?></a></li>

View File

@ -168,7 +168,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="modal-content">
<div class="modal-header"><h4 class="modal-title">Log</h4></div>
<div class="modal-body">
<textarea class="form-control" rows="20" id="logText" spellcheck="false"></textarea>
<textarea class="form-control textarea-code" rows="20" id="logText" spellcheck="false"></textarea>
</div>
</div>
</div>

View File

@ -18,7 +18,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<?php
if (empty(quarantine('settings')['retention_size']) || empty(quarantine('settings')['max_size'])):
?>
<span class="help-block"><span class="glyphicon glyphicon-remove text-danger" style="font-size:10px"></span> <b><?=$lang['quarantine']['disabled_by_config'];?></b></span>
<div class="panel-body"><div class="alert alert-info"><?=$lang['quarantine']['disabled_by_config'];?></div></div>
<?php
endif;
?>
@ -39,6 +39,11 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</ul>
</div>
</div>
<hr>
<div class="panel-body help-block">
<p><span class="dot-danger"></span> <?=$lang['quarantine']['high_danger'];?></p>
<p><span class="dot-neutral"></span> <?=$lang['quarantine']['neutral_danger'];?></p>
</div>
</div>
</div> <!-- /col-md-12 -->
</div> <!-- /row -->
@ -49,6 +54,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/quarantine.php';
<script type='text/javascript'>
<?php
$lang_mailbox = json_encode($lang['quarantine']);
echo "var acl = '". json_encode($_SESSION['acl']) . "';\n";
echo "var lang = ". $lang_mailbox . ";\n";
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin';

View File

@ -125,7 +125,18 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p class="small"><?=$lang['user']['direct_aliases_desc'];?></p>
</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['direct_aliases'];?></p>
<?php
if ($user_get_alias_details['direct_aliases'] === false) {
echo '&#10008;';
}
else {
foreach (array_filter($user_get_alias_details['direct_aliases']) as $direct_alias => $direct_alias_meta) {
(!empty($direct_alias_meta['public_comment'])) ?
printf('%s <small>(%s)</small><br>', $direct_alias, $direct_alias_meta['public_comment']) :
printf('%s<br>', $direct_alias);
}
}
?>
</div>
</div>
<div class="row">
@ -133,7 +144,18 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p class="small"><?=$lang['user']['shared_aliases_desc'];?></p>
</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['shared_aliases'];?></p>
<?php
if ($user_get_alias_details['shared_aliases'] === false) {
echo '&#10008;';
}
else {
foreach (array_filter($user_get_alias_details['shared_aliases']) as $shared_alias => $shared_alias_meta) {
(!empty($shared_alias_meta['public_comment'])) ?
printf('%s <small>(%s)</small><br>', $shared_alias, $shared_alias_meta['public_comment']) :
printf('%s<br>', $shared_alias);
}
}
?>
</div>
</div>
<hr>
@ -223,7 +245,42 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
</div>
</div>
<?php
// Show quarantine_notification options
$quarantine_notification = mailbox('get', 'quarantine_notification', $username);
?>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_notification'];?>:</div>
<div class="col-md-9 col-xs-7">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_notification'];?>">
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "never") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_notification"
data-api-url='edit/quarantine_notification'
data-api-attr='{"quarantine_notification":"never"}'><?=$lang['user']['never'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "hourly") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_notification"
data-api-url='edit/quarantine_notification'
data-api-attr='{"quarantine_notification":"hourly"}'><?=$lang['user']['hourly'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "daily") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_notification"
data-api-url='edit/quarantine_notification'
data-api-attr='{"quarantine_notification":"daily"}'><?=$lang['user']['daily'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "weekly") ? "active" : null;?>"
data-action="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="quarantine_notification"
data-api-url='edit/quarantine_notification'
data-api-attr='{"quarantine_notification":"weekly"}'><?=$lang['user']['weekly'];?></button>
</div>
<p class="help-block"><?=$lang['user']['quarantine_notification_info'];?></p>
</div>
</div>
<hr>
<div class="row">