mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-11-10 18:41:01 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84828d9531 | ||
|
|
88fbaa459a | ||
|
|
55982ac74b | ||
|
|
81f6f7b002 | ||
|
|
ca07d8d333 | ||
|
|
02552ffefd | ||
|
|
06424670fa | ||
|
|
489db90512 | ||
|
|
641ed63782 | ||
|
|
96a70652c3 | ||
|
|
2ac4b1deae | ||
|
|
1eb6d2e26c | ||
|
|
384e2f6ac1 | ||
|
|
32156a337a | ||
|
|
281cf93db3 | ||
|
|
f399c07c85 | ||
|
|
a693325fe6 | ||
|
|
9ad84eee92 | ||
|
|
b59869b720 | ||
|
|
7515bef66c | ||
|
|
b84ba8ded1 | ||
|
|
4845928e7a | ||
|
|
4ccfedd6b3 | ||
|
|
e8d9315d4a | ||
|
|
d977ddb501 | ||
|
|
e76f5237ed | ||
|
|
c11ed5dd1e | ||
|
|
4ef65fc382 |
No files matched your search
@@ -586,8 +586,8 @@ class DockerApi:
|
||||
if not user or id in ('user=', 'group='):
|
||||
return Response(content=json.dumps({ 'type': 'danger', 'msg': 'user and id required' }, indent=4), media_type="application/json")
|
||||
action = 'doveadm acl set -u "$ACL_USER" -- "$folder" "$ACL_ID" $ACL_RIGHTS' if rights else 'doveadm acl delete -u "$ACL_USER" -- "$folder" "$ACL_ID"'
|
||||
script = ('set -o pipefail; doveadm -f tab mailbox status -u "$ACL_USER" guid "*" | tail -n +2 | cut -f1 | while IFS= read -r folder; do '
|
||||
'case "$folder" in Shared|Shared/*) continue;; esac; ' + action + ' || exit 1; done && doveadm acl recalc -u "$ACL_USER"')
|
||||
script = ('set -o pipefail; doveadm mailbox list -u "$ACL_USER" | while IFS= read -r folder; do '
|
||||
'case "$folder" in Shared/*) continue;; esac; ' + action + ' || exit 1; done && doveadm acl recalc -u "$ACL_USER"')
|
||||
acl_return = container.exec_run(["/bin/bash", "-c", script],
|
||||
environment={ 'ACL_USER': user, 'ACL_ID': id, 'ACL_RIGHTS': ' '.join(rights) })
|
||||
return self.exec_run_handler('generic', acl_return)
|
||||
|
||||
@@ -4,7 +4,7 @@ catch_non_zero() {
|
||||
${CMD} > /dev/null
|
||||
EC=$?
|
||||
if [ ${EC} -ne 0 ]; then
|
||||
echo "Command ${CMD//${REDISPASS}/***} failed to execute, exit code was ${EC}"
|
||||
echo "Command ${CMD} failed to execute, exit code was ${EC}"
|
||||
fi
|
||||
}
|
||||
source /source_env.sh
|
||||
|
||||
@@ -3,8 +3,7 @@ LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ENV PIP_BREAK_SYSTEM_PACKAGES=1
|
||||
|
||||
RUN apk upgrade --no-cache && \
|
||||
apk add --no-cache nginx \
|
||||
RUN apk add --no-cache nginx \
|
||||
python3 \
|
||||
py3-pip && \
|
||||
pip install --upgrade pip && \
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /src
|
||||
ENV CGO_ENABLED=0 \
|
||||
GO111MODULE=on \
|
||||
NOOPT=1 \
|
||||
VERSION=1.14.5
|
||||
VERSION=1.14.0
|
||||
|
||||
RUN git clone --branch v${VERSION} https://github.com/Zuplu/postfix-tlspol && \
|
||||
cd /src/postfix-tlspol && \
|
||||
|
||||
@@ -277,34 +277,9 @@ if [[ ! -z ${SPAMHAUS_DQS_KEY} ]]; then
|
||||
}
|
||||
}
|
||||
"DBL" {
|
||||
# full copy of the DBL rule from modules.d/rbl.conf, pointed at DQS:
|
||||
# the override include replaces the stock rule instead of merging into it
|
||||
# override the defaults for DBL defined in modules.d/rbl.conf
|
||||
rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
|
||||
disable_monitoring = true;
|
||||
checks = ["emails", "dkim", "helo", "rdns", "replyto", "urls", "content_urls"];
|
||||
no_ip = true;
|
||||
ignore_defaults = true;
|
||||
exclude_users = false;
|
||||
emails_domainonly = true;
|
||||
selector = {
|
||||
from_smtp = "from('smtp'):domain";
|
||||
from_mime = "from('mime'):domain";
|
||||
mid = "header(Message-Id).regexp('@([^\.]+\.[^>]+)').last";
|
||||
}
|
||||
returncodes = {
|
||||
DBL_SPAM = "127.0.1.2";
|
||||
DBL_PHISH = "127.0.1.4";
|
||||
DBL_MALWARE = "127.0.1.5";
|
||||
DBL_BOTNET = "127.0.1.6";
|
||||
DBL_ABUSE = "127.0.1.102";
|
||||
DBL_ABUSE_REDIR = "127.0.1.103";
|
||||
DBL_ABUSE_PHISH = "127.0.1.104";
|
||||
DBL_ABUSE_MALWARE = "127.0.1.105";
|
||||
DBL_ABUSE_BOTNET = "127.0.1.106";
|
||||
DBL_PROHIBIT = "127.0.1.255";
|
||||
DBL_BLOCKED_OPENRESOLVER = "127.255.255.254";
|
||||
DBL_BLOCKED = "127.255.255.255";
|
||||
}
|
||||
}
|
||||
"ZRD" {
|
||||
ignore_defaults = true;
|
||||
|
||||
@@ -116,8 +116,8 @@ if ($result === false){
|
||||
exit;
|
||||
}
|
||||
if ($result) {
|
||||
error_log('MAILCOWAUTH: User auth for user ' . $post['username'] . " with service " . $service . " from IP " . $client_ip);
|
||||
set_sasl_log($post['username'], $client_ip, $service);
|
||||
error_log('MAILCOWAUTH: User auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $client_ip);
|
||||
set_sasl_log($post['username'], $client_ip, $post['service']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,11 +94,11 @@ location / {
|
||||
}
|
||||
|
||||
location /qhandler {
|
||||
rewrite ^/qhandler/([^/]+)/(.+?)/?$ /qhandler.php?action=$1&hash=$2;
|
||||
rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2;
|
||||
}
|
||||
|
||||
location /edit {
|
||||
rewrite ^/edit/([^/]+)/(.+?)/?$ /edit.php?$1=$2;
|
||||
rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2;
|
||||
}
|
||||
|
||||
location ~ ^/api/v1/(.*)$ {
|
||||
@@ -187,12 +187,6 @@ location /sogo-auth-verify {
|
||||
}
|
||||
|
||||
location ^~ /Microsoft-Server-ActiveSync {
|
||||
# Outlook for iOS/Android (Microsoft's sync service) sends EAS commands without credentials
|
||||
# and only authenticates after a 401 Basic challenge. SOGo answers those with 403, so challenge here.
|
||||
if ($http_authorization = "") {
|
||||
add_header WWW-Authenticate 'Basic realm="SOGo"' always;
|
||||
return 401;
|
||||
}
|
||||
auth_request /sogo-auth-verify;
|
||||
auth_request_set $user $upstream_http_x_user;
|
||||
auth_request_set $auth $upstream_http_x_auth;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
MX_IMPLICIT {
|
||||
expression = "MX_GOOD & MX_MISSING";
|
||||
score = -0.01;
|
||||
}
|
||||
VIRUS_FOUND {
|
||||
expression = "CLAM_VIRUS & !MAILCOW_WHITE";
|
||||
score = 2000.0;
|
||||
|
||||
@@ -23,6 +23,24 @@ symbols {
|
||||
}
|
||||
}
|
||||
|
||||
group "MX" {
|
||||
"MX_INVALID" {
|
||||
score = 0.5;
|
||||
description = "No connectable MX";
|
||||
one_shot = true;
|
||||
}
|
||||
"MX_MISSING" {
|
||||
score = 2.0;
|
||||
description = "No MX record";
|
||||
one_shot = true;
|
||||
}
|
||||
"MX_GOOD" {
|
||||
score = -0.01;
|
||||
description = "MX was ok";
|
||||
one_shot = true;
|
||||
}
|
||||
}
|
||||
|
||||
group "reputation" {
|
||||
symbols = {
|
||||
"IP_REPUTATION_HAM" {
|
||||
|
||||
@@ -5,6 +5,12 @@ RCPT_MAILCOW_DOMAIN {
|
||||
symbols_set = ["RCPT_MAILCOW_DOMAIN"];
|
||||
}
|
||||
|
||||
WHITELISTED_FWD_HOST {
|
||||
type = "ip";
|
||||
map = "redis://WHITELISTED_FWD_HOST";
|
||||
symbols_set = ["WHITELISTED_FWD_HOST"];
|
||||
}
|
||||
|
||||
BULK_HEADER {
|
||||
type = "content";
|
||||
map = "${LOCAL_CONFDIR}/custom/bulk_header.map";
|
||||
@@ -145,6 +151,20 @@ BAD_LANG {
|
||||
score = 5.0;
|
||||
}
|
||||
|
||||
BAZAAR_ABUSE_CH {
|
||||
type = "selector";
|
||||
selector = "attachments(hex,md5)";
|
||||
map = "https://bazaar.abuse.ch/export/txt/md5/recent/";
|
||||
score = 10.0;
|
||||
}
|
||||
|
||||
URLHAUS_ABUSE_CH {
|
||||
type = "selector";
|
||||
selector = "urls";
|
||||
map = "https://urlhaus.abuse.ch/downloads/text_online/";
|
||||
score = 10.0;
|
||||
}
|
||||
|
||||
SMTP_LIMITED_ACCESS {
|
||||
type = "user";
|
||||
map = "redis://SMTP_LIMITED_ACCESS";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
timeout = 8.0;
|
||||
symbol_bad_mx = "MX_INVALID";
|
||||
symbol_no_mx = "MX_MISSING";
|
||||
symbol_good_mx = "MX_GOOD";
|
||||
expire = 86400;
|
||||
key_prefix = "rmx";
|
||||
|
||||
@@ -9,5 +9,3 @@ disable_monitoring = true;
|
||||
# instead of silently accepting the message without further processing.
|
||||
soft_reject_on_timeout = true;
|
||||
local_addrs = /etc/rspamd/custom/mailcow_networks.map;
|
||||
# Must not be lower than message_size_limit in postfix main.cf
|
||||
max_message = 134217728;
|
||||
@@ -362,61 +362,6 @@ rspamd_config:register_symbol({
|
||||
priority = 19
|
||||
})
|
||||
|
||||
rspamd_config:register_symbol({
|
||||
name = 'WHITELISTED_FWD_HOST',
|
||||
type = 'prefilter',
|
||||
score = 0.0,
|
||||
callback = function(task)
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local redis_params = rspamd_parse_redis_server('fwd_host')
|
||||
local ip = task:get_from_ip()
|
||||
|
||||
if ip == nil or not ip:is_valid() then
|
||||
return false
|
||||
end
|
||||
|
||||
local from_ip_string = tostring(ip)
|
||||
local ip_check_table = {'WHITELISTED_FWD_HOST', from_ip_string}
|
||||
|
||||
local maxbits = 128
|
||||
local minbits = 7
|
||||
if ip:get_version() == 4 then
|
||||
maxbits = 32
|
||||
minbits = 8
|
||||
end
|
||||
for i=maxbits,minbits,-1 do
|
||||
table.insert(ip_check_table, ip:apply_mask(i):to_string() .. "/" .. i)
|
||||
end
|
||||
|
||||
local function fwd_host_cb(err, data)
|
||||
if err or type(data) ~= 'table' then
|
||||
rspamd_logger.infox(rspamd_config, "fwd_host query request for ip %s returned invalid or empty data (\"%s\") or error (\"%s\")", ip, data, err)
|
||||
return
|
||||
end
|
||||
for _,v in pairs(data) do
|
||||
if type(v) == 'string' then
|
||||
task:insert_result('WHITELISTED_FWD_HOST', 1.0, from_ip_string)
|
||||
task:set_flag('pass_all')
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local redis_ret = rspamd_redis_make_request(task,
|
||||
redis_params, -- connect params
|
||||
'WHITELISTED_FWD_HOST', -- hash key
|
||||
false, -- is write
|
||||
fwd_host_cb, --callback
|
||||
'HMGET', -- command
|
||||
ip_check_table -- arguments
|
||||
)
|
||||
if not redis_ret then
|
||||
rspamd_logger.infox(rspamd_config, "cannot check fwd_host redis map")
|
||||
end
|
||||
end,
|
||||
priority = 18
|
||||
})
|
||||
|
||||
rspamd_config:register_symbol({
|
||||
name = 'TLS_HEADER',
|
||||
type = 'postfilter',
|
||||
@@ -445,8 +390,9 @@ rspamd_config:register_symbol({
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local redis_params = rspamd_parse_redis_server('taghandler')
|
||||
local rspamd_http = require "rspamd_http"
|
||||
local rcpts = task:get_recipients({'smtp', 'orig'})
|
||||
local rcpts = task:get_recipients('smtp')
|
||||
local lua_util = require "lua_util"
|
||||
local tagged_rcpt = task:get_symbol("TAGGED_RCPT")
|
||||
|
||||
local function remove_moo_tag()
|
||||
local moo_tag_header = task:get_header('X-Moo-Tag', false)
|
||||
@@ -471,9 +417,8 @@ rspamd_config:register_symbol({
|
||||
|
||||
-- Check if recipient has a tag (contains '+')
|
||||
local tag = nil
|
||||
local base_user, tag_part = rcpt_user:match('^([^%+]+)%+(.+)$')
|
||||
if base_user and tag_part then
|
||||
tag = tag_part
|
||||
if tagged_rcpt ~= nil then
|
||||
tag = tagged_rcpt
|
||||
rspamd_logger.infox("TAG_MOO: found tag in recipient: %s (base: %s, tag: %s)", rcpt_addr, base_user, tag)
|
||||
end
|
||||
|
||||
@@ -553,7 +498,8 @@ rspamd_config:register_symbol({
|
||||
else
|
||||
rspamd_logger.infox("TAG_MOO: user wants subject modified for tagged mail")
|
||||
local sbj = task:get_header('Subject') or ''
|
||||
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
|
||||
local tag_value = tag[1] and tag[1].options and tag[1].options[1] or ''
|
||||
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag_value .. '] ' .. sbj)) .. '?='
|
||||
task:set_milter_reply({
|
||||
remove_headers = {
|
||||
['Subject'] = 1,
|
||||
@@ -961,9 +907,6 @@ rspamd_config:register_symbol({
|
||||
|
||||
if not seen_cte and rewrite.need_rewrite_ct then
|
||||
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', new_cte)
|
||||
task:set_milter_reply({
|
||||
add_headers = {['Content-Transfer-Encoding'] = new_cte}
|
||||
})
|
||||
end
|
||||
|
||||
-- End of headers
|
||||
|
||||
@@ -109,7 +109,7 @@ foreach ($rcpts as $rcpt) {
|
||||
}
|
||||
|
||||
// Skip if domain is excluded
|
||||
if (is_array($exclude_domains) && in_array($parsed_rcpt['domain'], $exclude_domains)) {
|
||||
if (in_array($parsed_rcpt['domain'], $exclude_domains)) {
|
||||
error_log(sprintf("QUARANTINE: Skipped domain %s", $parsed_rcpt['domain']) . PHP_EOL);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ if(file_exists('inc/vars.local.inc.php')) {
|
||||
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
||||
|
||||
header('Content-type: application/json');
|
||||
if (strtolower($_GET['Protocol']) == 'activesync' && $autodiscover_config['autodiscoverType'] == 'activesync' && getenv('SKIP_SOGO') != "y") {
|
||||
if (strtolower($_GET['Protocol']) == 'activesync' && getenv('SKIP_SOGO') != "y") {
|
||||
echo '{"Protocol":"ActiveSync","Url":"' . $autodiscover_config['activesync']['url'] . '"}';
|
||||
}
|
||||
elseif (strtolower($_GET['Protocol']) == 'autodiscoverv1') {
|
||||
|
||||
@@ -31,7 +31,7 @@ error_reporting(0);
|
||||
|
||||
$data = trim(file_get_contents("php://input"));
|
||||
|
||||
if (strpos($data, 'autodiscover/outlook/responseschema') !== false && $autodiscover_config['autodiscoverType'] == 'activesync') {
|
||||
if (strpos($data, 'autodiscover/outlook/responseschema') !== false) {
|
||||
$autodiscover_config['autodiscoverType'] = 'imap';
|
||||
if ($autodiscover_config['useEASforOutlook'] == 'yes' &&
|
||||
// Office for macOS does not support EAS
|
||||
|
||||
@@ -601,13 +601,6 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
|
||||
|
||||
// validate mailcow_password
|
||||
$mailcow_password = $user_res['attributes']['mailcow_password'][0];
|
||||
// Keycloak returns binary LDAP attributes like userPassword Base64 encoded
|
||||
if (substr($mailcow_password, 0, 1) != '{') {
|
||||
$decoded_password = base64_decode($mailcow_password, true);
|
||||
if ($decoded_password !== false && substr($decoded_password, 0, 1) == '{') {
|
||||
$mailcow_password = $decoded_password;
|
||||
}
|
||||
}
|
||||
if (!verify_hash($mailcow_password, $pass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,13 +35,6 @@ function fwdhost($_action, $_data = null) {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$hosts = array_unique(array_map(function ($host) {
|
||||
$net = explode('/', $host, 2);
|
||||
if (filter_var($net[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$net[0] = inet_ntop(inet_pton($net[0]));
|
||||
}
|
||||
return implode('/', $net);
|
||||
}, $hosts));
|
||||
foreach ($hosts as $host) {
|
||||
try {
|
||||
$redis->hSet('WHITELISTED_FWD_HOST', $host, $source);
|
||||
|
||||
@@ -1416,11 +1416,11 @@ function set_tfa($_data) {
|
||||
// Block TFA removal if force_tfa policy is active
|
||||
$is_forced_tfa = false;
|
||||
if ($_SESSION['mailcow_cc_role'] === 'user') {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
} else {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
}
|
||||
@@ -1687,11 +1687,11 @@ function unset_tfa_key($_data) {
|
||||
// Block key removal if force_tfa policy is active
|
||||
$is_forced_tfa = false;
|
||||
if ($_SESSION['mailcow_cc_role'] === 'user') {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
} else {
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_VALUE(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?");
|
||||
$stmt_check->execute(array($username));
|
||||
$is_forced_tfa = ($stmt_check->fetchColumn() == '1');
|
||||
}
|
||||
@@ -3106,17 +3106,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
case "get-keycloak-admin-token":
|
||||
// get access_token for service account of mailcow client
|
||||
if ($iam_settings['authsource'] !== 'keycloak') return false;
|
||||
// only introspect a cached access_token that is not (about to be) expired,
|
||||
// introspecting an expired token makes Keycloak log INTROSPECT_TOKEN_ERROR
|
||||
$token_not_expired = false;
|
||||
if (!empty($iam_settings['access_token'])) {
|
||||
$token_parts = explode('.', $iam_settings['access_token']);
|
||||
if (count($token_parts) === 3) {
|
||||
$token_payload = json_decode(base64_decode(strtr($token_parts[1], '-_', '+/')), true);
|
||||
$token_not_expired = is_array($token_payload) && isset($token_payload['exp']) && intval($token_payload['exp']) > time() + 30;
|
||||
}
|
||||
}
|
||||
if ($token_not_expired) {
|
||||
if (isset($iam_settings['access_token'])) {
|
||||
// check if access_token is valid
|
||||
$url = "{$iam_settings['server_url']}/realms/{$iam_settings['realm']}/protocol/openid-connect/token/introspect";
|
||||
$req = http_build_query(array(
|
||||
@@ -3167,8 +3157,6 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
||||
':key' => 'access_token',
|
||||
':value' => $res['access_token']
|
||||
));
|
||||
// keep the cached token in sync, so subsequent calls in the same run reuse it
|
||||
$iam_settings['access_token'] = $res['access_token'];
|
||||
return $res['access_token'];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2524,10 +2524,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -2696,10 +2692,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -3178,10 +3170,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':username' => $username,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = NOW() WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
@@ -4000,9 +3988,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(':goto' => '(^|,)'.preg_quote($_data, '/').'($|,)'));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
if (!hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['address'])) {
|
||||
continue;
|
||||
}
|
||||
// Keep old array for backward compatibility
|
||||
$data['fixed_sender_aliases'][] = $row['address'];
|
||||
// Split into allowed/blocked for proper display
|
||||
@@ -4938,9 +4923,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
else if ($SaslLogs['service'] == 'SSO') {
|
||||
$last_sso_login = strtotime($SaslLogs['datetime']);
|
||||
}
|
||||
else if ($SaslLogs['service'] == 'SOGO') {
|
||||
$last_sogo_login = strtotime($SaslLogs['datetime']);
|
||||
}
|
||||
}
|
||||
if (!isset($last_imap_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_imap_login = 0;
|
||||
@@ -4954,14 +4936,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
if (!isset($last_sso_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_sso_login = 0;
|
||||
}
|
||||
if (!isset($last_sogo_login) || $GLOBALS['SHOW_LAST_LOGIN'] === false) {
|
||||
$last_sogo_login = 0;
|
||||
}
|
||||
$mailboxdata['last_imap_login'] = $last_imap_login;
|
||||
$mailboxdata['last_smtp_login'] = $last_smtp_login;
|
||||
$mailboxdata['last_pop3_login'] = $last_pop3_login;
|
||||
$mailboxdata['last_sso_login'] = $last_sso_login;
|
||||
$mailboxdata['last_sogo_login'] = $last_sogo_login;
|
||||
|
||||
if (!isset($_extra) || $_extra != 'reduced') {
|
||||
$rl = ratelimit('get', 'mailbox', $_data);
|
||||
@@ -4999,16 +4977,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(':username' => $_data));
|
||||
$sender_acl_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($sender_acl_rows as $sender_acl_row) {
|
||||
$send_as = $sender_acl_row['send_as'];
|
||||
if ($_SESSION['mailcow_cc_role'] == 'domainadmin' && $send_as != '*') {
|
||||
$in_scope = (substr($send_as, 0, 1) == '@')
|
||||
? hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr($send_as, 1))
|
||||
: hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $send_as);
|
||||
if (!$in_scope) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$mailboxdata['sender_acl'][] = $send_as;
|
||||
$mailboxdata['sender_acl'][] = $sender_acl_row['send_as'];
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `tag_name`
|
||||
@@ -5947,12 +5916,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `modified` = NOW() WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6005,12 +5968,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':username' => $username,
|
||||
':tag_name' => $tag,
|
||||
));
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = NOW() WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -715,6 +715,10 @@ jQuery(function($){
|
||||
$.each(json, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
|
||||
|
||||
item.attributes.def_quota_for_mbox = humanFileSize(item.attributes.def_quota_for_mbox);
|
||||
item.attributes.max_quota_for_mbox = humanFileSize(item.attributes.max_quota_for_mbox);
|
||||
item.attributes.max_quota_for_domain = humanFileSize(item.attributes.max_quota_for_domain);
|
||||
|
||||
item.template = escapeHtml(item.template);
|
||||
if (item.attributes.rl_frame === "s"){
|
||||
item.attributes.rl_frame = lang_rl.second;
|
||||
@@ -797,25 +801,16 @@ jQuery(function($){
|
||||
title: lang.mailbox_defquota,
|
||||
data: 'attributes.def_quota_for_mbox',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.max_quota,
|
||||
data: 'attributes.max_quota_for_mbox',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.domain_quota_total,
|
||||
data: 'attributes.max_quota_for_domain',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.gal,
|
||||
@@ -937,7 +932,6 @@ jQuery(function($){
|
||||
item.last_mail_login = (item.attributes.imap_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">IMAP @ ' + unix_time_format(Number(item.last_imap_login)) + '</div><br>' : '') +
|
||||
(item.attributes.pop3_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">POP3 @ ' + unix_time_format(Number(item.last_pop3_login)) + '</div><br>' : '') +
|
||||
(item.attributes.smtp_access == 1 ? '<div class="text-start badge bg-info mb-2" style="min-width: 70px;">SMTP @ ' + unix_time_format(Number(item.last_smtp_login)) + '</div><br>' : '') +
|
||||
'<div class="text-start badge bg-info mb-2" style="min-width: 70px;">SOGO @ ' + unix_time_format(Number(item.last_sogo_login)) + '</div><br>' +
|
||||
'<div class="text-start badge bg-info" style="min-width: 70px;">SSO @ ' + unix_time_format(Number(item.last_sso_login)) + '</div>';
|
||||
/*
|
||||
if (!item.rl) {
|
||||
@@ -1250,6 +1244,8 @@ jQuery(function($){
|
||||
item.attributes.rl_value = (!item.attributes.rl_value) ? "∞" : escapeHtml(item.attributes.rl_value);
|
||||
item.attributes.ratelimit = item.attributes.rl_value + " " + item.attributes.rl_frame;
|
||||
|
||||
item.attributes.quota = humanFileSize(item.attributes.quota);
|
||||
|
||||
item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_in == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_out == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.pop3_access == 1 ? '1' : '0') + '</span></i>';
|
||||
@@ -1336,9 +1332,6 @@ jQuery(function($){
|
||||
title: lang.domain_quota,
|
||||
data: 'attributes.quota',
|
||||
defaultContent: '',
|
||||
render: function (data, type) {
|
||||
return type === 'sort' || type === 'type' ? data : humanFileSize(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.tls_enforce_in,
|
||||
|
||||
@@ -1087,14 +1087,10 @@ if (isset($_GET['query'])) {
|
||||
$data['mailboxes'][$mailbox][] = $mailbox;
|
||||
$user_alias_details = user_get_alias_details($mailbox);
|
||||
foreach ($user_alias_details['direct_aliases'] as $k => $v) {
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr(strrchr($k, '@'), 1))) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
foreach ($user_alias_details['shared_aliases'] as $k => $v) {
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], substr(strrchr($k, '@'), 1))) {
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
$data['mailboxes'][$mailbox][] = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1074,6 +1074,7 @@
|
||||
"verified_webauthn_login": "Inicio de sesión WebAuthn verificado",
|
||||
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado",
|
||||
"admin_added": "Se agregó el administrador %s",
|
||||
"admin_api_modified": "Se guardaron los cambios en la API",
|
||||
"admin_removed": "Se eliminó el administrador %s",
|
||||
"app_links": "Se guardaron los cambios en los enlaces de aplicaciones",
|
||||
"app_passwd_added": "Se agregó una nueva contraseña de aplicación",
|
||||
@@ -1332,6 +1333,10 @@
|
||||
"sogo_profile_reset_help": "Esto destruirá el perfil de SOGo del usuario y <b>eliminará de forma irrecuperable todos sus contactos y datos de calendario</b>.",
|
||||
"spam_score_reset": "Restablecer al valor predeterminado del servidor",
|
||||
"spamfilter_table_domain_policy": "n/d (política del dominio)",
|
||||
"syncjob_check_log": "Revisar registro",
|
||||
"syncjob_EX_OK": "Correcto",
|
||||
"syncjob_EXIT_OVERQUOTA": "El buzón de destino superó su cuota",
|
||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Usuario o contraseña incorrectos",
|
||||
"tfa_info": "La autenticación de dos factores ayuda a proteger su cuenta. Si la activa, necesitará contraseñas de aplicación para iniciar sesión en aplicaciones o servicios que no admiten la autenticación de dos factores (p. ej., clientes de correo)."
|
||||
},
|
||||
"warning": {
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
|
||||
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
|
||||
"tags": "Tag",
|
||||
"dry": "Simula sincronizzazione",
|
||||
"internal": "Interno",
|
||||
"internal_info": "Gli alias interni sono accessibili solo dal proprio dominio o dai domini alias.",
|
||||
"sender_allowed": "Consenti l'invio con questo alias"
|
||||
|
||||
@@ -97,11 +97,9 @@
|
||||
<div class="col-sm-10">
|
||||
<select data-live-search="true" data-width="100%" style="width:100%" id="editSelectSenderACL" name="sender_acl" size="10" multiple>
|
||||
{% for domain in sender_acl_handles.sender_acl_domains.ro %}
|
||||
{% if domain == '*' or mailcow_cc_role == 'admin' %}
|
||||
<option data-subtext="Admin" value="{{ domain }}" disabled selected>
|
||||
{{ lang.edit.dont_check_sender_acl|format(domain) }}
|
||||
</option>
|
||||
{% endif %}
|
||||
<option data-subtext="Admin" value="{{ domain }}" disabled selected>
|
||||
{{ lang.edit.dont_check_sender_acl|format(domain) }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% for domain in sender_acl_handles.sender_acl_domains.rw %}
|
||||
<option value="{{ domain }}" selected>
|
||||
|
||||
+5
-5
@@ -68,7 +68,7 @@ services:
|
||||
- redis
|
||||
|
||||
clamd-mailcow:
|
||||
image: ghcr.io/mailcow/clamd:1.4.6-2
|
||||
image: ghcr.io/mailcow/clamd:1.4.6-1
|
||||
restart: always
|
||||
depends_on:
|
||||
unbound-mailcow:
|
||||
@@ -348,7 +348,7 @@ services:
|
||||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: ghcr.io/mailcow/postfix:3.10.13-1
|
||||
image: ghcr.io/mailcow/postfix:3.10.12-1
|
||||
depends_on:
|
||||
mysql-mailcow:
|
||||
condition: service_started
|
||||
@@ -391,7 +391,7 @@ services:
|
||||
- postfix
|
||||
|
||||
postfix-tlspol-mailcow:
|
||||
image: ghcr.io/mailcow/postfix-tlspol:1.14.5
|
||||
image: ghcr.io/mailcow/postfix-tlspol:1.14.0
|
||||
depends_on:
|
||||
unbound-mailcow:
|
||||
condition: service_healthy
|
||||
@@ -428,7 +428,7 @@ services:
|
||||
- php-fpm-mailcow
|
||||
- sogo-mailcow
|
||||
- rspamd-mailcow
|
||||
image: ghcr.io/mailcow/nginx:1.30.6-3
|
||||
image: ghcr.io/mailcow/nginx:1.30.6-2
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
environment:
|
||||
@@ -612,7 +612,7 @@ services:
|
||||
- watchdog
|
||||
|
||||
dockerapi-mailcow:
|
||||
image: ghcr.io/mailcow/dockerapi:2.16
|
||||
image: ghcr.io/mailcow/dockerapi:2.15
|
||||
security_opt:
|
||||
- label=disable
|
||||
restart: always
|
||||
|
||||
Reference in new issue
Block a user