1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2026-06-14 02:20:28 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
FreddleSpl0it 32014dfcec [Web] Add password change acl 2026-05-06 11:01:15 +02:00
35 changed files with 223 additions and 1663 deletions
@@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v10.3.0
uses: actions/stale@v10.2.0
with:
repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
with:
fetch-depth: 0
- name: Run the Action
uses: devops-infra/action-pull-request@v1.3.0
uses: devops-infra/action-pull-request@v1.0.2
with:
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM nginx:1.30.2-alpine
FROM nginx:alpine
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ENV PIP_BREAK_SYSTEM_PACKAGES=1
+2 -2
View File
@@ -7,13 +7,13 @@ ARG APCU_PECL_VERSION=5.1.28
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.8.1
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.2.0
ARG MAILPARSE_PECL_VERSION=3.1.9
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MEMCACHED_PECL_VERSION=3.4.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.3.0
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.10.1
ARG COMPOSER_VERSION=2.9.5
RUN apk add -U --no-cache autoconf \
aspell-dev \
+1 -1
View File
@@ -2,7 +2,7 @@ FROM debian:trixie-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_4.1.0-1~e2b0b18
ARG RSPAMD_VER=rspamd_3.14.3-1~236eb65
ARG CODENAME=trixie
ENV LC_ALL=C
+3 -3
View File
@@ -1,6 +1,6 @@
# SOGo built from source to enable security patch application
# Repository: https://github.com/Alinto/sogo
# Version: SOGo-5.12.9
# Version: SOGo-5.12.4
#
# Applied security patches:
# -
@@ -12,8 +12,8 @@ FROM debian:bookworm
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG SOGO_VERSION=SOGo-5.12.9
ARG SOPE_VERSION=SOPE-5.12.9
ARG SOGO_VERSION=SOGo-5.12.5
ARG SOPE_VERSION=SOPE-5.12.5
# Security patches to apply (space-separated commit hashes)
ARG SOGO_SECURITY_PATCHES=""
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
+1 -4
View File
@@ -2,14 +2,11 @@ FROM alpine:3.23
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
# install unbound from alpine:edge to get security patches
RUN apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main unbound
# install other packages from regular alpine stable repo
RUN apk add --update --no-cache \
curl \
bind-tools \
coreutils \
unbound \
bash \
openssl \
drill \
+68 -8
View File
@@ -1,8 +1,7 @@
# Whitelist generated by Postwhite v3.4 on Mon Jun 1 00:52:17 UTC 2026
# Whitelist generated by Postwhite v3.4 on Wed Apr 1 00:33:31 UTC 2026
# https://github.com/stevejenkins/postwhite/
# 2186 total rules
# 2246 total rules
2a00:1450:4000::/36 permit
2a00:1450:4864::/56 permit
2a01:111:f400::/48 permit
2a01:111:f403:2800::/53 permit
2a01:111:f403:8000::/51 permit
@@ -32,7 +31,6 @@
2a02:a60:0:5::/64 permit
2a0f:f640::/56 permit
2c0f:fb50:4000::/36 permit
2c0f:fb50:4864::/56 permit
2.207.151.32/27 permit
2.207.151.53 permit
2.207.217.30 permit
@@ -56,8 +54,14 @@
8.25.194.0/23 permit
8.25.196.0/23 permit
8.36.116.0/24 permit
8.39.54.0/23 permit
8.39.54.250/31 permit
8.39.144.0/24 permit
8.40.222.0/23 permit
8.40.222.250/31 permit
12.130.86.238 permit
13.107.213.38 permit
13.107.246.38 permit
13.108.16.0/20 permit
13.110.208.0/21 permit
13.110.209.0/24 permit
@@ -67,6 +71,7 @@
13.111.191.0/24 permit
13.216.7.111 permit
13.216.54.180 permit
13.247.164.219 permit
15.200.21.50 permit
15.200.44.248 permit
15.200.201.185 permit
@@ -170,6 +175,7 @@
34.215.104.144 permit
34.218.115.239 permit
34.225.212.172 permit
34.241.242.183 permit
35.83.148.184 permit
35.155.198.111 permit
35.158.23.94 permit
@@ -193,6 +199,7 @@
40.233.64.216 permit
40.233.83.78 permit
40.233.88.28 permit
43.239.212.33 permit
44.206.138.57 permit
44.210.169.44 permit
44.217.45.156 permit
@@ -272,9 +279,9 @@
50.56.130.221 permit
50.56.130.222 permit
50.112.246.219 permit
51.83.17.38 permit
52.1.14.157 permit
52.5.230.59 permit
52.6.74.205 permit
52.12.53.23 permit
52.13.214.179 permit
52.26.1.71 permit
@@ -331,6 +338,7 @@
54.244.54.130 permit
54.244.242.0/24 permit
54.255.61.23 permit
56.124.6.228 permit
57.103.64.0/18 permit
57.129.93.249 permit
62.13.128.0/24 permit
@@ -397,6 +405,7 @@
65.110.161.77 permit
65.123.29.213 permit
65.123.29.220 permit
65.154.166.0/24 permit
65.212.180.36 permit
66.102.0.0/20 permit
66.119.150.192/26 permit
@@ -1210,6 +1219,9 @@
99.78.197.208/28 permit
103.9.96.0/22 permit
103.28.42.0/24 permit
103.84.217.15 permit
103.84.217.238 permit
103.89.75.238 permit
103.151.192.0/23 permit
103.168.172.128/27 permit
103.237.104.0/22 permit
@@ -1370,6 +1382,9 @@
117.120.16.0/21 permit
119.42.242.52/31 permit
119.42.242.156 permit
121.244.91.48 permit
121.244.91.52 permit
122.15.156.182 permit
123.126.78.64/29 permit
124.108.96.24/31 permit
124.108.96.28/31 permit
@@ -1452,7 +1467,21 @@
134.170.141.64/26 permit
134.170.143.0/24 permit
134.170.174.0/24 permit
135.84.80.0/24 permit
135.84.81.0/24 permit
135.84.82.0/24 permit
135.84.83.0/24 permit
135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.162.0/24 permit
136.143.176.0/24 permit
136.143.177.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
136.143.188.0/24 permit
136.143.190.0/23 permit
136.146.128.0/20 permit
136.147.128.0/20 permit
136.147.135.0/24 permit
@@ -1472,6 +1501,7 @@
139.138.46.219 permit
139.138.57.55 permit
139.138.58.119 permit
139.167.79.86 permit
139.177.108.0/25 permit
139.180.17.0/24 permit
140.238.148.191 permit
@@ -1521,14 +1551,12 @@
147.243.128.26 permit
148.105.0.0/16 permit
148.105.8.0/21 permit
148.116.32.128/25 permit
149.72.0.0/16 permit
149.72.234.184 permit
149.72.248.236 permit
149.97.173.180 permit
149.118.160.128/25 permit
150.136.21.199 permit
150.171.109.118 permit
150.230.98.160 permit
151.145.38.14 permit
152.67.105.195 permit
@@ -1605,6 +1633,11 @@
164.152.25.241 permit
164.177.132.168/30 permit
165.1.100.0/25 permit
165.173.128.0/24 permit
165.173.180.1 permit
165.173.180.250/31 permit
165.173.182.250/31 permit
165.173.189.205 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
@@ -1637,6 +1670,18 @@
168.245.12.252 permit
168.245.46.9 permit
168.245.127.231 permit
169.148.129.0/24 permit
169.148.131.0/24 permit
169.148.138.0/24 permit
169.148.142.10 permit
169.148.142.33 permit
169.148.144.0/25 permit
169.148.144.10 permit
169.148.146.0/23 permit
169.148.175.3 permit
169.148.179.3 permit
169.148.188.0/24 permit
169.148.188.182 permit
170.9.232.254 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
@@ -1842,7 +1887,16 @@
199.16.156.0/22 permit
199.33.145.1 permit
199.33.145.32 permit
199.34.22.36 permit
199.59.148.0/22 permit
199.67.80.2 permit
199.67.80.20 permit
199.67.82.2 permit
199.67.82.20 permit
199.67.84.0/24 permit
199.67.86.0/24 permit
199.67.88.0/24 permit
199.67.90.0/24 permit
199.101.161.130 permit
199.101.162.0/25 permit
199.122.120.0/21 permit
@@ -1898,6 +1952,8 @@
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
204.141.32.0/23 permit
204.141.42.0/23 permit
204.216.164.202 permit
204.220.160.0/21 permit
204.220.168.0/21 permit
@@ -2171,7 +2227,9 @@
2001:748:400:3301::4 permit
2404:6800:4000::/36 permit
2404:6800:4864::/56 permit
2603:1061:14:40::1 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
2607:f8b0:4000::/36 permit
2607:f8b0:4864::/56 permit
2620:109:c003:104::/64 permit
@@ -2185,6 +2243,8 @@
2620:10d:c09c:400::8:1 permit
2620:119:50c0:207::/64 permit
2620:119:50c0:207::215 permit
2620:1ec:46::38 permit
2620:1ec:bdf::38 permit
2800:3f0:4000::/36 permit
2800:3f0:4864::/56 permit
49.12.4.251 permit # checks.mailcow.email
@@ -3,7 +3,8 @@ rules {
backend = "http";
url = "http://nginx:9081/pipe.php";
selector = "reject_no_global_bl";
formatter = "multipart";
formatter = "default";
meta_headers = true;
}
RLINFO {
backend = "http";
@@ -15,7 +16,8 @@ rules {
backend = "http";
url = "http://nginx:9081/pushover.php";
selector = "mailcow_rcpt";
formatter = "multipart";
formatter = "json";
meta_headers = true;
}
}
+32 -27
View File
@@ -32,42 +32,47 @@ function parse_email($email) {
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
// rspamd metadata_exporter (multipart formatter):
// - $_POST['metadata'] JSON with the rspamd metadata
// - $_FILES['message'] raw RFC822 message
if (empty($_POST['metadata']) || !isset($_FILES['message']) || $_FILES['message']['error'] !== UPLOAD_ERR_OK) {
error_log("QUARANTINE: missing multipart parts from rspamd" . PHP_EOL);
http_response_code(400);
exit;
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$meta = json_decode($_POST['metadata'], true);
if (!is_array($meta)) {
error_log("QUARANTINE: cannot decode metadata JSON" . PHP_EOL);
http_response_code(400);
exit;
}
$raw_data_content = file_get_contents($_FILES['message']['tmp_name']);
$raw_data_content = file_get_contents('php://input');
$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
$raw_size = (int)$_FILES['message']['size'];
$headers = getallheaders();
$qid = $meta['qid'] ?? 'unknown';
$subject = iconv_mime_decode($meta['subject'] ?? '');
$score = $meta['score'] ?? 0;
$rcpts = $meta['rcpt'] ?? array();
$user = $meta['user'] ?? 'unknown';
$ip = $meta['ip'] ?? 'unknown';
$action = $meta['action'] ?? 'no action';
$sender = $meta['from'] ?? '';
$symbols = json_encode($meta['symbols'] ?? array());
$fuzzy = json_encode(is_array($meta['fuzzy'] ?? null) ? $meta['fuzzy'] : array());
$qid = $headers['X-Rspamd-Qid'];
$fuzzy = $headers['X-Rspamd-Fuzzy'];
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
$score = $headers['X-Rspamd-Score'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$user = $headers['X-Rspamd-User'];
$ip = $headers['X-Rspamd-Ip'];
$action = $headers['X-Rspamd-Action'];
$sender = $headers['X-Rspamd-From'];
$symbols = $headers['X-Rspamd-Symbols'];
$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
if (empty($sender)) {
error_log("QUARANTINE: Unknown sender, assuming empty-env-from@localhost" . PHP_EOL);
$sender = 'empty-env-from@localhost';
}
if ($fuzzy == 'unknown') {
$fuzzy = '[]';
}
try {
$max_size = (int)$redis->Get('Q_MAX_SIZE');
if (($max_size * 1048576) < $raw_size) {
@@ -89,7 +94,7 @@ catch (RedisException $e) {
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach ($rcpts as $rcpt) {
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
+26 -22
View File
@@ -32,46 +32,50 @@ function parse_email($email) {
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
// rspamd metadata_exporter (multipart formatter): metadata JSON arrives as $_POST['metadata'].
if (empty($_POST['metadata'])) {
error_log("NOTIFY: missing metadata part from rspamd" . PHP_EOL);
http_response_code(400);
exit;
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$meta = json_decode($_POST['metadata'], true);
if (!is_array($meta)) {
error_log("NOTIFY: cannot decode metadata JSON" . PHP_EOL);
http_response_code(400);
exit;
}
$headers = getallheaders();
$json_body = json_decode(file_get_contents('php://input'));
$qid = $meta['qid'] ?? 'unknown';
$rcpts = $meta['rcpt'] ?? array();
$sender = $meta['from'] ?? '';
$ip = $meta['ip'] ?? 'unknown';
$subject = iconv_mime_decode($meta['subject'] ?? '');
$messageid= $meta['message_id'] ?? '';
$qid = $headers['X-Rspamd-Qid'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$sender = $headers['X-Rspamd-From'];
$ip = $headers['X-Rspamd-Ip'];
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
$messageid= $json_body->message_id;
$priority = 0;
$symbols_array = $meta['symbols'] ?? array();
$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
if (is_array($symbols_array)) {
foreach ($symbols_array as $symbol) {
if (($symbol['name'] ?? null) == 'HAS_X_PRIO_ONE') {
if ($symbol['name'] == 'HAS_X_PRIO_ONE') {
$priority = 1;
break;
}
}
}
$sender_address = $meta['header_from'][0] ?? '';
$sender_address = $json_body->header_from[0];
$sender_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
$sender_address = $matches['address'];
$sender_name = trim($matches['name'], '"\' ');
}
$to_address = $meta['header_to'][0] ?? '';
$to_address = $json_body->header_to[0];
$to_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$to_address = $matches['address'];
@@ -81,7 +85,7 @@ if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach ($rcpts as $rcpt) {
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
+1 -13
View File
@@ -1072,7 +1072,6 @@ paths:
password2: "*"
quota: "3072"
force_pw_update: "1"
force_tfa: "1"
tls_enforce_in: "1"
tls_enforce_out: "1"
tags: ["tag1", "tag2"]
@@ -1119,7 +1118,6 @@ paths:
password2: atedismonsin
quota: "3072"
force_pw_update: "1"
force_tfa: "1"
tls_enforce_in: "1"
tls_enforce_out: "1"
tags: ["tag1", "tag2"]
@@ -1153,9 +1151,6 @@ paths:
force_pw_update:
description: forces the user to update its password on first login
type: boolean
force_tfa:
description: force 2FA enrollment at login
type: boolean
tls_enforce_in:
description: force inbound email tls encryption
type: boolean
@@ -2515,7 +2510,7 @@ paths:
description: >-
Using this endpoint you can perform actions on quarantine items. It is possible to release
emails from quarantine into to the inbox, or learn them as ham to improve Rspamd filtering.
You must provide the quarantine item IDs. You can get the IDs using the GET method.
You must provide the quarantine item IDs. You can get the IDs using the GET method.
operationId: Edit mails in Quarantine
requestBody:
content:
@@ -3419,7 +3414,6 @@ paths:
- mailbox
- active: "1"
force_pw_update: "0"
force_tfa: "0"
name: Full name
password: "*"
password2: "*"
@@ -3470,7 +3464,6 @@ paths:
attr:
active: "1"
force_pw_update: "0"
force_tfa: "0"
name: Full name
authsource: mailcow
password: ""
@@ -3494,9 +3487,6 @@ paths:
force_pw_update:
description: force user to change password on next login
type: boolean
force_tfa:
description: force 2FA enrollment at login
type: boolean
name:
description: Full name of the mailbox user
type: string
@@ -4891,7 +4881,6 @@ paths:
- active: "1"
attributes:
force_pw_update: "0"
force_tfa: "0"
mailbox_format: "maildir:"
quarantine_notification: never
sogo_access: "1"
@@ -5816,7 +5805,6 @@ paths:
- active: "1"
attributes:
force_pw_update: "0"
force_tfa: "0"
mailbox_format: "maildir:"
quarantine_notification: never
sogo_access: "1"
+8
View File
@@ -1035,6 +1035,14 @@ function edit_user_account($_data) {
// edit password
$is_forced_pw_update = !empty($_SESSION['pending_pw_update']);
if (((!empty($password_old) || $is_forced_pw_update) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2']))) {
if (!$is_forced_pw_update && (!isset($_SESSION['acl']['pw_change']) || $_SESSION['acl']['pw_change'] != "1")) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_data_log),
'msg' => 'access_denied'
);
return false;
}
// Only verify old password if this is NOT a forced password update
if (!$is_forced_pw_update) {
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+10 -3
View File
@@ -1331,6 +1331,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
$_data['quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
$_data['app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$_data['pw_change'] = (in_array('pw_change', $_data['acl'])) ? 1 : 0;
$_data['pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0;
} else {
$_data['spam_alias'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_spam_alias']);
@@ -1347,15 +1348,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['quarantine_notification'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_notification']);
$_data['quarantine_category'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_category']);
$_data['app_passwds'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_app_passwds']);
$_data['pw_change'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_pw_change']);
$_data['pw_reset'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_pw_reset']);
}
try {
$stmt = $pdo->prepare("INSERT INTO `user_acl`
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`, `pw_reset`)
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`, `pw_change`, `pw_reset`)
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds, :pw_reset) ");
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds, :pw_change, :pw_reset) ");
$stmt->execute(array(
':username' => $username,
':spam_alias' => $_data['spam_alias'],
@@ -1372,6 +1374,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':quarantine_notification' => $_data['quarantine_notification'],
':quarantine_category' => $_data['quarantine_category'],
':app_passwds' => $_data['app_passwds'],
':pw_change' => $_data['pw_change'],
':pw_reset' => $_data['pw_reset']
));
}
@@ -1792,6 +1795,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
$attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
$attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_change'] = (in_array('pw_change', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0;
} else {
$_data['acl'] = (array)$_data['acl'];
@@ -1809,6 +1813,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['acl_quarantine_notification'] = 0;
$attr['acl_quarantine_category'] = 0;
$attr['acl_app_passwds'] = 0;
$attr['acl_pw_change'] = 0;
}
@@ -3505,6 +3510,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $username;
}
return true;
break;
case 'mailbox_rename':
$domain = $_data['domain'];
@@ -3827,7 +3833,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value'];
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update'];
$attr["force_tfa"] = isset($_data['force_tfa']) ? intval($_data['force_tfa']) : $is_now['force_tfa'];
$attr["sogo_access"] = isset($_data['sogo_access']) ? intval($_data['sogo_access']) : $is_now['sogo_access'];
$attr["active"] = isset($_data['active']) ? intval($_data['active']) : $is_now['active'];
$attr["tls_enforce_in"] = isset($_data['tls_enforce_in']) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in'];
@@ -3862,6 +3867,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
$attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
$attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_change'] = (in_array('pw_change', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0;
} else {
foreach ($is_now as $key => $value){
@@ -6127,6 +6133,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $username;
}
return true;
break;
case 'mailbox_templates':
if ($_SESSION['mailcow_cc_role'] != "admin") {
+3 -1
View File
@@ -4,7 +4,7 @@ function init_db_schema()
try {
global $pdo;
$db_version = "19022026_1220";
$db_version = "16042026_1402";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -513,6 +513,7 @@ function init_db_schema()
"quarantine_notification" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine_category" => "TINYINT(1) NOT NULL DEFAULT '1'",
"app_passwds" => "TINYINT(1) NOT NULL DEFAULT '1'",
"pw_change" => "TINYINT(1) NOT NULL DEFAULT '1'",
"pw_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
),
"keys" => array(
@@ -1478,6 +1479,7 @@ function init_db_schema()
"acl_quarantine_notification" => 1,
"acl_quarantine_category" => 1,
"acl_app_passwds" => 1,
"acl_pw_change" => 1,
)
);
$stmt = $pdo->prepare("SELECT id FROM `templates` WHERE `type` = :type AND `template` = :template");
-1
View File
@@ -112,7 +112,6 @@ $AVAILABLE_LANGUAGES = array(
'sv-se' => 'Svenska (Swedish)',
'tr-tr' => 'Türkçe (Turkish)',
'uk-ua' => 'Українська (Ukrainian)',
'uz-uz' => 'Oʻzbekcha (Uzbek)',
'vi-vn' => 'Tiếng Việt (Vietnamese)',
'zh-cn' => '简体中文 (Simplified Chinese)',
'zh-tw' => '繁體中文 (Traditional Chinese)',
+3 -11
View File
@@ -403,6 +403,9 @@ $(document).ready(function() {
if (template.acl_app_passwds == 1){
acl.push("app_passwds");
}
if (template.acl_pw_change == 1){
acl.push("pw_change");
}
if (template.acl_pw_reset == 1){
acl.push("pw_reset");
}
@@ -424,11 +427,6 @@ $(document).ready(function() {
} else {
$('#force_pw_update').prop('checked', false);
}
if (template.force_tfa == 1){
$('#force_tfa').prop('checked', true);
} else {
$('#force_tfa').prop('checked', false);
}
if (template.sogo_access == 1){
$('#sogo_access').prop('checked', true);
} else {
@@ -1247,7 +1245,6 @@ jQuery(function($){
item.attributes.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.eas_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.dav_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.force_tfa = '<i class="text-' + (item.attributes.force_tfa == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.force_tfa == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.force_tfa == 1 ? '1' : '0') + '</span></i>';
if (item.attributes.quarantine_notification === 'never') {
item.attributes.quarantine_notification = lang.never;
} else if (item.attributes.quarantine_notification === 'hourly') {
@@ -1391,11 +1388,6 @@ jQuery(function($){
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
}
},
{
title: lang.force_tfa,
data: 'attributes.force_tfa',
defaultContent: ''
},
{
title: lang_edit.ratelimit,
data: 'attributes.ratelimit',
+4 -8
View File
@@ -102,21 +102,18 @@ jQuery(function($){
{
title: 'ID',
data: 'id',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang.qid,
data: 'qid',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang.sender,
data: 'sender',
className: 'senders-mw220',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang.subj,
@@ -131,8 +128,7 @@ jQuery(function($){
{
title: lang.rcpt,
data: 'rcpt',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang.danger,
+5 -8
View File
@@ -48,13 +48,13 @@ jQuery(function($){
url: "/api/v1/get/mailq/all",
dataSrc: function(data){
$.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + escapeHtml(item.queue_id) + '" />';
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
rcpts = $.map(item.recipients, function(i) {
return escapeHtml(i);
});
item.recipients = rcpts.join('<hr style="margin:1px!important">');
item.action = '<div class="btn-group">' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + escapeHtml(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
'</div>';
});
return data;
@@ -79,14 +79,12 @@ jQuery(function($){
{
title: 'QID',
data: 'queue_id',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: 'Queue',
data: 'queue_name',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang_admin.arrival_time,
@@ -108,8 +106,7 @@ jQuery(function($){
{
title: lang_admin.sender,
data: 'sender',
defaultContent: '',
render: $.fn.dataTable.render.text()
defaultContent: ''
},
{
title: lang_admin.recipients,
+1 -13
View File
@@ -1,17 +1,5 @@
{
"acl": {
"login_as": "E-poçt qutusu istifadəçisi olaraq daxil ol",
"alias_domains": "Alternativ domenlər əlavə et",
"app_passwds": "Tətbiq parollarını idarə et",
"bcc_maps": "BCC yönləndirmə xəritələri",
"delimiter_action": "Ayırıcı əməliyyatı",
"domain_desc": "Domen təsvirini dəyiş",
"domain_relayhost": "Domen üçün relay serveri dəyiş",
"eas_reset": "EAS cihazlarını sıfırla",
"extend_sender_acl": "Göndərən ACL-ni xarici ünvanlarla genişləndirməyə icazə ver",
"filters": "Filtrlər",
"mailbox_relayhost": "E-poçt qutusu üçün relay serveri dəyiş",
"prohibited": "ACL tərəfindən məhdudlaşdırılıb",
"protocol_access": "Protokol girişini dəyiş"
"login_as": "E-poçt qutusu istifadəçisi olaraq daxil ol"
}
}
+2 -1
View File
@@ -14,6 +14,7 @@
"prohibited": "Untersagt durch Richtlinie",
"protocol_access": "Ändern der erlaubten Protokolle",
"pushover": "Pushover",
"pw_change": "Passwortänderung erlauben",
"pw_reset": "Verwalten der E-Mail zur Passwortwiederherstellung erlauben",
"quarantine": "Quarantäne-Aktionen",
"quarantine_attachments": "Anhänge aus Quarantäne",
@@ -150,7 +151,7 @@
"arrival_time": "Ankunftszeit (Serverzeit)",
"authed_user": "Auth. Benutzer",
"ays": "Soll der Vorgang wirklich ausgeführt werden?",
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels kennzeichnen aktive permanente Sperren durch Denylisting.",
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Allowlist-Einträge.",
"change_logo": "Logo ändern",
"configuration": "Konfiguration",
"convert_html_to_text": "Konvertiere HTML zu reinem Text",
+1 -1
View File
@@ -14,6 +14,7 @@
"prohibited": "Prohibited by ACL",
"protocol_access": "Change protocol access",
"pushover": "Pushover",
"pw_change": "Allow password change",
"pw_reset": "Allow to reset mailcow user password",
"quarantine": "Quarantine actions",
"quarantine_attachments": "Quarantine attachments",
@@ -929,7 +930,6 @@
"filters": "Filters",
"fname": "Full name",
"force_pw_update": "Force password update at next login",
"force_tfa": "TFA",
"gal": "Global Address List",
"goto_ham": "Learn as <b>ham</b>",
"goto_spam": "Learn as <b>spam</b>",
+18 -40
View File
@@ -13,7 +13,7 @@
"domain_relayhost": "Mainīt domēna relayhost",
"eas_reset": "EAS ierīču atiestatīšana",
"extend_sender_acl": "Ļauj paplašināt sūtītāja ACL ar ārējām adresēm",
"login_as": "Pieteikties kā pastkastes lietotājam",
"login_as": "Pieteikšanās kā pastkastes lietotājam",
"mailbox_relayhost": "Pasta kastītes relayhost maiņa",
"prohibited": "Aizliegts ar ACL",
"protocol_access": "Protokola piekļuves maiņa",
@@ -91,12 +91,12 @@
"activate_api": "Aktivizēt API",
"active": "Aktīvs",
"add": "Pievienot",
"add_domain_admin": "Pievienot domēna pārvaldītāju",
"add_domain_admin": "Pievienot domēna administratoru",
"add_forwarding_host": "Pievienot pāradresācijas hostu",
"add_relayhost": "Pievienot Relayhost",
"add_row": "Pievienot rindu",
"admin": "Pārvaldītājs",
"admin_details": "Labot informāciju par pārvaldītāju",
"admin": "Administrators",
"admin_details": "Labot administratora detaļas",
"admin_domains": "Domēna uzdevumi",
"api_allow_from": "Atļaut API piekļuvi no šīm IP",
"api_key": "API atslēga",
@@ -114,7 +114,7 @@
"dkim_keys": "ARC/DKIM atslēgas",
"dkim_private_key": "Privāta atslēga",
"domain": "Domēns",
"domain_admins": "Domēna pārvaldītāji",
"domain_admins": "Domēna administratori",
"edit": "Labot",
"empty": "Nav iznākuma",
"f2b_ban_time": "Aizlieguma laiks (s)",
@@ -188,14 +188,7 @@
"quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:<br><small>Noklusējums ir 9999.0</small>",
"options": "Iespējas",
"password_reset_settings": "Paroļu atkopes iestatījumi",
"password_settings": "Paroļu iestatījumi",
"add_admin": "Pievienot pārvaldītāju",
"admins": "Pārvaldītāji",
"admins_ldap": "LDAP pārvaldītāji",
"admin_quicklink": "Paslēpt ātro saiti uz pārvaldītāju pieteikšanās lapu",
"domain_admin": "Domēna pārvaldītājs",
"domainadmin_quicklink": "Paslēpt ātro saiti uz domēna pārvaldītāju pieteikšanās lapu",
"user_quicklink": "Paslēpt ātro saiti uz lietotāju pieteikšanās lapu"
"password_settings": "Paroļu iestatījumi"
},
"danger": {
"access_denied": "Piekļuve liegta, vai nepareizi dati",
@@ -251,10 +244,7 @@
"app_passwd_id_invalid": "Lietotnes paroles Id %s ir nederīgs",
"img_dimensions_exceeded": "Attēls pārsniedz lielāko pieļaujamo attēla lielumu",
"img_size_exceeded": "Attēls pārsniedz lielāko pieļaujamo datnes lielumu",
"version_invalid": "Versija %s ir nederīga",
"generic_server_error": "Atgadījās neparedzēta servera kļūda. Lūgums sazināties ar pārvaldītāju.",
"password_reset_na": "Paroļu atkope šobrīd nav pieejama. Lūgums sazināties ar pārvaldītāju.",
"recovery_email_failed": "Nevarēja nosūtīt atkopes e-pasta ziņojumu. Lūgums sazināties ar pārvaldītāju."
"version_invalid": "Versija %s ir nederīga"
},
"diagnostics": {
"cname_from_a": "Vērtība, kas iegūta no A/AAAA ieraksta. Tas tiek atbalstīts tik ilgi, kamēr ieraksts norāda uz pareizo resursu.",
@@ -276,7 +266,7 @@
"delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī",
"description": "Apraksts",
"domain": "Labot domēnu",
"domain_admin": "Labot domēna pārvaldītāju",
"domain_admin": "Labot domēna administratoru",
"domain_quota": "Domēna kvota",
"domains": "Domēni",
"dont_check_sender_acl": "Atspējot sūtītāju pārbaudi domēnam %s (+ aizstājdomēni)",
@@ -339,8 +329,7 @@
"app_passwd": "Lietotnes parole",
"mta_sts_version": "Versija",
"mta_sts_version_info": "Norāda MTA-STS standarta versiju pašreiz ir derīga tikai <code>STSv1</code>.",
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sūtītāja pārbaude ir atspējota</span>",
"admin": "Labot pārvaldītāju"
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sūtītāja pārbaude ir atspējota</span>"
},
"footer": {
"cancel": "Atcelt",
@@ -373,14 +362,7 @@
"username": "Lietotājvārds",
"fido2_webauthn": "FIDO/WebAuthn pieteikšanās",
"mobileconfig_info": "Lūgums pieteikties kā pastkastes lietotājam, lai lejupielādētu pieprasīto Apple savienojuma profilu.",
"other_logins": "vai pieteikties ar",
"forgot_password": "> Aizmirsta parole?",
"login_linkstext": "Nepareiza pieteikšanās?",
"login_domainadmintext": "Pieteikties kā domēna pārvaldītājam",
"login_admintext": "Pieteikties kā pārvaldītājam",
"login_user": "Lietotu pieteikšanās",
"login_dadmin": "Domēna pārvaldītāju pieteikšanās",
"login_admin": "Pārvaldītāju pieteikšanās"
"other_logins": "vai pieslēgties ar"
},
"mailbox": {
"action": "Rīcība",
@@ -415,7 +397,7 @@
"description": "Apraksts",
"dkim_key_length": "DKIM atslēgas garums (bits)",
"domain": "Domēns",
"domain_admins": "Domēna pārvaldītāji",
"domain_admins": "Domēna administratori",
"domain_aliases": "Domēna aizstājvārdi",
"domain_quota": "Kvota",
"domain_quota_total": "Kopējais domēna ierobežojums",
@@ -525,7 +507,7 @@
"imap_smtp_server_auth_info": "Lūgums izmantot pilnu e-pasta adresi un PLAIN autentificēšanās mehānismu.<br>\nPieteikšanās dati tiks šifrēti ar servera puses obligātu šifrēšanu."
},
"success": {
"admin_modified": "Pārvaldītāja izmaiņas tika saglabātas",
"admin_modified": "Izmaiņas administrātoram ir saglabātas",
"alias_added": "Aizstājadrese %s (%d) tika pievienota",
"alias_domain_removed": "Aizstājdomēns %s tika noņemts",
"alias_modified": "Aizstājadreses izmaiņas %s tika saglabātas",
@@ -536,9 +518,9 @@
"dkim_added": "DKIM atslēga saglabāta",
"dkim_removed": "DKIM atslēga %s ir noņemta",
"domain_added": "Pievienots domēns %s",
"domain_admin_added": "Tika pievienots domēna pārvaldītājs %s",
"domain_admin_modified": "Domēna pārvaldītāja %s izmaiņas tika saglabātas",
"domain_admin_removed": "Tika noņemts domēna pārvaldītājs %s",
"domain_admin_added": "Domēna administrātors %s pievienots",
"domain_admin_modified": "Izmaiņas domēna administrātoram %s ir saglabātas",
"domain_admin_removed": "Domēna administrators %s tika noņemts",
"domain_modified": "Izmaiņas domēnam %s ir saglabātas",
"domain_removed": "Domēns %s ir noņemts",
"eas_reset": "ActiveSync ierīces priekš lietotāja %s tika atiestatītas",
@@ -566,9 +548,7 @@
"verified_yotp_login": "Apliecināta Yubico OTP pieteikšanās",
"app_passwd_removed": "Noņemta lietotnes parole ar Id %s",
"app_passwd_added": "Pievienota jauna lietotnes parole",
"f2b_banlist_refreshed": "Liegumu saraksta Id tika sekmīgi atsvaidzināts.",
"admin_added": "Tika pievienots pārvaldītājs %s",
"admin_removed": "Tika noņemts pārvaldītājs %s"
"f2b_banlist_refreshed": "Liegumu saraksta Id tika sekmīgi atsvaidzināts."
},
"tfa": {
"api_register": "%s izmanto Yubico Cloud API. Lūdzu iegūstiet API atslēgu priekš Jūsu atslēgas<a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">here</a>",
@@ -589,8 +569,7 @@
"waiting_usb_auth": "<i>Gaida USB ierīci...</i><br><br>Lūdzu, tagad nospiežiet pogu uz Jūsu WebAuthn USB ierīces.",
"waiting_usb_register": "<i>Gaida USB ierīci...</i><br><br>Lūgums augstāk ievadīt savu paroli un apstiprināt reģistrēšanos ar USB ierīces pogas nospiešanu.",
"yubi_otp": "Yubico OTP autentifikators",
"authenticators": "Autentificētāji",
"u2f_deprecated_important": "Lūgums reģistrēt savu atslēgu pārvaldības lapā ar jauno WebAuthn veidu."
"authenticators": "Autentificētāji"
},
"user": {
"action": "Rīcība",
@@ -722,8 +701,7 @@
"warning": {
"domain_added_sogo_failed": "Domēns pievienots, bet neizdevās pārsāknēt SOGO. Lūgums pārbaudīt servera žurnālus.",
"dovecot_restart_failed": "Dovecot neizdevās pārsāknēties. Lūgums pārbaudīt žurnālus",
"is_not_primary_alias": "Izlaists aizstājvārds %s, kas nav galvenais",
"no_active_admin": "Nevar deaktivēt pēdējo aktīvo pārvaldītāju"
"is_not_primary_alias": "Izlaists aizstājvārds %s, kas nav galvenais"
},
"oauth2": {
"access_denied": "Lūgums pieteikties kā pastkastes īpašniekam, lai nodrošinātu piekļuvi ar OAuth2."
+1 -2
View File
@@ -27,8 +27,7 @@
"tls_policy": "Versleutelingsbeleid",
"unlimited_quota": "Onbeperkte quota voor mailboxen",
"domain_desc": "Wijzig domeinbeschrijving",
"pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten",
"domain_relayhost": "Verander relayhost voor een domein"
"pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten"
},
"add": {
"activate_filter_warn": "Alle andere filters worden gedeactiveerd zolang deze geactiveerd is.",
+2 -2
View File
@@ -189,7 +189,7 @@
"api_info": "API jest w trakcie prac. Dokumentację można znaleźć pod adresem <a href=\"/api\">/api</a>",
"api_key": "klucz API",
"api_read_only": "Dostęp tylko do odczytu",
"api_read_write": "Dostęp do odczytu i zapisu",
"api_read_write": "Dostęp tylko do odczytu",
"api_skip_ip_check": "Pomiń sprawdzenie IP dla API",
"app_hide": "Ukryj dla logowania",
"app_links": "Linki aplikacji",
@@ -1226,7 +1226,7 @@
"decimal": ".",
"emptyTable": "Brak danych w tabeli",
"expand_all": "Rozszerz wszystko",
"info": "Wyświetlanie od _START_ do _END_ z _TOTAL_ wpisów",
"info": "Wyświetlanie od START do END z TOTAL wpisów",
"infoEmpty": "Wyświetlanie od 0 do 0 z 0 wpisów",
"infoFiltered": "(filtrowane z _MAX_ suma wpisów)",
"thousands": ",",
+8 -8
View File
@@ -38,7 +38,7 @@
"add_domain_only": "Adicionar somente domínio",
"add_domain_restart": "Adicionar domínio e reiniciar o SoGo",
"alias_address": "Endereço (s) de alias",
"alias_address_info": "<small>Endereço(s) de e-mail completo(s) ou @example.com, para capturar todas as mensagens de um domínio (separadas por vírgula). <b>Apenas domínios do Mailcow</b>.</small>",
"alias_address_info": "<small>Endereço/s de e-mail completo ou @example .com, para capturar todas as mensagens de um domínio (separadas por vírgula). <b> somente domínios mailcow</b>.</small>",
"alias_domain": "Domínio de alias",
"alias_domain_info": "<small>Somente nomes de domínio válidos (separados por vírgula).</small>",
"app_name": "Nome do aplicativo",
@@ -158,7 +158,7 @@
"logo_dark_label": "Invertido para o modo escuro",
"configuration": "Configuração",
"convert_html_to_text": "Converter HTML em texto sem formatação",
"copy_to_clipboard": "Copiado para a área de transferência!",
"copy_to_clipboard": "Text copied to clipboard!",
"cors_settings": "Configurações do CORS",
"credentials_transport_warning": "<b>Aviso</b>: Adicionar uma nova entrada no mapa de transporte atualizará as credenciais de todas as entradas com uma coluna correspondente do próximo salto.",
"customer_id": "ID do cliente",
@@ -223,7 +223,7 @@
"includes": "Inclua esses destinatários",
"ip_check": "Verificação de IP",
"ip_check_disabled": "A verificação de IP está desativada. Você pode ativá-lo em <br><strong>Sistema > Configuração > Opções > Personalizar</strong>",
"ip_check_opt_in": "Opte por usar o serviço de terceiros <strong>ipv4.mailcow.email</strong> e <strong>ipv6.mailcow.email</strong> para resolver endereços IP externos.",
"ip_check_opt_in": "Opte por usar o serviço de terceiros <strong>ipv4.mailcow.email.</strong> e <strong>ipv6.mailcow.email</strong> para resolver endereços IP externos.",
"is_mx_based": "Baseado em MX",
"last_applied": "Aplicado pela última vez",
"license_info": "Uma licença não é necessária, mas ajuda no desenvolvimento.<br><a href=\"https://www.servercow.de/mailcow? Lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registre seu GUID aqui</a> ou <a href=\"https://www.servercow.de/mailcow? Lang=en#support\" target=\"_blank\" alt=\"Support order\">comprar suporte para sua instalação de mailcow.</a>",
@@ -245,7 +245,7 @@
"oauth2_add_client": "Adicionar cliente OAuth2",
"oauth2_client_id": "ID do cliente",
"oauth2_client_secret": "Segredo do cliente",
"oauth2_info": "A implementação do OAuth2 suporta o tipo de concessão \"Código de Autorização\" e emite tokens de atualização.<br>\nO servidor também emite automaticamente novos tokens de atualização após um token de atualização ter sido usado.<br>\nO escopo padrão é <i>profile</i>. Somente usuários de caixa de correio podem ser autenticados com o OAuth2. Se o parâmetro de escopo for omitido, o padrão será <i>profile</i>.<br>\nO parâmetro <i>state</i> deve ser enviado pelo cliente como parte da solicitação de autorização.<br><br>\nCaminhos para solicitações à API OAuth2: <br>\n\n<ul>\n <li>Ponto de extremidade de autorização: <code>/oauth/authorize</code></li>\n <li>Endpoint do token: <code>/oauth/token</code></li>\n <li>Página de recursos: <code>/oauth/profile</code></li>\n</ul>\n\nRegenerar o segredo do cliente não expirará os códigos de autorização existentes, mas impedirá a renovação do token.<br><br>\nA revogação dos tokens de cliente causará o encerramento imediato de todas as sessões ativas. Todos os clientes precisarão se autenticar novamente.",
"oauth2_info": "A implementação OAuth2 suporta o tipo de concessão \"Código de Autorização\" e emite tokens de atualização.<br>\nO servidor também emite automaticamente novos tokens de atualização, depois que um token de atualização foi usado.<br><br>\n&#8226; O escopo padrão é <i>perfil</i>. Somente usuários com caixa de e-mail podem ser autenticados contra o OAuth2. Se o parâmetro de escopo for omitido, ele voltará para <i>perfil</i>.<br>\nCaminhos para solicitações OAuth2 API: <br>\n<ul>\n<li>Endpoint de autorização: <code>/oauth/authorize</code></li>\n<li>Endpoint token: <code>/oauth/token</code></li>\n<li>Página de recursos: <code>/oauth/profile</code></li>\n</ul>\nRegenerar o segredo do cliente não expirará os códigos de autorização existentes, mas eles não renovarão seu token.<br><br>\nA revogação dos tokens do cliente causará o término imediato de todas as sessões ativas. Todos os clientes precisam se autenticar novamente.",
"oauth2_redirect_uri": "URI de redirecionamento",
"oauth2_renew_secret": "Gere um novo segredo de cliente",
"oauth2_revoke_tokens": "Revogar todos os tokens do cliente",
@@ -730,7 +730,7 @@
"pushover_verify": "Verifique as credenciais",
"quota_mb": "Cota (MiB)",
"quota_warning_bcc": "Aviso de cota BCC",
"quota_warning_bcc_info": "Os avisos serão enviados em cópias separadas para os seguintes destinatários. O assunto será precedido pelo nome de usuário correspondente entre colchetes, por exemplo: <code>Aviso de cota (user@example.com)</code>.",
"quota_warning_bcc_info": "Os avisos serão enviados em cópias separadas para os seguintes destinatários. O assunto será sufixado pelo nome de usuário correspondente entre colchetes, por exemplo: <code>Aviso de cota (</code>user@example.com).",
"ratelimit": "Limite de taxa",
"redirect_uri": "URL de redirecionamento/retorno de chamada",
"relay_all": "Retransmita todos os destinatários",
@@ -759,7 +759,7 @@
"spam_score": "Defina uma pontuação de spam personalizada",
"subfolder2": "Sincronizar na subpasta no destino <br><small>(vazio = não usar subpasta</small>)",
"syncjob": "Editar tarefa de sincronização",
"target_address": "Ir para o(s) endereço(s) <small>(separados por vírgula)</small>",
"target_address": "<small>Ir para endereço/es (separados por vírgula)</small>",
"target_domain": "Domínio de destino",
"timeout1": "Tempo limite para conexão com o host remoto",
"timeout2": "Tempo limite para conexão com o host local",
@@ -807,7 +807,7 @@
"cancel": "Cancelar",
"confirm_delete": "Confirme a exclusão",
"delete_now": "Excluir agora",
"delete_these_items": "Por favor, confirme as alterações feitas no seguinte ID de objeto.",
"delete_these_items": "Confirme suas alterações no seguinte ID de objeto",
"hibp_check": "Verifique em haveibeenpwned.com",
"hibp_nok": "Combinado! Essa é uma senha potencialmente perigosa!",
"hibp_ok": "Nenhuma combinação encontrada.",
@@ -1393,7 +1393,7 @@
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Não é possível se conectar ao servidor remoto",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Nome de usuário ou senha incorretos",
"tag_handling": "Definir o tratamento para e-mails marcados",
"tag_help_example": "Exemplo de endereço de e-mail com tag: eu+Facebook</b>@exemplo.org",
"tag_help_example": "Exemplo de um endereço de e-mail marcado: me <b>+Facebook</b> @example .org",
"tag_help_explain": "Na subpasta: uma nova subpasta com o nome da tag será criada abaixo da CAIXA DE ENTRADA (“Caixa de entrada/Facebook”). <br>\r\nNo assunto: o nome das tags será anexado ao assunto do e-mail, por exemplo: “[Facebook] Minhas notícias”.",
"tag_in_none": "Não faça nada",
"tag_in_subfolder": "Na subpasta",
File diff suppressed because it is too large Load Diff
+5 -22
View File
@@ -268,10 +268,10 @@
"includes": "Bao gồm những người nhận này",
"ip_check": "Kiểm tra IP",
"ip_check_disabled": "Kiểm tra IP đã bị vô hiệu hóa. Bạn có thể bật nó trong<br> <strong>Hệ thống > Cấu hình > Tùy chọn > Tùy chỉnh</strong>",
"ip_check_opt_in": "Đăng ký tham gia sử dụng dịch vụ bên thứ ba dùng <strong>ipv4.mailcow.email</strong> và <strong>ipv6.mailcow.email</strong> để phân giải địa chỉ IP bên ngoài.",
"ip_check_opt_in": "Chọn tham gia sử dụng dịch vụ bên thứ ba <strong>ipv4.mailcow.email</strong> và <strong>ipv6.mailcow.email</strong> để phân giải địa chỉ IP bên ngoài.",
"is_mx_based": "Dựa trên MX",
"last_applied": "Áp dụng lần cuối",
"license_info": "Giấy phép tuy không bắt buộc nhưng giúp phát triển thêm.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"Đặt hàng SAL\">Đăng ký GUID của bạn tại đây</a> hoặc <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Đặt hàng hỗ trợ\">mua hỗ trợ cho cài đặt mailcow của bạn.</a>",
"license_info": "Giấy phép không bắt buộc nhưng giúp phát triển thêm.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"Đặt hàng SAL\">Đăng ký GUID của bạn tại đây</a> hoặc <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Đặt hàng hỗ trợ\">mua hỗ trợ cho cài đặt mailcow của bạn.</a>",
"link": "Liên kết",
"loading": "Vui lòng đợi...",
"login_time": "Thời gian đăng nhập",
@@ -556,9 +556,7 @@
"validity_missing": "Vui lòng gán thời hạn hiệu lực",
"value_missing": "Vui lòng cung cấp tất cả các giá trị",
"version_invalid": "Phiên bản %s không hợp lệ",
"yotp_verification_failed": "Xác thực Yubico OTP thất bại: %s",
"tfa_removal_blocked": "Xác thực hai yếu tố không thể bị xóa vì đây là yêu cầu bắt buộc đối với tài khoản của bạn.",
"quarantine_category_invalid": "Danh mục cách ly phải là một trong các loại sau : add_header, reject, all."
"yotp_verification_failed": "Xác thực Yubico OTP thất bại: %s"
},
"datatables": {
"collapse_all": "Thu gọn tất cả",
@@ -581,9 +579,7 @@
"aria": {
"sortAscending": ": kích hoạt để sắp xếp cột tăng dần",
"sortDescending": ": kích hoạt để sắp xếp cột giảm dần"
},
"decimal": ".",
"thousands": ","
}
},
"debug": {
"architecture": "Kiến trúc",
@@ -697,19 +693,6 @@
"internal_info": "Bí danh nội bộ chỉ có thể truy cập từ tên miền sở hữu hoặc tên miền bí danh.",
"kind": "Loại",
"last_modified": "Sửa đổi lần cuối",
"lookup_mx": "Đích là một biểu thức chính quy để khớp với tên MX (<code>.*.google.com</code> để định tuyến tất cả thư nhắm đến MX kết thúc bằng google.com qua bước nhảy này)",
"sender_allowed": "Cho phép gửi đi bằng bí danh",
"sender_allowed_info": "Nếu bị vô hiệu hóa, bí danh này chỉ có thể nhận thư. Sử dụng ACL của người gửi để ghi đè và cấp quyền gửi cho các hộp thư cụ thể.",
"mailbox": "Chỉnh sửa hộp thư",
"mailbox_quota_def": "Hạn mức hộp thư mặc định",
"mailbox_relayhost_info": "Chỉ áp dụng cho hộp thư và các bí danh trực tiếp, thao tác này sẽ ghi đè lên máy chủ chuyển tiếp tên miền.",
"mailbox_rename": "Đổi tên hộp thư",
"mailbox_rename_agree": "Tôi đã tạo bản sao lưu.",
"mailbox_rename_warning": "QUAN TRỌNG! Hãy tạo bản sao lưu trước khi đổi tên hộp thư.",
"mailbox_rename_alias": "Tạo tự động bí danh",
"mailbox_rename_title": "Tên mới của hộp thư cục bộ",
"max_aliases": "Số lượng Bí danh tối đa",
"max_mailboxes": "Số lượng hộp thư tối đa có thể có",
"max_quota": "Dung lượng tối đa cho mỗi hộp thư (MiB)"
"lookup_mx": "Đích là một biểu thức chính quy để khớp với tên MX (<code>.*.google.com</code> để định tuyến tất cả thư nhắm đến MX kết thúc bằng google.com qua bước nhảy này)"
}
}
-1
View File
@@ -65,7 +65,6 @@ if (isset($_GET['app_password'])) {
$attr['protocols'][] = 'dav_access';
}
app_passwd("add", $attr);
$password = htmlspecialchars($password, ENT_NOQUOTES);
} else {
$app_password = false;
}
+1 -1
View File
@@ -23,7 +23,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2" for="script_data">Script:</label>
<div class="col-sm-10">
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control textarea-code" rows="20" id="script_data" name="script_data" required>{{ result.script_data }}</textarea>
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control textarea-code" rows="20" id="script_data" name="script_data" required>{{ result.script_data|raw }}</textarea>
</div>
</div>
<div class="row mb-2">
@@ -8,7 +8,6 @@
<input type="hidden" value="default" name="sender_acl">
<input type="hidden" value="0" name="force_pw_update">
<input type="hidden" value="0" name="force_tfa">
<input type="hidden" value="0" name="sogo_access">
<input type="hidden" value="0" name="protocol_access">
@@ -132,6 +131,7 @@
<option value="quarantine_notification" {% if template.attributes.acl_quarantine_notification == '1' %} selected{% endif %}>{{ lang.acl["quarantine_notification"] }}</option>
<option value="quarantine_category" {% if template.attributes.acl_quarantine_category == '1' %} selected{% endif %}>{{ lang.acl["quarantine_category"] }}</option>
<option value="app_passwds" {% if template.attributes.acl_app_passwds == '1' %} selected{% endif %}>{{ lang.acl["app_passwds"] }}</option>
<option value="pw_change" {% if template.attributes.acl_pw_change == '1' %} selected{% endif %}>{{ lang.acl["pw_change"] }}</option>
<option value="pw_reset" {% if template.attributes.acl_pw_reset == '1' %} selected{% endif %}>{{ lang.acl["pw_reset"] }}</option>
</select>
</div>
@@ -166,14 +166,6 @@
</div>
</div>
</div>
<div class="row">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="force_tfa" id="force_tfa"{% if template.attributes.force_tfa == '1' %} checked{% endif %}> {{ lang.tfa.force_tfa }}</label>
<small class="text-muted">{{ lang.tfa.force_tfa_info }}</small>
</div>
</div>
</div>
{% if not skip_sogo %}
<div class="row">
<div class="offset-sm-2 col-sm-10">
+1
View File
@@ -172,6 +172,7 @@
<option value="quarantine_notification" selected>{{ lang.acl["quarantine_notification"] }}</option>
<option value="quarantine_category" selected>{{ lang.acl["quarantine_category"] }}</option>
<option value="app_passwds" selected>{{ lang.acl["app_passwds"] }}</option>
<option value="pw_change" selected>{{ lang.acl["pw_change"] }}</option>
<option value="pw_reset" selected>{{ lang.acl["pw_reset"] }}</option>
</select>
</div>
@@ -108,7 +108,9 @@
<div class="row">
<div class="col-12 col-md-3 d-flex"></div>
<div class="col-12 col-md-9 d-flex flex-wrap">
{% if acl.pw_change == 1 %}
<a class="btn btn-secondary" href="#pwChangeModal" data-bs-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.change_password }}</a>
{% endif %}
{% if acl.pw_reset == 1 %}
<a class="btn btn-secondary ms-4" href="#pwRecoveryEmailModal" data-bs-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.pw_recovery_email }}</a></p>
{% endif %}
+5 -5
View File
@@ -1,7 +1,7 @@
services:
unbound-mailcow:
image: ghcr.io/mailcow/unbound:1.25.1-1
image: ghcr.io/mailcow/unbound:1.25
environment:
- TZ=${TZ}
- SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
@@ -84,7 +84,7 @@ services:
- clamd
rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:4.1.0-1
image: ghcr.io/mailcow/rspamd:3.14.3-1
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -117,7 +117,7 @@ services:
- rspamd
php-fpm-mailcow:
image: ghcr.io/mailcow/phpfpm:8.2.29-3
image: ghcr.io/mailcow/phpfpm:8.2.29-2
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
@@ -200,7 +200,7 @@ services:
- phpfpm
sogo-mailcow:
image: ghcr.io/mailcow/sogo:5.12.9-1
image: ghcr.io/mailcow/sogo:5.12.5-3
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -419,7 +419,7 @@ services:
- php-fpm-mailcow
- sogo-mailcow
- rspamd-mailcow
image: ghcr.io/mailcow/nginx:1.30.2-1
image: ghcr.io/mailcow/nginx:1.06
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
@@ -25,6 +25,6 @@ services:
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
mysql-mailcow:
image: alpine:3.24
image: alpine:3.23
command: /bin/true
restart: "no"