Merge branch 'temp-master'

master
André 2018-09-09 21:21:43 +02:00
commit c8a1cbd25a
40 changed files with 865 additions and 520 deletions

View File

@ -49,4 +49,6 @@ COPY sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
COPY acl.diff /acl.diff
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
VOLUME /usr/lib/GNUstep/SOGo/
RUN rm -rf /tmp/* /var/tmp/*

View File

@ -16,13 +16,6 @@ command=/usr/sbin/cron -f
autorestart=true
priority=2
[program:sogo-webres]
command=/usr/bin/python -u -m SimpleHTTPServer 9192
directory=/usr/lib/GNUstep/SOGo/
user=sogo
autorestart=true
priority=4
[program:bootstrap-sogo]
command=/bootstrap-sogo.sh
stdout_logfile=/dev/stdout

View File

@ -59,10 +59,10 @@ function mail_error() {
log_msg "Sent notification email to ${1}"
}
get_container_ip() {
# ${1} is container
CONTAINER_ID=()
CONTAINER_IPS=()
CONTAINER_IP=
LOOP_C=1
until [[ ${CONTAINER_IP} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] || [[ ${LOOP_C} -gt 5 ]]; do
@ -72,15 +72,21 @@ get_container_ip() {
# returned id can have multiple elements (if scaled), shuffle for random test
CONTAINER_ID=($(printf "%s\n" "${CONTAINER_ID[@]}" | shuf))
if [[ ! -z ${CONTAINER_ID} ]]; then
for matched_container in "${CONTAINER_ID[@]}"; do
CONTAINER_IP=$(curl --silent http://dockerapi:8080/containers/${matched_container}/json | jq -r '.NetworkSettings.Networks[].IPAddress')
# grep will do nothing if one of these vars is empty
[[ -z ${CONTAINER_IP} ]] && continue
[[ -z ${IPV4_NETWORK} ]] && continue
# only return ips that are part of our network
if ! grep -q ${IPV4_NETWORK} <(echo ${CONTAINER_IP}); then
CONTAINER_IP=
fi
for matched_container in "${CONTAINER_ID[@]}"; do
CONTAINER_IPS=($(curl --silent http://dockerapi:8080/containers/${matched_container}/json | jq -r '.NetworkSettings.Networks[].IPAddress'))
for ip_match in "${CONTAINER_IPS[@]}"; do
# grep will do nothing if one of these vars is empty
[[ -z ${ip_match} ]] && continue
[[ -z ${IPV4_NETWORK} ]] && continue
# only return ips that are part of our network
if ! grep -q ${IPV4_NETWORK} <(echo ${ip_match}); then
continue
else
CONTAINER_IP=${ip_match}
break
fi
done
[[ ! -z ${CONTAINER_IP} ]] && break
done
fi
LOOP_C=$((LOOP_C + 1))
@ -88,7 +94,6 @@ get_container_ip() {
[[ ${LOOP_C} -gt 5 ]] && echo 240.0.0.0 || echo ${CONTAINER_IP}
}
# Check functions
nginx_checks() {
err_count=0
diff_c=0

View File

@ -175,7 +175,7 @@ namespace {
type = shared
separator = /
prefix = Shared/%%u/
location = maildir:%%h/:CONTROL=~/Shared/%%u:INDEXPVT=~/Shared/%%u
location = maildir:%%h/:INDEX=~/Shared/%%u;CONTROL=~/Shared/%%u
subscriptions = no
list = children
}

View File

@ -156,44 +156,19 @@ server {
}
location /SOGo.woa/WebServerResources/ {
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
expires $expires;
allow all;
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /.woa/WebServerResources/ {
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
expires $expires;
allow all;
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /SOGo/WebServerResources/ {
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
proxy_pass http://sogo:9192/$1.SOGo/Resources/$2;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
}
include /etc/nginx/conf.d/site.*.custom;

View File

@ -1,11 +1,10 @@
# High spam networks, disabled by default
#201942:5 #Soltia Consulting SL - ipinfo.io
#16276:5 #OVH
#12876:5 #ONLINE S.A.S
#31034:5
#12874:5
#30823:5
#197071:5
#16276:2 #OVH
#12876:2 #ONLINE S.A.S
#31034:5 #ARUBA-ASN, IT
#12874:5 #FASTWEB, IT
#30823:3 #PKV spam
#42831:5 #UK Dedicated Servers Ltd
#29119:5 #Aire Networks del Mediterraneo S.L.U.
#13335:5 #Cloudflare
@ -17,7 +16,7 @@
#14061:4 #Digitalocean
#55293:4 #A2 Hosting
#63018:4 #US Dedicated
#197518:2
#197518:2 #RACKMARKT
#44493:2
#46606:2
#49505:2
@ -25,3 +24,5 @@
#197695:2
#198068:2
#43146:2
#49100:4
#39364:4

View File

@ -25,13 +25,6 @@ WHITELISTED_FWD_HOST {
symbols_set = ["WHITELISTED_FWD_HOST"];
}
KEEP_SPAM {
type = "ip";
map = "redis://KEEP_SPAM";
action = "accept";
symbols_set = ["KEEP_SPAM"];
}
LOCAL_BL_ASN {
require_symbols = "!MAILCOW_WHITE";
type = "asn";
@ -40,11 +33,3 @@ LOCAL_BL_ASN {
description = "Sender's ASN is on the local blacklist";
symbols_set = ["LOCAL_BL_ASN"];
}
#SPOOFED_SENDER {
# type = "rcpt";
# filter = "email:domain:tld";
# map = "redis://DOMAIN_MAP";
# require_symbols = "AUTH_NA | !RCVD_VIA_SMTP_AUTH";
# symbols_set = ["SPOOFED_SENDER"];
#}

View File

@ -7,6 +7,60 @@ rspamd_config.MAILCOW_AUTH = {
end
}
rspamd_config:register_symbol({
name = 'KEEP_SPAM',
type = 'prefilter',
callback = function(task)
local util = require("rspamd_util")
local rspamd_logger = require "rspamd_logger"
local rspamd_ip = require 'rspamd_ip'
local uname = task:get_user()
if uname then
return false
end
local redis_params = rspamd_parse_redis_server('keep_spam')
local ip = task:get_from_ip()
local from_ip_string = ip:to_string()
ip_check_table = {from_ip_string}
local maxbits = 128
local minbits = 32
if ip:get_version() == 4 then
maxbits = 32
minbits = 8
end
for i=maxbits,minbits,-1 do
local nip = ip:apply_mask(i):to_string() .. "/" .. i
table.insert(ip_check_table, nip)
end
local function keep_spam_cb(err, data)
if err then
rspamd_logger.infox(rspamd_config, "keep_spam query request for ip %s returned invalid or empty data (\"%s\") or error (\"%s\")", ip, data, err)
return false
else
for k,v in pairs(data) do
if (v and v ~= userdata and v == '1') then
rspamd_logger.infox(rspamd_config, "found ip in keep_spam map, setting pre-result", v)
task:set_pre_result('accept', 'IP matched with forward hosts')
end
end
end
end
table.insert(ip_check_table, 1, 'KEEP_SPAM')
local redis_ret_user = rspamd_redis_make_request(task,
redis_params, -- connect params
'KEEP_SPAM', -- hash key
false, -- is write
keep_spam_cb, --callback
'HMGET', -- command
ip_check_table -- arguments
)
if not redis_ret_user then
rspamd_logger.infox(rspamd_config, "cannot check keep_spam redis map")
end
end,
priority = 19
})
rspamd_config:register_symbol({
name = 'TAG_MOO',
type = 'postfilter',

View File

@ -29,7 +29,7 @@ $tfa_data = get_tfa();
<div class="form-group">
<label class="control-label col-sm-3" for="admin_pass"><?=$lang['admin']['password'];?>:</label>
<div class="col-sm-9">
<input type="password" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>">
<input type="password" data-hibp="true" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>">
</div>
</div>
<div class="form-group">
@ -44,7 +44,7 @@ $tfa_data = get_tfa();
</div>
</div>
</form>
<hr>
<legend><?=$lang['tfa']['tfa'];?></legend>
<div class="row">
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div>
<div class="col-sm-9 col-xs-7">
@ -76,12 +76,10 @@ $tfa_data = get_tfa();
</select>
</div>
</div>
</div>
</div>
<div class="hidden panel panel-primary">
<div class="panel-heading">API</div>
<div class="panel-body">
<legend data-target="#api" style="margin-top:40px;cursor:pointer" id="api_legend" unselectable="on" data-toggle="collapse">
<span id="api_arrow" style="font-size:12px" class="rotate glyphicon glyphicon-menu-down"></span> API (experimental, work in progress)
</legend>
<div id="api" class="collapse">
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
<div class="form-group">
<label class="control-label col-sm-3" for="allow_from"><?=$lang['admin']['api_allow_from'];?>:</label>
@ -111,6 +109,7 @@ $tfa_data = get_tfa();
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -65,12 +65,6 @@ body.modal-open {
font-size:9pt;
background:transparent;
}
.bootstrap-select {
width: auto!important;
}
.table-condensed .input-sm {
width: 100%!important;
}
.full-width-select {
width: 100%!important;
}

View File

@ -5,9 +5,6 @@ table.footable>tbody>tr.footable-empty>td {
.pagination a {
text-decoration: none !important;
}
.panel panel-default {
overflow: visible !important;
}
.btn-group {
width: max-content;
}

View File

@ -148,3 +148,13 @@ nav .glyphicon {
color: #5a5a5a;
white-space: nowrap;
}
.haveibeenpwned {
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.full-width-select {
width: 100%!important;
}

View File

@ -92,7 +92,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="domains"><?=$lang['edit']['domains'];?></label>
<div class="col-sm-10">
<select data-live-search="true" id="domains" name="domains" multiple required>
<select data-live-search="true" class="full-width-select" id="domains" name="domains" multiple required>
<?php
foreach ($result['selected_domains'] as $domain):
?>
@ -111,7 +111,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="password"><?=$lang['edit']['password'];?></label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" id="password" placeholder="">
<input type="password" data-hibp="true" class="form-control" name="password" id="password" placeholder="">
</div>
</div>
<div class="form-group">
@ -140,6 +140,30 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</form>
<form data-id="daacl" class="form-inline well" method="post">
<div class="row">
<div class="col-sm-1">
<p class="help-block">ACL</p>
</div>
<div class="col-sm-10">
<div class="form-group">
<select id="da_acl" name="da_acl" size="10" multiple>
<?php
$da_acls = acl('get', 'domainadmin', $domain_admin);
foreach ($da_acls as $acl => $val):
?>
<option value="<?=$acl;?>" <?=($val == 1) ? 'selected' : null;?>><?=$lang['acl'][$acl];?></option>
<?php
endforeach;
?>
</select>
</div>
<div class="form-group">
<button class="btn btn-default" id="edit_selected" data-id="daacl" data-item="<?=htmlspecialchars($domain_admin);?>" data-api-url='edit/da-acl' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
</div>
</div>
</div>
</form>
<?php
}
else {
@ -266,7 +290,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</select>
</div>
<div class="form-group">
<button class="btn btn-default" id="edit_selected" data-id="domratelimit" data-item="<?=$domain;?>" data-api-url='edit/rl-domain' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
<button data-acl="<?=$_SESSION['acl']['ratelimit'];?>" class="btn btn-default" id="edit_selected" data-id="domratelimit" data-item="<?=$domain;?>" data-api-url='edit/rl-domain' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
</div>
</form>
<hr>
@ -278,14 +302,14 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<table class="table table-striped table-condensed" id="wl_policy_domain_table"></table>
</div>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_wl_domain" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_domain" data-api-url='delete/domain-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</ul>
</div>
</div>
<form class="form-inline" data-id="add_wl_policy_domain">
<div class="input-group">
<div class="input-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
<span class="input-group-btn">
<button class="btn btn-default" id="add_item" data-id="add_wl_policy_domain" data-api-url='add/domain-policy' data-api-attr='{"domain":"<?= $domain; ?>","object_list":"wl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
@ -300,14 +324,14 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<table class="table table-striped table-condensed" id="bl_policy_domain_table"></table>
</div>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_bl_domain" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_domain" data-api-url='delete/domain-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</ul>
</div>
</div>
<form class="form-inline" data-id="add_bl_policy_domain">
<div class="input-group">
<div class="input-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
<span class="input-group-btn">
<button class="btn btn-default" id="add_item" data-id="add_bl_policy_domain" data-api-url='add/domain-policy' data-api-attr='{"domain":"<?= $domain; ?>","object_list":"bl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
@ -474,7 +498,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="password"><?=$lang['edit']['password'];?></label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" id="password" placeholder="<?=$lang['edit']['unchanged_if_empty'];?>">
<input type="password" data-hibp="true" class="form-control" name="password" id="password" placeholder="<?=$lang['edit']['unchanged_if_empty'];?>">
</div>
</div>
<div class="form-group">
@ -527,6 +551,30 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</form>
<form data-id="useracl" class="form-inline well" method="post">
<div class="row">
<div class="col-sm-1">
<p class="help-block">ACL</p>
</div>
<div class="col-sm-10">
<div class="form-group">
<select id="user_acl" name="user_acl" size="10" multiple>
<?php
$user_acls = acl('get', 'user', $mailbox);
foreach ($user_acls as $acl => $val):
?>
<option value="<?=$acl;?>" <?=($val == 1) ? 'selected' : null;?>><?=$lang['acl'][$acl];?></option>
<?php
endforeach;
?>
</select>
</div>
<div class="form-group">
<button class="btn btn-default" id="edit_selected" data-id="useracl" data-item="<?=htmlspecialchars($mailbox);?>" data-api-url='edit/user-acl' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
</div>
</div>
</div>
</form>
<?php
}
}
@ -553,7 +601,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" id="password" value="<?=htmlspecialchars($result['password'], ENT_QUOTES, 'UTF-8');?>">
<input type="password" data-hibp="true" class="form-control" name="password" id="password" value="<?=htmlspecialchars($result['password'], ENT_QUOTES, 'UTF-8');?>">
</div>
</div>
<div class="form-group">
@ -965,7 +1013,7 @@ else {
<script type='text/javascript'>
<?php
$lang_user = json_encode($lang['user']);
echo "var lang = ". $lang_user . ";\n";
echo "var lang_user = ". $lang_user . ";\n";
echo "var table_for_domain = '". ((isset($domain)) ? $domain : null) . "';\n";
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";

View File

@ -12,10 +12,20 @@ logger();
<script src="/js/formcache.min.js"></script>
<script src="/js/google.charts.loader.js"></script>
<script src="/js/numberedtextarea.min.js"></script>
<script src="/js/sha1.min.js"></script>
<script src="/js/u2f-api.js"></script>
<script src="/js/api.js"></script>
<script src="/js/mailcow.js"></script>
<script>
var loading_text = '<?= $lang['footer']['loading']; ?>'
<?php
$lang_footer = json_encode($lang['footer']);
$lang_acl = json_encode($lang['acl']);
$lang_tfa = json_encode($lang['tfa']);
echo "var lang_footer = ". $lang_footer . ";\n";
echo "var lang_acl = ". $lang_acl . ";\n";
echo "var lang_tfa = ". $lang_tfa . ";\n";
echo "var docker_timeout = ". $DOCKER_TIMEOUT * 1000 . ";\n";
?>
$(window).scroll(function() {
sessionStorage.scrollTop = $(this).scrollTop();
});
@ -28,17 +38,8 @@ $(window).load(function() {
$(".overlay").hide();
});
$(document).ready(function() {
window.mailcow_alert_box = function(message, type) {
msg = $('<span/>').text(message).text();
if (type == 'danger') {
auto_hide = 0;
$('#' + localStorage.getItem("add_modal")).modal('show');
localStorage.removeItem("add_modal");
} else {
auto_hide = 5000;
}
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
}
// TFA, CSRF, Alerts in footer.inc.php
// Other general functions in mailcow.js
<?php
$alertbox_log_parser = alertbox_log_parser($_SESSION);
if (is_array($alertbox_log_parser)) {
@ -50,7 +51,6 @@ $(document).ready(function() {
unset($_SESSION['return']);
}
?>
$('[data-cached-form="true"]').formcache({key: $(this).data('id')});
// Confirm TFA modal
<?php if (isset($_SESSION['pending_tfa_method'])):?>
$('#ConfirmTFAModal').modal({
@ -68,7 +68,7 @@ $(document).ready(function() {
dataType: 'script',
url: "/api/v1/get/u2f-authentication/<?= (isset($_SESSION['pending_mailcow_cc_username'])) ? rawurlencode($_SESSION['pending_mailcow_cc_username']) : null; ?>",
complete: function(data){
$('#u2f_status_auth').html('<?=$lang['tfa']['waiting_usb_auth'];?>');
$('#u2f_status_auth').html(lang_tfa.waiting_usb_auth);
data;
setTimeout(function() {
console.log("Ready to authenticate");
@ -98,7 +98,6 @@ $(document).ready(function() {
<?php endif; ?>
// Set TFA modals
$('#selectTFA').change(function () {
if ($(this).val() == "yubi_otp") {
$('#YubiOTPModal').modal('show');
@ -121,7 +120,7 @@ $(document).ready(function() {
data;
setTimeout(function() {
console.log("Ready to register");
$('#u2f_status_reg').html('<?=$lang['tfa']['waiting_usb_register'];?>');
$('#u2f_status_reg').html(lang_tfa.waiting_usb_register);
u2f.register(appId, registerRequests, registeredKeys, function(deviceResponse) {
var form = document.getElementById('u2f_reg_form');
var reg = document.getElementById('u2f_register_data');
@ -146,92 +145,6 @@ $(document).ready(function() {
}
});
$(function () {
$('[data-toggle="tooltip"]').tooltip()
});
// Remember last navigation pill
(function () {
'use strict';
if ($('a[data-toggle="tab"]').length) {
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
if ($(this).data('dont-remember') == 1) {
return true;
}
var id = $(this).parents('[role="tablist"]').attr('id');
var key = 'lastTag';
if (id) {
key += ':' + id;
}
localStorage.setItem(key, $(e.target).attr('href'));
});
$('[role="tablist"]').each(function (idx, elem) {
var id = $(elem).attr('id');
var key = 'lastTag';
if (id) {
key += ':' + id;
}
var lastTab = localStorage.getItem(key);
if (lastTab) {
$('[href="' + lastTab + '"]').tab('show');
}
});
}
})();
// Disable submit after submitting form (not API driven buttons)
$('form').submit(function() {
if ($('form button[type="submit"]').data('submitted') == '1') {
return false;
} else {
$(this).find('button[type="submit"]').first().text('<?= $lang['footer']['loading']; ?>');
$('form button[type="submit"]').attr('data-submitted', '1');
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).on("keydown", disableF5);
}
});
// IE fix to hide scrollbars when table body is empty
$('tbody').filter(function (index) {
return $(this).children().length < 1;
}).remove();
// Init Bootstrap Selectpicker
$('select').selectpicker();
// Trigger container restart
$('#RestartContainer').on('show.bs.modal', function(e) {
var container = $(e.relatedTarget).data('container');
$('#containerName').text(container);
$('#triggerRestartContainer').click(function(){
$(this).prop("disabled",true);
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
$('#statusTriggerRestartContainer').html('<?= $lang['footer']['restarting_container']; ?>');
$.ajax({
method: 'get',
url: '/inc/ajax/container_ctrl.php',
timeout: <?= $DOCKER_TIMEOUT * 1000; ?>,
data: {
'service': container,
'action': 'restart'
}
})
.always( function (data, status) {
$('#statusTriggerRestartContainer').append(data);
var htmlResponse = $.parseHTML(data)
if ($(htmlResponse).find('span').hasClass('text-success')) {
$('#triggerRestartContainer').html('<span class="glyphicon glyphicon-ok"></span> ');
setTimeout(function(){
$('#RestartContainer').modal('toggle');
window.location = window.location.href.split("#")[0];
}, 1200);
} else {
$('#triggerRestartContainer').html('<span class="glyphicon glyphicon-remove"></span> ');
}
})
});
})
// CSRF
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');
if (sessionStorage.scrollTop != "undefined") {

View File

@ -0,0 +1,216 @@
<?php
function acl($_action, $_scope = null, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'edit':
switch ($_scope) {
case 'user':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['user_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'user', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `user_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['da_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'domainadmin', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `da_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'user':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin') {
return false;
}
if ($_SESSION['mailcow_cc_role'] == 'domainadmin' && $_SESSION['mailcow_cc_username'] != $_data) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
}
break;
case 'to_session':
if (!isset($_SESSION['mailcow_cc_role'])) {
return false;
}
unset($_SESSION['acl']);
$username = strtolower(trim($_SESSION['mailcow_cc_username']));
// Admins get access to all modules
if ($_SESSION['mailcow_cc_role'] == 'admin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'admin')) {
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
$stmt = $pdo->query("SHOW COLUMNS FROM `da_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
}
elseif ($_SESSION['mailcow_cc_role'] == 'domainadmin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'domainadmin')) {
// Read all exting user_acl modules and set to 1
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
// Read da_acl rules for current user, OVERWRITE overlapping modules
// This prevents access to a users sync jobs, when a domain admins was not given access to sync jobs
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => (isset($_SESSION["dual-login"]["username"])) ? $_SESSION["dual-login"]["username"] : $username));
$acl_user = $stmt->fetch(PDO::FETCH_ASSOC);
foreach ($acl_user as $acl_user_key => $acl_user_val) {
$acl['acl'][$acl_user_key] = $acl_user_val;
}
unset($acl['acl']['username']);
}
elseif ($_SESSION['mailcow_cc_role'] == 'user') {
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$acl['acl'] = $stmt->fetch(PDO::FETCH_ASSOC);
unset($acl['acl']['username']);
}
if (!empty($acl)) {
$_SESSION = array_merge($_SESSION, $acl);
}
break;
}
}

View File

@ -233,6 +233,14 @@ function bcc($_action, $_data = null, $attr = null) {
return $bccdata;
break;
case 'delete':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
@ -279,6 +287,14 @@ function recipient_map($_action, $_data = null, $attr = null) {
}
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['recipient_maps']) || $_SESSION['acl']['recipient_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$old_dest = strtolower(trim($_data['recipient_map_old']));
if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1);
@ -343,6 +359,14 @@ function recipient_map($_action, $_data = null, $attr = null) {
);
break;
case 'edit':
if (!isset($_SESSION['acl']['recipient_maps']) || $_SESSION['acl']['recipient_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = recipient_map('details', $id);
@ -458,6 +482,14 @@ function recipient_map($_action, $_data = null, $attr = null) {
return $mapdata;
break;
case 'delete':
if (!isset($_SESSION['acl']['recipient_maps']) || $_SESSION['acl']['recipient_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {

View File

@ -90,43 +90,22 @@ function domain_admin($_action, $_data = null) {
);
return false;
}
try {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username, :domain, :created, :active)");
$stmt->execute(array(
':username' => $username,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
catch (PDOException $e) {
domain_admin('delete', $username);
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
return false;
}
}
try {
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)");
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username, :domain, :created, :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':active' => $active
));
}
else {
$_SESSION['return'][] = array(
@ -136,6 +115,10 @@ function domain_admin($_action, $_data = null) {
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
@ -209,41 +192,20 @@ function domain_admin($_action, $_data = null) {
continue;
}
}
try {
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
if (!empty($domains)) {
foreach ($domains as $domain) {
try {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username_new, :domain, :created, :active)");
$stmt->execute(array(
':username_new' => $username_new,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username_new, :domain, :created, :active)");
$stmt->execute(array(
':username_new' => $username_new,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
}
@ -265,56 +227,36 @@ function domain_admin($_action, $_data = null) {
continue;
}
$password_hashed = hash_password($password);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
else {
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
$_SESSION['return'][] = array(
@ -365,21 +307,11 @@ function domain_admin($_action, $_data = null) {
return false;
}
$password_hashed = hash_password($password_new);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
return false;
}
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username
));
}
$_SESSION['return'][] = array(
'type' => 'success',
@ -407,24 +339,14 @@ function domain_admin($_action, $_data = null) {
);
continue;
}
try {
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),

View File

@ -373,54 +373,6 @@ function check_login($user, $pass) {
sleep($_SESSION['ldelay']);
return false;
}
function set_acl() {
global $pdo;
if (!isset($_SESSION['mailcow_cc_username'])) {
return false;
}
if ($_SESSION['mailcow_cc_role'] == 'admin' || $_SESSION['mailcow_cc_role'] == 'domainadmin') {
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
}
else {
$username = strtolower(trim($_SESSION['mailcow_cc_username']));
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$acl['acl'] = $stmt->fetch(PDO::FETCH_ASSOC);
unset($acl['acl']['username']);
}
if (!empty($acl)) {
$_SESSION = array_merge($_SESSION, $acl);
}
else {
$_SESSION['return'][] = array(
'type' => 'info',
'log' => array(__FUNCTION__),
'msg' => 'set_acl_failed'
);
return false;
}
}
function get_acl($username) {
global $pdo;
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$username = strtolower(trim($username));
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$acl = $stmt->fetch(PDO::FETCH_ASSOC);
unset($acl['username']);
if (!empty($acl)) {
return $acl;
}
else {
return false;
}
}
function formatBytes($size, $precision = 2) {
if(!is_numeric($size)) {
return "0";
@ -1266,7 +1218,7 @@ function get_admin_details() {
return false;
}
$stmt = $pdo->query("SELECT `admin`.`username`, `api`.`active` AS `api_active`, `api`.`api_key`, `api`.`allow_from` FROM `admin`
INNER JOIN `api` ON `admin`.`username` = `api`.`username`
LEFT OUTER JOIN `api` ON `admin`.`username` = `api`.`username`
WHERE `admin`.`superadmin`='1'
AND `admin`.`active`='1'");
$data = $stmt->fetch(PDO::FETCH_ASSOC);

View File

@ -1452,14 +1452,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
break;
case 'filter':
$sieve = new Sieve\SieveParser();
if (!is_array($_data['id'])) {
$ids = array();
$ids[] = $_data['id'];
}
else {
$ids = $_data['id'];
}
if (!isset($_SESSION['acl']['filters']) || $_SESSION['acl']['filters'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
@ -1468,6 +1460,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$sieve = new Sieve\SieveParser();
if (!is_array($_data['id'])) {
$ids = array();
$ids[] = $_data['id'];
}
else {
$ids = $_data['id'];
}
foreach ($ids as $id) {
$is_now = mailbox('get', 'filter_details', $id);
if (!empty($is_now)) {

View File

@ -6,6 +6,14 @@ function policy($_action, $_scope, $_data = null) {
$_data_log = $_data;
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
$object = $_data['domain'];
@ -90,14 +98,6 @@ function policy($_action, $_scope, $_data = null) {
);
return false;
}
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if ($_data['object_list'] == "bl") {
$object_list = "blacklist_from";
}
@ -151,6 +151,14 @@ function policy($_action, $_scope, $_data = null) {
}
break;
case 'delete':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
(array)$prefids = $_data['prefid'];
@ -215,14 +223,6 @@ function policy($_action, $_scope, $_data = null) {
else {
$prefids = $_data['prefid'];
}
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
foreach ($prefids as $prefid) {
if (!is_numeric($prefid)) {
$_SESSION['return'][] = array(

View File

@ -5,6 +5,14 @@ function ratelimit($_action, $_scope, $_data = null) {
$_data_log = $_data;
switch ($_action) {
case 'edit':
if (!isset($_SESSION['acl']['ratelimit']) || $_SESSION['acl']['ratelimit'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
if (!is_array($_data['object'])) {

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "19082018_1004";
$db_version = "01092018_1902";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -280,10 +280,7 @@ function init_db_schema() {
"delimiter_action" => "TINYINT(1) NOT NULL DEFAULT '1'",
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'",
"bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'",
"recipient_maps" => "TINYINT(1) NOT NULL DEFAULT '0'",
),
"keys" => array(
"primary" => array(
@ -300,6 +297,32 @@ function init_db_schema() {
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"da_acl" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'",
"login_as" => "TINYINT(1) NOT NULL DEFAULT '1'",
"bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'",
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
"ratelimit" => "TINYINT(1) NOT NULL DEFAULT '1'",
"spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'",
),
"keys" => array(
"primary" => array(
"" => array("username")
),
"fkey" => array(
"fk_domain_admin_acl" => array(
"col" => "username",
"ref" => "domain_admins.username",
"delete" => "CASCADE",
"update" => "NO ACTION"
)
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"alias_domain" => array(
"cols" => array(
"alias_domain" => "VARCHAR(255) NOT NULL",
@ -950,8 +973,9 @@ DELIMITER ;';
'msg' => 'db_init_complete'
);
// Fix user_acl
// Fix ACL
$stmt = $pdo->query("INSERT INTO `user_acl` (`username`) SELECT `username` FROM `mailbox` WHERE `kind` = '' AND NOT EXISTS (SELECT `username` FROM `user_acl`);");
$stmt = $pdo->query("INSERT INTO `da_acl` (`username`) SELECT DISTINCT `username` FROM `domain_admins` WHERE `username` != 'admin' AND NOT EXISTS (SELECT `username` FROM `da_acl`);");
}
catch (PDOException $e) {
$_SESSION['return'][] = array(

View File

@ -124,6 +124,7 @@ if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) {
require_once $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.php';
include $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.acl.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.address_rewriting.inc.php';
@ -141,6 +142,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php';
init_db_schema();
if (isset($_SESSION['mailcow_cc_role'])) {
set_acl();
acl('to_session');
}
$UI_TEXTS = customize('get', 'ui_texts');

View File

@ -40,7 +40,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
}
}
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1") {
if (isset($_GET["duallogin"])) {
$duallogin = html_entity_decode(rawurldecode($_GET["duallogin"]));
if (filter_var($duallogin, FILTER_VALIDATE_EMAIL)) {

View File

@ -13,6 +13,10 @@ jQuery(function($){
e.preventDefault();
$('#duplicate_dkim_arrow').toggleClass("animation");
});
$("#api_legend").on('click', function(e) {
e.preventDefault();
$('#api_arrow').toggleClass("animation");
});
$("#rspamd_preset_1").on('click', function(e) {
e.preventDefault();
$("form[data-id=rsetting]").find("#desc").val(lang.rsettings_preset_1);

View File

@ -5,9 +5,9 @@ $(document).ready(function() {
} else {
var parent_btn_grp = $(elem).parentsUntil(".btn-group").parent();
if (parent_btn_grp.hasClass('btn-group')) {
parent_btn_grp.replaceWith('<button class="btn btn-default btn-sm" disabled>' + loading_text + '</a>');
parent_btn_grp.replaceWith('<button class="btn btn-default btn-sm" disabled>' + lang_footer.loading + '</a>');
}
$(elem).text(loading_text);
$(elem).text(lang_footer.loading);
$(elem).attr('data-submitted', '1');
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).on("keydown", disableF5);

View File

@ -61,10 +61,10 @@ jQuery(function($){
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
{"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
{"sorted": true,"name":"value","title":lang_user.spamfilter_table_rule},
{"name":"object","title":"Scope"}
],
"empty": lang.empty,
"empty": lang_user.empty,
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
@ -78,7 +78,7 @@ jQuery(function($){
item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
}
else {
item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
}
});
}
@ -98,10 +98,10 @@ jQuery(function($){
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
{"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
{"sorted": true,"name":"value","title":lang_user.spamfilter_table_rule},
{"name":"object","title":"Scope"}
],
"empty": lang.empty,
"empty": lang_user.empty,
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
@ -115,7 +115,7 @@ jQuery(function($){
item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
}
else {
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
}
});
}

View File

@ -1,4 +1,5 @@
$(document).ready(function() {
acl_data = JSON.parse(acl);
FooTable.domainFilter = FooTable.Filtering.extend({
construct: function(instance){
this._super(instance);
@ -82,6 +83,7 @@ $(document).ready(function() {
$(".generate_password").click(function( event ) {
event.preventDefault();
$('[data-hibp]').trigger('input');
var random_passwd = Math.random().toString(36).slice(-8)
$('#password').prop('type', 'text');
$('#password').val(random_passwd);
@ -233,6 +235,13 @@ jQuery(function($){
eval(draw_table + '()');
});
function table_mailbox_ready(ft, name) {
if(is_dual) {
$('.login_as').data("toggle", "tooltip")
.attr("disabled", true)
.removeAttr("href")
.attr("title", "Dual login cannot be used twice")
.tooltip();
}
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
var ft_paging = ft.use(FooTable.Paging)
$(heading).children('.table-lines').text(function(){
@ -264,10 +273,10 @@ jQuery(function($){
},
},
{"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm","style":{"width":"125px"}},
{"name":"rl","title":"RL","breakpoints":"xs sm md","style":{"width":"125px"}},
{"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm"},
{"name":"rl","title":"RL","breakpoints":"xs sm md","style":{"maxWidth":"100px","width":"100px"}},
{"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm md"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"240px","width":"240px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"240px","width":"240px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
],
"rows": $.ajax({
dataType: 'json',
@ -374,11 +383,11 @@ jQuery(function($){
}).join('/1');
}
item.chkbox = '<input type="checkbox" data-id="mailbox" name="multi_select" value="' + encodeURIComponent(item.username) + '" />';
if (role == "admin") {
if (acl_data.login_as === 1) {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?mailbox=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="#" id="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>' +
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="login_as btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>' +
'</div>';
}
else {

View File

@ -0,0 +1,193 @@
$(document).ready(function() {
// mailcow alert box generator
window.mailcow_alert_box = function(message, type) {
msg = $('<span/>').text(message).text();
if (type == 'danger') {
auto_hide = 0;
$('#' + localStorage.getItem("add_modal")).modal('show');
localStorage.removeItem("add_modal");
} else {
auto_hide = 5000;
}
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
}
// https://stackoverflow.com/questions/4399005/implementing-jquerys-shake-effect-with-animate
function shake(div,interval=100,distance=10,times=4) {
$(div).css('position','relative');
for(var iter=0;iter<(times+1);iter++){
$(div).animate({ left: ((iter%2==0 ? distance : distance*-1))}, interval);
}
$(div).animate({ left: 0},interval);
}
// form cache
$('[data-cached-form="true"]').formcache({key: $(this).data('id')});
// tooltips
$(function () {
$('[data-toggle="tooltip"]').tooltip()
});
// remember last navigation pill
(function () {
'use strict';
if ($('a[data-toggle="tab"]').length) {
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
if ($(this).data('dont-remember') == 1) {
return true;
}
var id = $(this).parents('[role="tablist"]').attr('id');
var key = 'lastTag';
if (id) {
key += ':' + id;
}
localStorage.setItem(key, $(e.target).attr('href'));
});
$('[role="tablist"]').each(function (idx, elem) {
var id = $(elem).attr('id');
var key = 'lastTag';
if (id) {
key += ':' + id;
}
var lastTab = localStorage.getItem(key);
if (lastTab) {
$('[href="' + lastTab + '"]').tab('show');
}
});
}
})();
// IE fix to hide scrollbars when table body is empty
$('tbody').filter(function (index) {
return $(this).children().length < 1;
}).remove();
// selectpicker
$('select').selectpicker();
// haveibeenpwned?
$('[data-hibp]').after('<p class="small haveibeenpwned">↪ Check against haveibeenpwned.com</p><span class="hibp-out"></span>');
$('[data-hibp]').on('input', function() {
out_field = $(this).next('.haveibeenpwned').next('.hibp-out').text('').attr('class', 'hibp-out');
});
$('.haveibeenpwned:not(.task-running)').on('click', function() {
var hibp_field = $(this)
$(hibp_field).addClass('task-running');
var hibp_result = $(hibp_field).next('.hibp-out')
var password_field = $(this).prev('[data-hibp]')
if ($(password_field).val() == '') {
shake(password_field);
}
else {
$(hibp_result).attr('class', 'hibp-out label label-info');
$(hibp_result).text(lang_footer.loading);
var password_digest = $.sha1($(password_field).val())
var digest_five = password_digest.substring(0, 5).toUpperCase();
var queryURL = "https://api.pwnedpasswords.com/range/" + digest_five;
var compl_digest = password_digest.substring(5, 41).toUpperCase();
$.ajax({
url: queryURL,
type: 'GET',
success: function(res) {
if (res.search(compl_digest) > -1){
$(hibp_result).removeClass('label label-info').addClass('label label-danger');
$(hibp_result).text(lang_footer.hibp_nok)
} else {
$(hibp_result).removeClass('label label-info').addClass('label label-success');
$(hibp_result).text(lang_footer.hibp_ok)
}
$(hibp_field).removeClass('task-running');
},
error: function(xhr, status, error) {
$(hibp_result).removeClass('label label-info').addClass('label label-warning');
$(hibp_result).text('API error: ' + xhr.responseText)
$(hibp_field).removeClass('task-running');
}
});
}
});
// Disable disallowed inputs
$('[data-acl="0"]').each(function(){
if ($(this).attr('class') == 'btn-group') {
$(this).find('a').each(function(){
$(this).removeClass('dropdown-toggle')
.removeAttr('data-toggle')
.removeAttr('id')
.attr("disabled", true);
$(this).click(function(event) {
event.preventDefault();
return;
});
});
$(this).find('button').each(function() {
$(this).attr("disabled", true);
});
} else if ($(this).attr('class') == 'input-group') {
$(this).find('input').each(function() {
$(this).removeClass('dropdown-toggle')
.removeAttr('data-toggle')
.attr("disabled", true);
$(this).click(function(event) {
event.preventDefault();
});
});
$(this).find('button').each(function() {
$(this).attr("disabled", true);
});
} else if ($(this).hasClass('btn')) {
$(this).attr("disabled", true);
} else if ($(this).attr('data-provide', 'slider')) {
$(this).slider("disable");
}
$(this).data("toggle", "tooltip");
$(this).attr("title", lang_acl.prohibited);
$(this).tooltip();
});
// disable submit after submitting form (not API driven buttons)
$('form').submit(function() {
if ($('form button[type="submit"]').data('submitted') == '1') {
return false;
} else {
$(this).find('button[type="submit"]').first().text(lang_footer.loading);
$('form button[type="submit"]').attr('data-submitted', '1');
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).on("keydown", disableF5);
}
});
// trigger container restart
$('#RestartContainer').on('show.bs.modal', function(e) {
var container = $(e.relatedTarget).data('container');
$('#containerName').text(container);
$('#triggerRestartContainer').click(function(){
$(this).prop("disabled",true);
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
$('#statusTriggerRestartContainer').html(lang_footer.restarting_container);
$.ajax({
method: 'get',
url: '/inc/ajax/container_ctrl.php',
timeout: docker_timeout,
data: {
'service': container,
'action': 'restart'
}
})
.always( function (data, status) {
$('#statusTriggerRestartContainer').append(data);
var htmlResponse = $.parseHTML(data)
if ($(htmlResponse).find('span').hasClass('text-success')) {
$('#triggerRestartContainer').html('<span class="glyphicon glyphicon-ok"></span> ');
setTimeout(function(){
$('#RestartContainer').modal('toggle');
window.location = window.location.href.split("#")[0];
}, 1200);
} else {
$('#triggerRestartContainer').html('<span class="glyphicon glyphicon-remove"></span> ');
}
})
});
})
});

1
data/web/js/sha1.min.js vendored 100644
View File

@ -0,0 +1 @@
!function(r){var o=function(r,o){return r<<o|r>>>32-o},e=function(r){var o,e="";for(o=7;o>=0;o--)e+=(r>>>4*o&15).toString(16);return e};jQuery.extend({sha1:function(r){var a,t,n,h,C,c,f,d,u,i=new Array(80),A=1732584193,g=4023233417,s=2562383102,S=271733878,m=3285377520,p=(r=function(r){r=r.replace(/\x0d\x0a/g,"\n");for(var o="",e=0;e<r.length;e++){var a=r.charCodeAt(e);a<128?o+=String.fromCharCode(a):a>127&&a<2048?(o+=String.fromCharCode(a>>6|192),o+=String.fromCharCode(63&a|128)):(o+=String.fromCharCode(a>>12|224),o+=String.fromCharCode(a>>6&63|128),o+=String.fromCharCode(63&a|128))}return o}(r)).length,l=new Array;for(t=0;t<p-3;t+=4)n=r.charCodeAt(t)<<24|r.charCodeAt(t+1)<<16|r.charCodeAt(t+2)<<8|r.charCodeAt(t+3),l.push(n);switch(p%4){case 0:t=2147483648;break;case 1:t=r.charCodeAt(p-1)<<24|8388608;break;case 2:t=r.charCodeAt(p-2)<<24|r.charCodeAt(p-1)<<16|32768;break;case 3:t=r.charCodeAt(p-3)<<24|r.charCodeAt(p-2)<<16|r.charCodeAt(p-1)<<8|128}for(l.push(t);l.length%16!=14;)l.push(0);for(l.push(p>>>29),l.push(p<<3&4294967295),a=0;a<l.length;a+=16){for(t=0;t<16;t++)i[t]=l[a+t];for(t=16;t<=79;t++)i[t]=o(i[t-3]^i[t-8]^i[t-14]^i[t-16],1);for(h=A,C=g,c=s,f=S,d=m,t=0;t<=19;t++)u=o(h,5)+(C&c|~C&f)+d+i[t]+1518500249&4294967295,d=f,f=c,c=o(C,30),C=h,h=u;for(t=20;t<=39;t++)u=o(h,5)+(C^c^f)+d+i[t]+1859775393&4294967295,d=f,f=c,c=o(C,30),C=h,h=u;for(t=40;t<=59;t++)u=o(h,5)+(C&c|C&f|c&f)+d+i[t]+2400959708&4294967295,d=f,f=c,c=o(C,30),C=h,h=u;for(t=60;t<=79;t++)u=o(h,5)+(C^c^f)+d+i[t]+3395469782&4294967295,d=f,f=c,c=o(C,30),C=h,h=u;A=A+h&4294967295,g=g+C&4294967295,s=s+c&4294967295,S=S+f&4294967295,m=m+d&4294967295}return(u=e(A)+e(g)+e(s)+e(S)+e(m)).toLowerCase()}})}();

View File

@ -1039,6 +1039,12 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "rl-mbox":
process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
break;
case "user-acl":
process_edit_return(acl('edit', 'user', array_merge(array('username' => $items), $attr)));
break;
case "da-acl":
process_edit_return(acl('edit', 'domainadmin', array_merge(array('username' => $items), $attr)));
break;
case "alias-domain":
process_edit_return(mailbox('edit', 'alias_domain', array_merge(array('alias_domain' => $items), $attr)));
break;

View File

@ -16,6 +16,9 @@ $lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen a
$lang['footer']['delete_now'] = 'Jetzt löschen';
$lang['footer']['cancel'] = 'Abbrechen';
$lang['footer']['hibp_nok'] = 'Übereinstimmung gefunden! Dieses Passwort ist potentiell gefährlich!';
$lang['footer']['hibp_ok'] = 'Keine Übereinstimmung gefunden.';
$lang['danger']['mysql_error'] = "MySQL Fehler: %s";
$lang['danger']['redis_error'] = "Redis Fehler: %s";
$lang['danger']['unknown_tfa_method'] = "Unbekannte TFA Methode";
@ -43,6 +46,7 @@ $lang['danger']['domain_cannot_match_hostname'] = "Domain darf nicht dem Hostnam
$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt; SOGo konnte nicht neugestartet werden";
$lang['danger']['rl_timeframe'] = "Ratelimit Zeitraum ist inkorrekt";
$lang['success']['rl_saved'] = "Ratelimit für Objekt %s wurde gesetzt";
$lang['success']['acl_saved'] = "ACL für Objekt %s wurde gesetzt";
$lang['success']['deleted_syncjobs'] = "Syncjobs gelöscht: %s";
$lang['success']['deleted_syncjob'] = "Syncjobs ID %s gelöscht";
$lang['success']['delete_filters'] = "Filter gelöscht: %s";
@ -332,6 +336,8 @@ $lang['edit']['relay_all_info'] = '<small>Wenn Sie <b>nicht</b> alle Empfänger-
$lang['edit']['full_name'] = 'Voller Name';
$lang['edit']['quota_mb'] = 'Speicherplatz (MiB)';
$lang['edit']['sender_acl'] = 'Darf Nachrichten versenden als';
$lang['edit']['sender_acl_disabled'] = '↳ <span class="label label-danger">Absenderprüfung deaktiviert</span>';
$lang['user']['sender_acl_disabled'] = '<span class="label label-danger">Absenderprüfung deaktiviert</span>';
$lang['edit']['previous'] = 'Vorherige Seite';
$lang['edit']['unchanged_if_empty'] = 'Unverändert, wenn leer';
$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s u. Alias-Dom. nicht prüfen';
@ -339,7 +345,22 @@ $lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen';
$lang['edit']['kind'] = 'Art';
$lang['edit']['resource'] = 'Ressource';
$lang['add']['syncjob'] = 'Sync-Job erstellen';
$lang['acl']['spam_alias'] = 'Temporäre E-Mail Aliasse';
$lang['acl']['tls_policy'] = 'Verschlüsselungsrichtlinie';
$lang['acl']['spam_score'] = 'Spam Bewertung';
$lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
$lang['acl']['delimiter_action'] = 'Delimiter Aktionen (tags)';
$lang['acl']['syncjobs'] = 'Sync Jobs';
$lang['acl']['eas_reset'] = 'EAS-Cache zurücksetzen';
$lang['acl']['quarantine'] = 'Quarantäne';
$lang['acl']['login_as'] = 'Einloggen als Mailbox-Benutzer';
$lang['acl']['bcc_maps'] = 'BCC Maps';
$lang['acl']['filters'] = 'Filter';
$lang['acl']['ratelimit'] = 'Rate limit';
$lang['acl']['recipient_maps'] = 'Empfängerumschreibungen';
$lang['acl']['prohibited'] = 'Untersagt durch Richtlinie';
$lang['add']['generate'] = 'generieren';
$lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
$lang['add']['hostname'] = 'Servername';
$lang['add']['port'] = 'Port';

View File

@ -16,6 +16,9 @@ $lang['footer']['delete_these_items'] = 'Please confirm your changes to the foll
$lang['footer']['delete_now'] = 'Delete now';
$lang['footer']['cancel'] = 'Cancel';
$lang['footer']['hibp_nok'] = 'Matched! This is a potentially dangerous password!';
$lang['footer']['hibp_ok'] = 'No match found.';
$lang['danger']['mysql_error'] = "MySQL error: %s";
$lang['danger']['redis_error'] = "Redis error: %s";
$lang['danger']['unknown_tfa_method'] = "Unknown TFA method";
@ -43,6 +46,7 @@ $lang['danger']['domain_cannot_match_hostname'] = "Domain cannot match hostname"
$lang['warning']['domain_added_sogo_failed'] = "Added domain but failed to restart SOGo, please check your server logs.";
$lang['danger']['rl_timeframe'] = "Rate limit time frame is incorrect";
$lang['success']['rl_saved'] = "Rate limit for object %s saved";
$lang['success']['acl_saved'] = "ACL for object %s saved";
$lang['success']['deleted_syncjobs'] = "Deleted syncjobs: %s";
$lang['success']['deleted_syncjob'] = "Deleted syncjob ID %s";
$lang['success']['delete_filters'] = "Deleted filters: %s";
@ -342,6 +346,7 @@ $lang['edit']['full_name'] = 'Full name';
$lang['edit']['quota_mb'] = 'Quota (MiB)';
$lang['edit']['sender_acl'] = 'Allow to send as';
$lang['edit']['sender_acl_disabled'] = '↳ <span class="label label-danger">Sender check is disabled</span>';
$lang['user']['sender_acl_disabled'] = '<span class="label label-danger">Sender check is disabled</span>';
$lang['edit']['previous'] = 'Previous page';
$lang['edit']['unchanged_if_empty'] = 'If unchanged leave blank';
$lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s (+ alias domains)";
@ -349,6 +354,22 @@ $lang['edit']['multiple_bookings'] = 'Multiple bookings';
$lang['edit']['kind'] = 'Kind';
$lang['edit']['resource'] = 'Resource';
$lang['acl']['spam_alias'] = 'Temporary aliases';
$lang['acl']['tls_policy'] = 'TLS policy';
$lang['acl']['spam_score'] = 'Spam score';
$lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
$lang['acl']['delimiter_action'] = 'Delimiter action';
$lang['acl']['syncjobs'] = 'Sync jobs';
$lang['acl']['eas_reset'] = 'Reset EAS devices';
$lang['acl']['quarantine'] = 'Quarantine';
$lang['acl']['login_as'] = 'Login as mailbox user';
$lang['acl']['bcc_maps'] = 'BCC maps';
$lang['acl']['filters'] = 'Filters';
$lang['acl']['ratelimit'] = 'Rate limit';
$lang['acl']['recipient_maps'] = 'Recipient maps';
$lang['acl']['prohibited'] = 'Prohibited by ACL';
$lang['add']['generate'] = 'generate';
$lang['add']['syncjob'] = 'Add sync job';
$lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!';
$lang['add']['hostname'] = 'Hostname';

View File

@ -214,7 +214,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<table class="table table-striped" id="filter_table"></table>
</div>
<div class="mass-actions-mailbox">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['filters'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="filter_item" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
@ -245,7 +245,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<table class="table table-striped" id="bcc_table"></table>
</div>
<div class="mass-actions-mailbox">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['bcc_maps'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="bcc" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
@ -261,7 +261,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel panel-default <?=($_SESSION['mailcow_cc_role'] == "admin") ?: 'hidden';?>">
<div class="panel-heading">
<?=$lang['mailbox']['recipient_maps'];?> <span class="badge badge-info table-lines"></span>
<div class="btn-group pull-right">
@ -272,12 +272,6 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="table-responsive">
<table class="table table-striped" id="recipient_map_table"></table>
</div>
<?php
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"))
$display = 'block';
else
$display = 'none';
?>
<div class="mass-actions-mailbox" style="display: <?php echo $display; ?>">
<div class="btn-group">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="recipient_map" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
@ -304,9 +298,12 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/mailbox.php';
<?php
$lang_mailbox = json_encode($lang['mailbox']);
echo "var lang = ". $lang_mailbox . ";\n";
echo "var acl = '". json_encode($_SESSION['acl']) . "';\n";
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin';
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false';
echo "var role = '". $role . "';\n";
echo "var is_dual = " . $is_dual . ";\n";
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
?>
</script>

View File

@ -67,7 +67,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label>
<div class="col-sm-10">
<select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domains" size="5" multiple>
<select title="<?=$lang['admin']['search_domain_da'];?>" class="full-width-select" name="domains" size="5" multiple>
<?php
foreach (mailbox('get', 'domains') as $domain) {
echo "<option>".htmlspecialchars($domain)."</option>";
@ -79,7 +79,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" id="password" placeholder="" required>
<input type="password" class="form-control" data-hibp="true" name="password" id="password" placeholder="" required>
</div>
</div>
<div class="form-group">

View File

@ -23,7 +23,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
<div class="col-sm-10">
<select data-live-search="true" id="addSelectDomain" name="domain" id="domain" required>
<select class="full-width-select" data-live-search="true" id="addSelectDomain" name="domain" id="domain" required>
<?php
foreach (mailbox('get', 'domains') as $domain) {
echo "<option>".htmlspecialchars($domain)."</option>";
@ -48,10 +48,9 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?> (<a href="#" class="generate_password"><?=$lang['add']['generate'];?></a>)</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" id="password" placeholder="" required>
(<a href="#" class="generate_password">Generate</a>)
<input type="password" data-hibp="true" class="form-control" name="password" id="password" placeholder="" required>
</div>
</div>
<div class="form-group">

View File

@ -37,7 +37,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="password1"><?=$lang['add']['password'];?></label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password1" id="password1" required>
<input type="password" class="form-control" name="password1" id="password1" data-hibp="true" required>
</div>
</div>
<div class="form-group">
@ -155,7 +155,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
<div class="col-sm-5">
<input type="password" class="form-control" name="user_new_pass" id="user_new_pass" autocomplete="off" required>
<input type="password" data-hibp="true" class="form-control" name="user_new_pass" id="user_new_pass" autocomplete="off" required>
</div>
</div>
<div class="form-group">

View File

@ -18,7 +18,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<table id="quarantinetable" class="table table-striped"></table>
</div>
<div class="mass-actions-quarantine">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="qitems" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['quarantine']['toggle_all'];?></a>
<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">

View File

@ -88,7 +88,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
fclose($fh);
}
}
?>
<div class="container">
<h3><?=$lang['user']['user_settings'];?></h3>
@ -145,7 +144,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['aliases_also_send_as'];?></p>
<p><?=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : $user_get_alias_details['aliases_also_send_as'];?></p>
</div>
</div>
<div class="row">
@ -172,87 +171,71 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?>, <?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
</div>
</div>
<hr>
<?php
($_SESSION['acl']['delimiter_action'] == 0 && $_SESSION['acl']['delimiter_action'] == 0 && $_SESSION['acl']['delimiter_action'] == 0) ? null : '<hr>';
// Show tagging options
if ($_SESSION['acl']['delimiter_action'] == 1):
$get_tagging_options = mailbox('get', 'delimiter_action', $username);
?>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
<div class="col-md-9 col-xs-7">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['delimiter_action'];?>">
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subfolder") ? 'active' : null; ?>"
id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="delimiter_action"
data-api-url='edit/delimiter_action'
data-api-attr='{"tagged_mail_handler":"subfolder"}'><?=$lang['user']['tag_in_subfolder'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subject") ? 'active' : null; ?>"
id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="delimiter_action"
data-api-url='edit/delimiter_action'
data-api-attr='{"tagged_mail_handler":"subject"}'><?=$lang['user']['tag_in_subject'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "none") ? 'active' : null; ?>"
id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="delimiter_action"
data-api-url='edit/delimiter_action'
data-api-attr='{"tagged_mail_handler":"none"}'><?=$lang['user']['tag_in_none'];?></button>
</div>
<p class="help-block"><?=$lang['user']['tag_help_explain'];?></p>
<p class="help-block"><?=$lang['user']['tag_help_example'];?></p>
</div>
</div>
<?php
endif;
// Show TLS policy options
if ($_SESSION['acl']['tls_policy'] == 1):
$get_tls_policy = mailbox('get', 'tls_policy', $username);
?>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tls_policy'];?>:</div>
<div class="col-md-9 col-xs-7">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['tls_policy'];?>">
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_in'] == "1") ? "active" : null;?>"
id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="tls_policy"
data-api-url='edit/tls_policy'
data-api-attr='{"tls_enforce_in":<?=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_in'];?></button>
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_out'] == "1") ? "active" : null;?>"
id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="tls_policy"
data-api-url='edit/tls_policy'
data-api-attr='{"tls_enforce_out":<?=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_out'];?></button>
</div>
<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
</div>
</div>
<?php
endif;
// Rest EAS devices
if ($_SESSION['acl']['eas_reset'] == 1):
?>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
<div class="col-md-9 col-xs-7">
<button class="btn btn-xs btn-default" id="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="eas_cache" data-api-url='delete/eas_cache' href="#"><?=$lang['user']['eas_reset_now'];?></button>
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['eas_reset'];?>" id="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="eas_cache" data-api-url='delete/eas_cache' href="#"><?=$lang['user']['eas_reset_now'];?></button>
<p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
</div>
</div>
<?php
endif;
?>
</div>
</div>
@ -273,11 +256,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
</div>
</div>
</div>
<?php
if ($_SESSION['acl']['spam_alias'] == 1):
?>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_alias'];?>">
<div class="btn-group">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="tla" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
@ -299,9 +280,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
</div>
</div>
</div>
<?php
endif;
?>
</div>
<div role="tabpanel" class="tab-pane" id="Spamfilter">
@ -309,7 +288,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<form class="form-horizontal" role="form" data-id="spam_score" method="post">
<div class="form-group">
<div class="col-lg-6 col-sm-12">
<input name="spam_score" id="spam_score" type="text" style="width: 100%;"
<input data-acl="<?=$_SESSION['acl']['spam_score'];?>" name="spam_score" id="spam_score" type="text" style="width: 100%;"
data-provide="slider"
data-slider-min="1"
data-slider-max="2000"
@ -330,21 +309,17 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<p><?=$lang['user']['spamfilter_hint'];?></p>
</div>
</div>
<?php
if ($_SESSION['acl']['spam_score'] == 1):
?>
<div class="form-group">
<div class="col-sm-10">
<button type="button" class="btn btn-sm btn-success" id="edit_selected"
<button data-acl="<?=$_SESSION['acl']['spam_score'];?>" type="button" class="btn btn-sm btn-success" id="edit_selected"
data-item="<?= htmlentities($username); ?>"
data-id="spam_score"
data-api-url='edit/spam-score'
data-api-attr='{}'><?=$lang['user']['save_changes'];?></button>
</div>
</div>
<?php
endif;
?>
</form>
<hr>
<div class="row">
@ -354,26 +329,22 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="table-responsive">
<table class="table table-striped table-condensed" id="wl_policy_mailbox_table"></table>
</div>
<?php
if ($_SESSION['acl']['spam_policy'] == 1):
?>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_wl_mailbox" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_mailbox" data-api-url='delete/mailbox-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</div>
</div>
<form class="form-inline" data-id="add_wl_policy_mailbox">
<div class="input-group">
<div class="input-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
<span class="input-group-btn">
<button class="btn btn-default" id="add_item" data-id="add_wl_policy_mailbox" data-api-url='add/mailbox-policy' data-api-attr='{"username":<?= json_encode($username); ?>,"object_list":"wl"}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['spamfilter_table_add'];?></button>
</span>
</div>
</form>
<?php
endif;
?>
</div>
<div class="col-sm-6">
<h4><?=$lang['user']['spamfilter_bl'];?></h4>
@ -381,28 +352,22 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="table-responsive">
<table class="table table-striped table-condensed" id="bl_policy_mailbox_table"></table>
</div>
<?php
if ($_SESSION['acl']['spam_policy'] == 1):
?>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_bl_mailbox" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_mailbox" data-api-url='delete/mailbox-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</div>
</div>
<form class="form-inline" data-id="add_bl_policy_mailbox">
<div class="input-group">
<div class="input-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
<input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
<input type="hidden" name="username" value="<?= htmlentities($username) ;?>">
<input type="hidden" name="object_list" value="bl">
<span class="input-group-btn">
<button class="btn btn-default" id="add_item" data-id="add_bl_policy_mailbox" data-api-url='add/mailbox-policy' data-api-attr='{"username":<?= json_encode($username); ?>,"object_list":"bl"}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['spamfilter_table_add'];?></button>
</span>
</div>
</form>
<?php
endif;
?>
</div>
</div>
</div>
@ -411,11 +376,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="table-responsive">
<table class="table table-striped" id="sync_job_table"></table>
</div>
<?php
if ($_SESSION['acl']['syncjobs'] == 1):
?>
<div class="mass-actions-user">
<div class="btn-group">
<div class="btn-group" data-acl="<?=$_SESSION['acl']['syncjobs'];?>">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="syncjob" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
@ -427,9 +390,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addSyncJobModal"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['create_syncjob'];?></a>
</div>
</div>
<?php
endif;
?>
</div>
</div>

View File

@ -136,7 +136,7 @@ services:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.34
image: mailcow/sogo:1.36
build: ./data/Dockerfiles/sogo
environment:
- DBNAME=${DBNAME}
@ -264,6 +264,8 @@ services:
- ./data/assets/ssl/:/etc/ssl/mail/:ro
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
volumes_from:
- sogo-mailcow
ports:
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
@ -328,7 +330,7 @@ services:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
image: mailcow/watchdog:1.19
image: mailcow/watchdog:1.21
# Debug
#command: /watchdog.sh
build: ./data/Dockerfiles/watchdog