1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-11 00:46:01 +00:00

Merge pull request #6948 from mailcow/staging

2025-12
This commit is contained in:
DerLinkman
2025-12-09 13:29:15 +01:00
committed by GitHub
39 changed files with 1749 additions and 307 deletions

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v10.1.0
uses: actions/stale@v10.1.1
with:
repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60

View File

@@ -27,7 +27,7 @@ jobs:
- "watchdog-mailcow"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Docker
run: |
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh

View File

@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Run the Action

View File

@@ -13,7 +13,7 @@ jobs:
packages: write
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Generate postscreen_access.cidr
run: |

View File

@@ -1,11 +1,11 @@
# Contribution Guidelines
**_Last modified on 15th August 2024_**
**_Last modified on 12th November 2025_**
First of all, thank you for wanting to provide a bugfix or a new feature for the mailcow community, it's because of your help that the project can continue to grow!
As we want to keep mailcow's development structured we setup these Guidelines which helps you to create your issue/pull request accordingly.
**PLEASE NOTE, THAT WE MIGHT CLOSE ISSUES/PULL REQUESTS IF THEY DON'T FULLFIL OUR WRITTEN GUIDELINES WRITTEN INSIDE THIS DOCUMENT**. So please check this guidelines before you propose a Issue/Pull Request.
**PLEASE NOTE, THAT WE WILL CLOSE ISSUES/PULL REQUESTS IF THEY DON'T FULFILL OUR WRITTEN GUIDELINES WRITTEN INSIDE THIS DOCUMENT**. So please check this guidelines before you propose a Issue/Pull Request.
## Topics
@@ -27,14 +27,18 @@ However, please note the following regarding pull requests:
6. Please **ALWAYS** create the actual pull request against the staging branch and **NEVER** directly against the master branch. *If you forget to do this, our moobot will remind you to switch the branch to staging.*
7. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
8. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
9. If your PR requires a Docker image rebuild (changes to Dockerfiles or files in data/Dockerfiles/), update the image tag in docker-compose.yml. Use the base-image versioning (e.g. ghcr.io/mailcow/sogo:5.12.4 → :5.12.5 for version bumps; append a letter for patch fixes, e.g. :5.12.4a). Follow this scheme.
---
## Issue Reporting
**_Last modified on 15th August 2024_**
**_Last modified on 12th November 2025_**
If you plan to report a issue within mailcow please read and understand the following rules:
### Security disclosures / Security-related fixes
- Security vulnerabilities and security fixes must always be reported confidentially first to the contact address specified in SECURITY.md before they are integrated, published, or publicly disclosed in issues/PRs. Please wait for a response from the specified contact to ensure coordinated and responsible disclosure.
### Issue Reporting Guidelines
1. **ONLY** use the issue tracker for bug reports or improvement requests and NOT for support questions. For support questions you can either contact the [mailcow community on Telegram](https://docs.mailcow.email/#community-support-and-chat) or the mailcow team directly in exchange for a [support fee](https://docs.mailcow.email/#commercial-support).

View File

@@ -1,3 +1,3 @@
FROM debian:bookworm-slim
FROM debian:trixie-slim
RUN apt update && apt install pigz -y --no-install-recommends
RUN apt update && apt install pigz zstd -y --no-install-recommends

View File

@@ -204,16 +204,17 @@ EOF
# Create random master Password for SOGo SSO
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
# Creating additional creds file for SOGo notify crons (calendars, etc)
echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
cat <<EOF > /etc/dovecot/sogo-sso.conf
# Autogenerated by mailcow
passdb {
driver = static
args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
args = allow_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
}
EOF
# Creating additional creds file for SOGo notify crons (calendars, etc) (dummy user, sso password)
echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
if [[ "${MASTER}" =~ ^([nN][oO]|[nN])+$ ]]; then
# Toggling MASTER will result in a rebuild of containers, so the quota script will be recreated
cat <<'EOF' > /usr/local/bin/quota_notify.py

View File

@@ -167,7 +167,7 @@ DELIMITER //
CREATE EVENT clean_spamalias
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP() AND permanent = 0;
END;
//
DELIMITER ;

View File

@@ -4,7 +4,7 @@ WORKDIR /src
ENV CGO_ENABLED=0 \
GO111MODULE=on \
NOOPT=1 \
VERSION=1.8.14
VERSION=1.8.22
RUN git clone --branch v${VERSION} https://github.com/Zuplu/postfix-tlspol && \
cd /src/postfix-tlspol && \

View File

@@ -390,7 +390,7 @@ hosts = unix:/var/run/mysqld/mysqld.sock
dbname = ${DBNAME}
query = SELECT goto FROM spamalias
WHERE address='%s'
AND validity >= UNIX_TIMESTAMP()
AND (validity >= UNIX_TIMESTAMP() OR permanent != 0)
EOF
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
@@ -524,4 +524,4 @@ if [[ $? != 0 ]]; then
else
postfix -c /opt/postfix/conf start
sleep 126144000
fi
fi

View File

@@ -50,10 +50,6 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
<string>YES</string>
<key>SOGoEncryptionKey</key>
<string>${RAND_PASS}</string>
<key>SOGoURLEncryptionEnabled</key>
<string>YES</string>
<key>SOGoURLEncryptionPassphrase</key>
<string>${SOGO_URL_ENCRYPTION_KEY}</string>
<key>OCSAdminURL</key>
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_admin</string>
<key>OCSCacheFolderURL</key>

View File

@@ -13,6 +13,7 @@ events {
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '

View File

@@ -14,7 +14,6 @@ ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=15768000;";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN" always;

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Wed Oct 1 00:21:33 UTC 2025
# Whitelist generated by Postwhite v3.4 on Mon Dec 1 00:24:43 UTC 2025
# https://github.com/stevejenkins/postwhite/
# 2216 total rules
# 2186 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:2800::/53 permit
@@ -29,7 +29,9 @@
2a01:b747:3005:200::/56 permit
2a01:b747:3006:200::/56 permit
2a02:a60:0:5::/64 permit
2a0f:f640::/56 permit
2c0f:fb50:4000::/36 permit
2.207.151.53 permit
2.207.217.30 permit
3.64.237.68 permit
3.65.3.180 permit
@@ -50,14 +52,11 @@
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.41 permit
13.107.246.41 permit
13.107.213.69 permit
13.107.246.69 permit
13.108.16.0/20 permit
13.110.208.0/21 permit
13.110.209.0/24 permit
13.110.216.0/22 permit
@@ -66,6 +65,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
@@ -169,7 +169,6 @@
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,7 +192,6 @@
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
@@ -275,7 +273,6 @@
50.112.246.219 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
@@ -302,7 +299,6 @@
52.96.91.34 permit
52.96.111.82 permit
52.96.172.98 permit
52.96.214.50 permit
52.96.222.194 permit
52.96.222.226 permit
52.96.223.2 permit
@@ -341,7 +337,6 @@
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
@@ -402,23 +397,15 @@
64.207.219.143 permit
64.233.160.0/19 permit
65.52.80.137 permit
65.54.51.64/26 permit
65.54.61.64/26 permit
65.54.121.120/29 permit
65.54.190.0/24 permit
65.54.241.0/24 permit
65.55.29.77 permit
65.55.33.64/28 permit
65.55.34.0/24 permit
65.55.42.224/28 permit
65.55.52.224/27 permit
65.55.78.128/25 permit
65.55.81.48/28 permit
65.55.90.0/24 permit
65.55.94.0/25 permit
65.55.111.0/24 permit
65.55.113.64/26 permit
65.55.116.0/25 permit
65.55.126.0/25 permit
65.55.174.0/25 permit
65.55.178.128/27 permit
@@ -426,7 +413,6 @@
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
@@ -640,7 +626,6 @@
74.208.4.220 permit
74.208.4.221 permit
74.209.250.0/24 permit
75.2.70.75 permit
76.223.128.0/19 permit
76.223.176.0/20 permit
77.238.176.0/24 permit
@@ -669,18 +654,12 @@
81.169.146.245 permit
81.169.146.246 permit
81.223.46.0/27 permit
82.165.159.2 permit
82.165.159.3 permit
82.165.159.4 permit
82.165.159.12 permit
82.165.159.13 permit
82.165.159.14 permit
82.165.159.34 permit
82.165.159.35 permit
82.165.159.40 permit
82.165.159.41 permit
82.165.159.42 permit
82.165.159.45 permit
82.165.159.130 permit
82.165.159.131 permit
85.9.206.169 permit
@@ -1230,11 +1209,8 @@
98.139.245.208/30 permit
98.139.245.212/31 permit
99.78.197.208/28 permit
99.83.190.102 permit
103.9.96.0/22 permit
103.28.42.0/24 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
@@ -1400,9 +1376,6 @@
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
@@ -1430,6 +1403,7 @@
128.245.248.0/21 permit
129.41.77.70 permit
129.41.169.249 permit
129.77.16.0/20 permit
129.80.5.164 permit
129.80.64.36 permit
129.80.67.121 permit
@@ -1466,10 +1440,6 @@
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
@@ -1481,6 +1451,7 @@
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
136.147.176.0/20 permit
@@ -1495,7 +1466,6 @@
139.138.46.219 permit
139.138.57.55 permit
139.138.58.119 permit
139.167.79.86 permit
139.180.17.0/24 permit
140.238.148.191 permit
141.148.159.229 permit
@@ -1552,9 +1522,6 @@
155.248.220.138 permit
155.248.234.149 permit
155.248.237.141 permit
157.55.0.192/26 permit
157.55.1.128/26 permit
157.55.2.0/25 permit
157.55.9.128/25 permit
157.55.11.0/25 permit
157.55.49.0/25 permit
@@ -1618,9 +1585,6 @@
164.152.23.32 permit
164.152.25.241 permit
164.177.132.168/30 permit
165.173.128.0/24 permit
165.173.180.250/31 permit
165.173.182.250/31 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
@@ -1650,29 +1614,16 @@
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.188.0/24 permit
169.148.188.182 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
170.10.132.56/29 permit
170.10.132.64/29 permit
170.10.133.0/24 permit
172.217.32.0/20 permit
172.253.56.0/21 permit
172.253.112.0/20 permit
173.0.84.0/29 permit
173.0.84.224/27 permit
173.0.94.244/30 permit
173.194.0.0/16 permit
173.194.0.0/17 permit
173.203.79.182 permit
173.203.81.39 permit
173.224.161.128/25 permit
@@ -1812,6 +1763,7 @@
194.97.212.12 permit
194.106.220.0/23 permit
194.113.24.0/22 permit
194.113.42.0/26 permit
194.154.193.192/27 permit
195.4.92.0/23 permit
195.54.172.0/23 permit
@@ -1825,6 +1777,7 @@
198.61.254.21 permit
198.61.254.231 permit
198.178.234.57 permit
198.202.211.1 permit
198.244.48.0/20 permit
198.244.56.107 permit
198.244.56.108 permit
@@ -1846,16 +1799,7 @@
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
@@ -1912,8 +1856,6 @@
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
@@ -1946,7 +1888,6 @@
207.46.52.79 permit
207.46.58.128/25 permit
207.46.116.128/29 permit
207.46.117.0/24 permit
207.46.132.128/27 permit
207.46.198.0/25 permit
207.46.200.0/27 permit
@@ -2052,19 +1993,11 @@
212.82.111.228/31 permit
212.82.111.230 permit
212.123.28.40 permit
212.227.15.3 permit
212.227.15.4 permit
212.227.15.5 permit
212.227.15.6 permit
212.227.15.7 permit
212.227.15.8 permit
212.227.15.14 permit
212.227.15.15 permit
212.227.15.18 permit
212.227.15.19 permit
212.227.15.25 permit
212.227.15.26 permit
212.227.15.29 permit
212.227.15.44 permit
212.227.15.45 permit
212.227.15.46 permit
@@ -2072,17 +2005,11 @@
212.227.15.50 permit
212.227.15.52 permit
212.227.15.53 permit
212.227.15.54 permit
212.227.15.55 permit
212.227.17.1 permit
212.227.17.2 permit
212.227.17.7 permit
212.227.17.11 permit
212.227.17.12 permit
212.227.17.16 permit
212.227.17.17 permit
212.227.17.18 permit
212.227.17.19 permit
212.227.17.20 permit
212.227.17.21 permit
212.227.17.22 permit
@@ -2167,8 +2094,6 @@
216.205.24.0/24 permit
216.221.160.0/19 permit
216.239.32.0/19 permit
217.72.192.77 permit
217.72.192.78 permit
217.77.141.52 permit
217.77.141.59 permit
217.175.194.0/24 permit
@@ -2201,9 +2126,6 @@
2603:1030:20e:3::23c permit
2603:1030:b:3::152 permit
2603:1030:c02:8::14 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
2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit

View File

@@ -86,6 +86,12 @@
SOGoMaximumFailedLoginInterval = 900;
SOGoFailedLoginBlockInterval = 900;
// Enable SOGo URL Description for GDPR compliance, this may cause some issues with calendars and contacts. Also uncomment the encryption key below to use it.
//SOGoURLEncryptionEnabled = NO;
// Set a 16 character encryption key for SOGo URL Description, change this to your own value
//SOGoURLPathEncryptionKey = "SOGoSuperSecret0";
GCSChannelCollectionTimer = 60;
GCSChannelExpireAge = 60;

View File

@@ -49,6 +49,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
// Default to 1 yr
$_data["validity"] = 8760;
}
if (isset($_data["permanent"]) && filter_var($_data["permanent"], FILTER_VALIDATE_BOOL)) {
$permanent = 1;
}
else {
$permanent = 0;
}
$domain = $_data['domain'];
$description = $_data['description'];
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
@@ -65,13 +71,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false;
}
$validity = strtotime("+" . $_data["validity"] . " hour");
$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`) VALUES
(:address, :description, :goto, :validity)");
$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`, `permanent`) VALUES
(:address, :description, :goto, :validity, :permanent)");
$stmt->execute(array(
':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain,
':description' => $description,
':goto' => $username,
':validity' => $validity
':validity' => $validity,
':permanent' => $permanent
));
$_SESSION['return'][] = array(
'type' => 'success',
@@ -2103,15 +2110,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
if (empty($_data['validity'])) {
if (empty($_data['validity']) && empty($_data['permanent'])) {
continue;
}
$validity = round((int)time() + ($_data['validity'] * 3600));
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity WHERE
if (isset($_data['permanent']) && filter_var($_data['permanent'], FILTER_VALIDATE_BOOL)) {
$permanent = 1;
$validity = 0;
}
else if (isset($_data['validity'])) {
$permanent = 0;
$validity = round((int)time() + ($_data['validity'] * 3600));
}
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity, `permanent` = :permanent WHERE
`address` = :address");
$stmt->execute(array(
':address' => $address,
':validity' => $validity
':validity' => $validity,
':permanent' => $permanent
));
$_SESSION['return'][] = array(
'type' => 'success',
@@ -4584,10 +4599,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`description`,
`validity`,
`created`,
`modified`
`modified`,
`permanent`
FROM `spamalias`
WHERE `goto` = :username
AND `validity` >= :unixnow");
AND (`validity` >= :unixnow
OR `permanent` != 0)");
$stmt->execute(array(':username' => $_data, ':unixnow' => time()));
$tladata = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $tladata;
@@ -5162,7 +5179,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE (`kind` = '' OR `kind` = NULL) AND `domain` = :domain AND `username` != :username");
$stmt->execute(array(':domain' => $row['domain'], ':username' => $_data));
$MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow");
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND (`validity` >= :unixnow OR `permanent` != 0)");
$stmt->execute(array(':address' => $_data, ':unixnow' => time()));
$SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];

View File

@@ -4,7 +4,7 @@ function init_db_schema()
try {
global $pdo;
$db_version = "07102025_1015";
$db_version = "10312025_0525";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -554,7 +554,8 @@ function init_db_schema()
"description" => "TEXT NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"validity" => "INT(11)"
"validity" => "INT(11)",
"permanent" => "TINYINT(1) NOT NULL DEFAULT '0'"
),
"keys" => array(
"primary" => array(

View File

@@ -85,7 +85,7 @@ $AVAILABLE_LANGUAGES = array(
// 'ca-es' => 'Català (Catalan)',
'bg-bg' => 'Български (Bulgarian)',
'cs-cz' => 'Čeština (Czech)',
'da-dk' => 'Danish (Dansk)',
'da-dk' => 'Dansk (Danish)',
'de-de' => 'Deutsch (German)',
'en-gb' => 'English',
'es-es' => 'Español (Spanish)',

View File

@@ -175,6 +175,10 @@ jQuery(function($){
'</div>';
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
item.address = escapeHtml(item.address);
item.validity = {
value: item.validity,
permanent: item.permanent
};
}
else {
item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
@@ -218,9 +222,21 @@ jQuery(function($){
title: lang.alias_valid_until,
data: 'validity',
defaultContent: '',
createdCell: function(td, cellData) {
createSortableDate(td, cellData)
}
render: function (data, type) {
var date = new Date(data.value ? data.value * 1000 : 0);
switch (type) {
case "sort":
if (data.permanent) {
return 0;
}
return date.getTime();
default:
if (data.permanent) {
return lang.forever;
}
return date.toLocaleDateString(LOCALE, DATETIME_FORMAT);
}
},
},
{
title: lang.created_on,

View File

@@ -1992,6 +1992,7 @@ if (isset($_GET['query'])) {
break;
case "cors":
process_edit_return(cors('edit', $attr));
break;
case "identity-provider":
process_edit_return(identity_provider('edit', $attr));
break;

View File

@@ -19,7 +19,16 @@
"spam_alias": "Àlies temporals",
"spam_score": "Puntuació de correu brossa",
"tls_policy": "Política TLS",
"unlimited_quota": "Quota ilimitada per bústies de correo"
"unlimited_quota": "Quota ilimitada per bústies de correo",
"delimiter_action": "Acció delimitadora",
"domain_relayhost": "Canviar relayhost per un domini",
"extend_sender_acl": "Permetre extendre l'ACL del remitent per adreces externes",
"mailbox_relayhost": "Canvia el host de reenviament per una bústia",
"pushover": "Pushover",
"pw_reset": "Permetre el restabliment de la contrasenya de l'usuari mailcow",
"ratelimit": "Límit de peticions",
"smtp_ip_access": "Canvia hosts permesos per SMTP",
"sogo_access": "Permetre la gestió d'accés a SOGo"
},
"add": {
"activate_filter_warn": "All other filters will be deactivated, when active is checked.",
@@ -73,7 +82,25 @@
"validate": "Validar",
"validation_success": "Validated successfully",
"app_name": "Nom de l'aplicació",
"app_password": "Afegir contrasenya a l'aplicació"
"app_password": "Afegir contrasenya a l'aplicació",
"app_passwd_protocols": "Protocols autoritzats per la contrasenya de l'aplicació",
"bcc_dest_format": "La destinació c/o ha de ser una única adreça de correu vàlida.<br>Si necessiteu enviar una còpia a diverses adreces, creeu un àlies i utilitzeu-lo aquí.",
"comment_info": "Els comentaris privats no són visibles per l'usuari, mentre que els comentaris públics apareixen com una descripció emergent a la informació de l'usuari",
"custom_params": "Paràmetres personalitzats",
"custom_params_hint": "Correcte: --param=xy, incorrecte: --param xy",
"destination": "Destí",
"disable_login": "No permetre l'inici de sessió (els missatges entrants continuen sent acceptats)",
"domain_matches_hostname": "El domini %s coincideix amb el nom del servidor",
"dry": "Simular la sincronització",
"gal": "Llista d'adreces global",
"generate": "genereu",
"inactive": "Inactiu",
"internal": "Intern",
"internal_info": "Els àlies interns són només accessibles des del mateix domini o els àlies de dominis.",
"mailbox_quota_def": "Quota per defecte de la bústia",
"nexthop": "Següent salt",
"private_comment": "Comentari privat",
"public_comment": "Comentari púlbic"
},
"admin": {
"access": "Accés",

View File

@@ -149,7 +149,7 @@
"arrival_time": "Čas zařazení do fronty (čas na serveru)",
"authed_user": "Přihlášený uživatel",
"ays": "Opravdu chcete pokračovat?",
"ban_list_info": "Seznam blokovaných IP adres je zobrazen níže: <b>síť (zbývající čas blokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během několika sekund.<br />Červeně označené položky jsou pernamentní bloky z blacklistu.",
"ban_list_info": "Viz seznam zablokovaných IP níže: <b>síť (zbývající doba zablokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během pár sekund.<br />Červeně označeny jsou položky z trvalých seznamů.",
"change_logo": "Změnit logo",
"logo_normal_label": "Normální",
"logo_dark_label": "Inverzní pro tmavý režim",
@@ -183,16 +183,16 @@
"empty": "Žádné výsledky",
"excludes": "Vyloučit tyto příjemce",
"f2b_ban_time": "Doba blokování (s)",
"f2b_blacklist": "Sítě/hostitelé na blacklistu",
"f2b_blacklist": "Sítě či hostitelé na seznamu zákazů",
"f2b_filter": "Regex filtre",
"f2b_list_info": "Síť nebo hostitelé na blacklistu mají vždy větší váhu než položky na whitelistu. <b>Každá úprava seznamů trvá pár sekund.</b>",
"f2b_list_info": "Sítě či hostitelé na seznamu zákazů mají vždy větší váhu než položky na seznamu povolení. <b>Každá úprava seznamu trvá pár sekund.</b>",
"f2b_max_attempts": "Max. pokusů",
"f2b_netban_ipv4": "Rozsah IPv4 podsítě k zablokování (8-32)",
"f2b_netban_ipv6": "Rozsah IPv6 podsítě k zablokování (8-128)",
"f2b_parameters": "Parametry automatického firewallu",
"f2b_regex_info": "Záznamy které se berou v úvahu: SOGo, Postfix, Dovecot, PHP-FPM.",
"f2b_retry_window": "Časový horizont pro maximum pokusů (s)",
"f2b_whitelist": "Sítě/hostitelé na whitelistu",
"f2b_whitelist": "Sítě či hostitelé na seznamu povolení",
"filter_table": "Tabulka filtrů",
"forwarding_hosts": "Předávající servery",
"forwarding_hosts_add_hint": "Lze zadat IPv4/IPv6 adresy, sítě ve formátu CIDR, názvy serverů (budou převedeny na IP adresy) nebo názvy domén (budou převedeny na IP pomocí SPF záznamů, příp. MX záznamů).",
@@ -304,7 +304,7 @@
"rspamd_com_settings": "Název nastavení se vygeneruje automaticky, viz ukázky nastavení níže. Více informací viz <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd dokumentace</a>",
"rspamd_global_filters": "Mapa globálních filtrů",
"rspamd_global_filters_agree": "Budu opatrný!",
"rspamd_global_filters_info": "Mapa globálních filtrů obsahuje jiné globální black- a whitelisty.",
"rspamd_global_filters_info": "Mapa globálních filtrů obsahuje různé seznamy povolených a zakázaných serverů",
"rspamd_global_filters_regex": "Názvy stačí k vysvětlení. Položky musejí obsahovat jen platné regulární výrazy ve tvaru \"/vyraz/parametry\" (e.g. <code>/.+@domena\\.tld/i</code>).<br>\n Každý výraz bude podroben základní kontrole, přesto je možné Rspamd 'rozbít', nebude-li syntax zcela korektní.<br>\n Rspamd se pokusí po každé změně načíst mapu znovu. V případě potíží <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">restartujte Rspamd</a>, aby se konfigurace načetla explicitně.",
"rspamd_settings_map": "Nastavení Rspamd",
"sal_level": "Úroveň 'Moo'",
@@ -733,7 +733,7 @@
"sogo_visible_info": "Tato volba určuje objekty, jež lze zobrazit v SOGo (sdílené nebo nesdílené aliasy, jež ukazuje alespoň na jednu schránku).",
"spam_alias": "Vytvořit nebo změnit dočasné aliasy",
"spam_filter": "Spam filtr",
"spam_policy": "Přidat nebo odebrat položky whitelistu/blacklistu",
"spam_policy": "Přidat nebo odebrat položky seznamu",
"spam_score": "Nastavte vlastní skóre spamu",
"subfolder2": "Synchronizace do podsložky v cílovém umístění<br><small>(prázdné = nepoužívat podsložku)</small>",
"syncjob": "Upravit synchronizační úlohu",
@@ -883,7 +883,7 @@
"bcc": "BCC",
"bcc_destination": "Cíl kopie",
"bcc_destinations": "Cíl kopií",
"bcc_info": "<br/>Skrytá kopie (mapa BCC) se používá pro tiché předávání kopií všech zpráv na jinou adresu. Mapa příjemců se použije, funguje-li je místní cíl jako adresát zprávy. Totéž platí pro mapy odesílatelů.<br/>\n Místní cíl se nedozví, selže-li doručení na cíl BCC.",
"bcc_info": "Skrytá kopie (mapa BCC) se používá pro tiché předávání kopií všech zpráv na jinou adresu. Mapa příjemců se použije, funguje-li je místní cíl jako adresát zprávy. Totéž platí pro mapy odesílatelů.<br/>\n Místní cíl se nedozví, selže-li doručení na cíl BCC.",
"bcc_local_dest": "Týká se",
"bcc_map": "Skrytá kopie",
"bcc_map_type": "Typ skryté kopie",
@@ -1060,7 +1060,7 @@
"notified": "Oznámeno",
"qhandler_success": "Požadavek úspěšně přijat. Můžete nyní zavřít okno.",
"qid": "Rspamd QID",
"qinfo": "Karanténní systém uloží odmítnutou poštu do databáze (odesílatel se <em>nedozví</em>, že pošta byla doručena) jakož i pošta, která bude jako kopie doručena do složky Nevyžádaná pošta. \r\n<br>\"Naučit jako spam a smazat\" naučí zprávu jako spam přes Bayesian theorem a současně vypočítá fuzzy hashes pro odmítnutí podobných zpráv v budoucnosti. \r\n<br> Prosím, berte na vědomí, že naučení více zpráv může být - záleží na vašem systému - časově náročné . <br> Položky na černé listině jsou z karantény vyloučeny.",
"qinfo": "Karanténa uloží do databáze odmítnutou poštu (odesílatel se <em>nedozví</em>, že pošta byla doručena) jakož i poštu, jež se jako kopie doručuje do složky Nevyžádaná pošta.\n <br>\"Naučit jako spam a smazat\" předá zprávu systému k naučení bayesiánskou analýzou jako spam a současně stanoví fuzzy hashe pro odmítání podobných zpráv v budoucnosti.\n <br> Vezměte na vědomí, že učení více zpráv může být podle výkonnosti systému zabrat více času. <br> Položky na seznamu zákazů jsou z karantény vyloučeny.",
"qitem": "Položka v karanténě",
"quarantine": "Karanténa",
"quick_actions": "Akce",
@@ -1347,12 +1347,12 @@
"sogo_profile_reset": "Resetovat profil SOGo",
"sogo_profile_reset_help": "Tato volba odstraní uživatelský profil SOGo a <b>nenávratně vymaže všechna data</b>.",
"sogo_profile_reset_now": "Resetovat profil",
"spam_aliases": "Dočasné e-mailové aliasy",
"spam_aliases": "Spam aliasy",
"spam_score_reset": "Obnovit výchozí nastavení serveru",
"spamfilter": "Filtr spamu",
"spamfilter_behavior": "Hodnocení",
"spamfilter_bl": "Seznam zakázaných adres (blacklist)",
"spamfilter_bl_desc": "Zakázané emailové adresy budou <b>vždy</b> klasifikovány jako spam a odmítnuty. Odmítnutá pošta <b>nebude</b> uložena do karantény. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou poštovní schránkou), s výjimkou doménových košů a samotné poštovní schránky.",
"spamfilter_bl": "Seznam zákazů",
"spamfilter_bl_desc": "Zakázané emailové adresy budou <b>vždy</b> klasifikovány jako spam a odmítnuty. Odmítnutá pošta <b>se neukládá</b> do karantény. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou poštovní schránkou), s výjimkou doménových košů a samotné poštovní schránky.",
"spamfilter_default_score": "Výchozí hodnoty",
"spamfilter_green": "Zelená: tato zpráva není spam",
"spamfilter_hint": "První hodnota představuje \"nízké spam skóre\" a druhá \"vysoké spam skóre\".",
@@ -1363,7 +1363,7 @@
"spamfilter_table_empty": "Žádná data k zobrazení",
"spamfilter_table_remove": "smazat",
"spamfilter_table_rule": "Pravidlo",
"spamfilter_wl": "Seznam povolených adres (whitelist)",
"spamfilter_wl": "Seznam povolení",
"spamfilter_wl_desc": "Povolené emailové adresy <b>nebudou nikdy klasifikovány jako spam</b>. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou mailovou schránkou), s výjimkou doménových košů a samotné mailové schránky.",
"spamfilter_yellow": "Žlutá: tato zpráva může být spam, bude označena jako spam a přesunuta do složky nevyžádané pošty",
"status": "Stav",
@@ -1407,7 +1407,10 @@
"authentication": "Autentifikace",
"overview": "Přehled",
"protocols": "Protokoly",
"value": "Hodnota"
"value": "Hodnota",
"expire_never": "Nikdy nevyprší",
"forever": "Navždy",
"spam_aliases_info": "Spam alias je dočasná adresa, již lze použít k ochraně skutečných adres. <br>Případně lze nastavit také dobu platnosti, po níž je alias automaticky deaktivován, čímž se řeší případy zneužitých či odcizených adres."
},
"warning": {
"cannot_delete_self": "Nelze smazat právě přihlášeného uživatele",

View File

@@ -987,7 +987,7 @@
"sogo_visible": "Alias Sichtbarkeit in SOGo",
"sogo_visible_n": "Alias in SOGo verbergen",
"sogo_visible_y": "Alias in SOGo anzeigen",
"spam_aliases": "Temp. Alias",
"spam_aliases": "Spam-Alias",
"stats": "Statistik",
"status": "Status",
"sync_jobs": "Synchronisationen",
@@ -1281,7 +1281,9 @@
"encryption": "Verschlüsselung",
"excludes": "Ausschlüsse",
"expire_in": "Ungültig in",
"expire_never": "Niemals ungültig",
"fido2_webauthn": "FIDO2/WebAuthn",
"forever": "Für immer",
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
"from": "von",
"generate": "generieren",
@@ -1346,7 +1348,8 @@
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
"sogo_profile_reset_now": "Profil jetzt zurücksetzen",
"spam_aliases": "Temporäre E-Mail-Aliasse",
"spam_aliases": "Spam E-Mail-Aliasse",
"spam_aliases_info": "Ein Spam-Alias ist eine temporäre E-Mailadresse, die benutzt werden kann, um eine echte E-Mail Adressen zu schützen. <br>Optional kann eine Ablaufzeit gesetzt werden, sodass der Alias nach dem definierten Zeitraum automatisch deaktiviert wird, was missbrauchte oder geleakte Adressen effektiv entsorgt.",
"spam_score_reset": "Auf Server-Standard zurücksetzen",
"spamfilter": "Spamfilter",
"spamfilter_behavior": "Bewertung",

View File

@@ -1288,7 +1288,9 @@
"encryption": "Encryption",
"excludes": "Excludes",
"expire_in": "Expire in",
"expire_never": "Never Expire",
"fido2_webauthn": "FIDO2/WebAuthn",
"forever": "Forever",
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
"from": "from",
"generate": "generate",
@@ -1355,7 +1357,8 @@
"sogo_profile_reset": "Reset SOGo profile",
"sogo_profile_reset_help": "This will destroy a user's SOGo profile and <b>delete all contact and calendar data irretrievable</b>.",
"sogo_profile_reset_now": "Reset profile now",
"spam_aliases": "Temporary email aliases",
"spam_aliases": "Spam email aliases",
"spam_aliases_info": "A spam alias is a temporary email address that can be used to protect real email addresses. <br>Optionally, an expiration time can be set so that the alias is automatically deactivated after the defined period, effectively disposing of abused or leaked addresses.",
"spam_score_reset": "Reset to server default",
"spamfilter": "Spam filter",
"spamfilter_behavior": "Rating",

View File

@@ -1084,6 +1084,7 @@
"aliases_send_as_all": "No verificar permisos del remitente para los siguientes dominios (y sus aliases)",
"change_password": "Cambiar contraseña",
"create_syncjob": "Crear nuevo trabajo de sincronización",
"created_on": "Creado",
"daily": "Cada día",
"day": "Día",
"description": "Descripción",
@@ -1095,6 +1096,9 @@
"edit": "Editar",
"encryption": "Cifrado",
"excludes": "Excluye",
"expire_in": "Expirará en",
"expire_never": "Nunca expirará",
"forever": "Siempre",
"hour": "Hora",
"hourly": "Cada hora",
"hours": "Horas",
@@ -1115,7 +1119,8 @@
"shared_aliases": "Alias compartidos",
"shared_aliases_desc": "Los alias compartidos no se ven afectados por la configuración específica del usuario, como el filtro de correo no deseado o la política de cifrado. Los filtros de spam correspondientes solo pueden ser realizados por un administrador como una política de dominio.",
"sogo_profile_reset": "Resetear perfil SOGo",
"spam_aliases": "Alias de email temporales",
"spam_aliases": "Alias de email de spam",
"spam_aliases_info": "Un alias de spam es una dirección de correo electrónico temporal que se puede usar para proteger direcciones de correo electrónico reales. <br>Opcionalmente, se puede establecer un tiempo de expiración para que el alias se desactive automáticamente después del período definido, eliminando efectivamente las direcciones abusadas o filtradas.",
"spamfilter": "Filtro anti-spam",
"spamfilter_behavior": "Clasificación",
"spamfilter_bl": "Lista negra",

View File

@@ -16,7 +16,7 @@
"quarantine_notification": "Modifier la notification de quarantaine",
"quarantine_category": "Modifier la catégorie de la notification de quarantaine",
"ratelimit": "Limite d'envoi",
"recipient_maps": "Cartes destinataire",
"recipient_maps": "Cartes des destinataires",
"smtp_ip_access": "Changer les hôtes autorisés pour SMTP",
"sogo_access": "Autoriser la gestion des accès à SOGo",
"sogo_profile_reset": "Réinitialiser le profil SOGo",
@@ -109,7 +109,9 @@
"bcc_dest_format": "La destination Cci doit être une seule adresse de courriel valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
"tags": "Etiquettes",
"app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application",
"dry": "Simuler la synchronisation"
"dry": "Simuler la synchronisation",
"internal": "Interne",
"internal_info": "Les alias internes sont accessibles uniquement depuis le domaine ou les alias du domaine."
},
"admin": {
"access": "Accès",
@@ -407,7 +409,9 @@
"iam_host": "Hôte",
"iam_host_info": "Saisissez un ou plusieurs hôtes LDAP, séparés par des virgules.",
"iam_import_users": "Importer des utilisateurs",
"filter": "Filtrer"
"filter": "Filtrer",
"needs_restart": "nécessite un redémarrage",
"iam": "Fournisseur d'identité"
},
"danger": {
"access_denied": "Accès refusé ou données de formulaire non valides",
@@ -441,7 +445,7 @@
"global_filter_write_error": "Impossible décrire le fichier de filtre : %s",
"global_map_invalid": "ID de carte globale %s non valide",
"global_map_write_error": "Impossible décrire lID de la carte globale %s : %s",
"goto_empty": "Une adresse alias doit contenir au moins une adresse 'goto'valide",
"goto_empty": "Une adresse alias doit contenir au moins une adresse 'goto' valide",
"goto_invalid": "Adresse Goto %s non valide",
"ham_learn_error": "Erreur d'apprentissage Ham : %s",
"imagick_exception": "Erreur : Exception Imagick lors de la lecture de limage",
@@ -548,7 +552,11 @@
"generic_server_error": "Une erreur de serveur inattendue s'est produite. Veuillez contacter votre administrateur.",
"authsource_in_use": "Le fournisseur d'identité ne peut pas être modifié ou supprimé car il est actuellement utilisé par un ou plusieurs utilisateurs.",
"iam_test_connection": "Échec de la connexion",
"required_data_missing": "La donnée requise %s est manquante"
"required_data_missing": "La donnée requise %s est manquante",
"max_age_invalid": "L'âge maximum %s est invalide",
"mode_invalid": "Le mode %s est invalide",
"mx_invalid": "L'enregistrement MX %s est invalide",
"version_invalid": "La version %s est invalide"
},
"debug": {
"chart_this_server": "Graphique (ce serveur)",
@@ -693,7 +701,7 @@
"spam_score": "Définir un score spam personnalisé",
"subfolder2": "Synchronisation dans le sous-dossier sur la destination<br><small>(vide = ne pas utiliser de sous-dossier)</small>",
"syncjob": "Modifier la tâche de synchronisation",
"target_address": "Adresse(s) Goto<small>(séparé(s) par des virgules)</small>",
"target_address": "Adresse(s) Goto <small>(séparé(s) par des virgules)</small>",
"target_domain": "Domaine cible",
"timeout1": "Délai de connexion à lhôte distant",
"timeout2": "Délai de connexion à lhôte local",
@@ -734,7 +742,20 @@
"mailbox_rename_alias": "Créer un alias automatiquement",
"sogo_access": "Redirection directe vers SOGo",
"pushover": "Pushover",
"pushover_sound": "Son"
"pushover_sound": "Son",
"internal": "Interne",
"internal_info": "Les alias internes sont accessibles uniquement depuis le domaine ou les alias du domaine.",
"mta_sts": "MTA-STS",
"mta_sts_version": "Version",
"mta_sts_version_info": "Défini la version du standard MTA-STS actuellement seul <code>STSv1</code> est valide.",
"mta_sts_mode": "Mode",
"mta_sts_max_age": "Âge maximum",
"mta_sts_mx": "Serveur MX",
"mta_sts_mx_notice": "Plusieurs serveurs MX peuvent être spécifiés (séparés par des virgules).",
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> est un standard qui oblige la délivrance des courriels entre les serveurs de courriels à utiliser TLS avec des certificats valides. <br>Il est utilisé quand <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> n'est pas possible à cause d'un manque ou d'un non support de DNSSEC.<br><b>Note</b> : Si le domaine du destinataire supporte DANE avec DNSSEC, DANE est <b>toujours</b> préféré MTA-STS sert seulement en secours.",
"mta_sts_mode_info": "Il y a trois modes parmi lesquels choisir :<ul><li><em>testing</em> la politique est seulement surveillée, les violations n'ont pas d'impact.</li><li><em>enforce</em> la politique est appliquée strictement, les connexions sans TLS valide sont rejetées.</li><li><em>none</em> la politique est publiée mais non appliquée.</li></ul>",
"mta_sts_max_age_info": "Durée en secondes pendant laquelle les serveurs de courriel peuvent mettre en cache cette politique avant de revérifier.",
"mta_sts_mx_info": "Autoriser l'envoi uniquement aux noms d'hôtes des serveurs de courriels indiqués explicitement ; le MTA émetteur vérifie si le nom d'hôte DNS du MX correspond à la liste de la politique, et autorise la délivrance seulement avec un certificat TLS valide (protège contre le MITM)."
},
"footer": {
"cancel": "Annuler",
@@ -789,7 +810,8 @@
"login_linkstext": "L'identifiant n'est pas correct ?",
"login_usertext": "Se connecter en tant qu'utilisateur",
"login_domainadmintext": "Se connecter en tant qu'administrateur du domaine",
"login_admintext": "Se connecter en tant qu'administrateur"
"login_admintext": "Se connecter en tant qu'administrateur",
"email": "Adresse de courriel"
},
"mailbox": {
"action": "Action",
@@ -896,7 +918,7 @@
"recipient_map_new_info": "La destination de la carte du destinataire doit être une adresse de courriel valide ou un nom de domaine.",
"recipient_map_old": "Destinataire original",
"recipient_map_old_info": "La destination originale des cartes des destinataires doit être une adresse de courriel valide ou un nom de domaine.",
"recipient_maps": "Cartes des bénéficiaires",
"recipient_maps": "Cartes des destinataires",
"relay_all": "Relayer tous les destinataires",
"remove": "Supprimer",
"resources": "Ressources",
@@ -965,7 +987,8 @@
"syncjob_check_log": "Vérifier le journal",
"recipient": "Destinataire",
"open_logs": "Afficher les journaux",
"iam": "Fournisseur d'identité"
"iam": "Fournisseur d'identité",
"internal": "Interne"
},
"oauth2": {
"access_denied": "Veuillez vous connecter en tant que propriétaire de la boîte de réception pour accorder laccès via Oauth2.",
@@ -1222,7 +1245,7 @@
"email_and_dav": "Courriel, calendriers et contacts",
"encryption": "Chiffrement",
"excludes": "Exclus",
"expire_in": "Expire dans",
"expire_in": "Expirer dans",
"force_pw_update": "Vous <b>devez</b> définir un nouveau mot de passe pour pouvoir accéder aux services liés aux logiciels de groupe.",
"generate": "générer",
"hour": "heure",
@@ -1350,7 +1373,12 @@
"mailbox_general": "Général",
"mailbox_settings": "Paramètres",
"tfa_info": "L'authentification à deux facteurs permet de protéger votre compte. Si vous l'activez, vous aurez besoin de mots de passe d'application pour vous connecter à des applications ou des services qui ne prennent pas en charge l'authentification à deux facteurs (par exemple les clients e-mails).",
"overview": "Vue d'ensemble"
"overview": "Vue d'ensemble",
"expire_never": "Ne jamais expirer",
"forever": "Pour toujours",
"spam_aliases_info": "Un alias de spam est une adresse de courriel temporaire qui peut être utilisée pour protéger les véritables adresses de courriel. <br> De manière optionnelle, une durée d'expiration peut être définie afin que l'alias soit automatiquement désactivé après la période définie, éliminant ainsi les adresses étant abusées ou ayant fuité.",
"authentication": "Authentification",
"protocols": "Protocoles"
},
"warning": {
"cannot_delete_self": "Impossible de supprimer lutilisateur connecté",

View File

@@ -6,7 +6,8 @@
"weeks": "Εβδομάδες",
"with_app_password": "με κωδικό εφαρμογής",
"year": "χρόνος",
"years": "χρόνια"
"years": "χρόνια",
"value": "Τιμή"
},
"warning": {
"cannot_delete_self": "Αδυναμία διαγραφής συνδεδεμένου χρήστη",
@@ -16,5 +17,170 @@
"hash_not_found": "Η κατακερματισμένη τιμή (hash value) δεν βρέθηκε ή έχει είδη διαγραφεί.",
"ip_invalid": "Παραλείφθηκε μη έγκυρη διεύθυνση IP: %s",
"is_not_primary_alias": "Παραλείφθηκε μη πρωτεύον ψευδώνυμο %s"
},
"acl": {
"alias_domains": "Προσθήκη ψευδωνύμων τομέων",
"app_passwds": "Διαχείριση κωδικών εφαρμογής",
"bcc_maps": "χαρτογράφηση BCC",
"delimiter_action": "Ενέργεια οριοθέτη",
"domain_desc": "Αλλαγή περιγραφής τομέα",
"domain_relayhost": "Αλλαγή του διακομιστή αναμετάδοσης για ένα τομέα",
"eas_reset": "Επαναφορά συσκευών EAS",
"extend_sender_acl": "Να επιτρέπεται η επέκταση ACL του αποστολέα με εξωτερικές διευθύνσεις",
"filters": "Φίλτρα",
"login_as": "Είσοδος ως χρήστης e-mail",
"mailbox_relayhost": "Αλλαγή διακομιστή αναμετάδοσης για ένα γραμματοκιβώτιο",
"prohibited": "Απαγορεύεται από την ACL",
"protocol_access": "Αλλαγή πρόσβασης πρωτοκόλλου",
"pushover": "Pushover",
"pw_reset": "Επιτρέψτε την επαναφορά κωδικού πρόσβασης του χρήστη",
"quarantine": "Ενέργειες καραντίνας",
"quarantine_attachments": "Συνημμένα καραντίνας",
"quarantine_category": "Αλλαγή κατηγορίας ειδοποιήσεων καραντίνας",
"quarantine_notification": "Αλλαγή ειδοποιήσεων καραντίνας",
"ratelimit": "Όριο τιμής",
"recipient_maps": "Χάρτες παραληπτών",
"smtp_ip_access": "Αλλαγή επιτρεπόμενων διακομιστών SMTP",
"sogo_access": "Επιτρέψτε τη διαχείριση της πρόσβασης στο SOGo",
"sogo_profile_reset": "Επαναφορά του προφίλ SOGo",
"spam_alias": "Προσωρινά ψευδώνυμα",
"spam_policy": "Λίστα απορρίψεων/Λίστα επιτρεπόμενων",
"spam_score": "Βαθμολογία ανεπιθύμητης αλληλογραφίας",
"syncjobs": "Εργασίες συγχρονισμού",
"tls_policy": "Πολιτική TLS",
"unlimited_quota": "Απεριόριστο όριο για γραμματοκιβώτια"
},
"add": {
"activate_filter_warn": "Όλα τα άλλα φίλτρα θα απενεργοποιηθούν, όταν επιλεγεί η επιλογή \"ενεργό\".",
"active": "Ενεργό",
"add": "Προσθήκη",
"add_domain_only": "Προσθήκη μόνο του τομέα",
"add_domain_restart": "Προσθήκη του τομέα και επανεκκίνηση του SOGo",
"alias_address": "Διευθύνσεις ψευδωνύμων",
"alias_address_info": "<small>Πλήρης διεύθυνση(εις) e-mail ή @example.com, για να λαμβάνετε ΟΛΑ τα μηνύματα ενός τομέα (χωρισμένα με κόμα). <b>μόνο τομείς του mailcow</b>.</small>",
"alias_domain": "Ψευδώνυμο τομέα",
"alias_domain_info": "<small>Μόνο έγκυρα ονόματα τομέα (χωρισμένα με κόμα).</small>",
"app_name": "Όνομα εφαρμογής",
"app_password": "Προσθήκη κωδικού εφαρμογής",
"app_passwd_protocols": "Επιτρεπόμενα πρωτόκολλα για κωδικούς εφαρμογών",
"automap": "Αυτόματη αντιστοίχηση φακέλων (\"Απεσταλμένα μηνύματα\", \"Απεσταλμένα\" => \"Στάλθηκαν\" κ.τ.λ.)",
"backup_mx_options": "Επιλογές αναμετάδοσης",
"bcc_dest_format": "Η BCC διεύθυνση πρέπει να είναι μία και έγκυρη διεύθυνση e-mail.<br>Αν θέλετε να στείλετε αντίγραφα σε πολλούς παραλήπτες, δημιουργήστε ένα ψευδόνυμο για όλους και χρησιμοποιήστε το εδώ.",
"comment_info": "Τα προσωπικά σχόλια δεν είναι ορατά στον χρήστη. Τα δημόσια σχόλια εμφανίζονται ως tooltips.",
"custom_params": "Προσαρμοσμένες παράμετροι",
"custom_params_hint": "Σωστή σύνταξη: --param=xy, λάθος σύνταξη: --param xy",
"delete1": "Διαγραφή όταν ολοκληρωθεί",
"delete2": "Διαγραφή μηνυμάτων στον προορισμό που δεν βρίσκονται στην πηγή",
"delete2duplicates": "Διαγραφή διπλότυπων στον προορισμό",
"description": "Περιγραφή",
"destination": "Προορισμός",
"disable_login": "Απαγόρευση εισόδου (η εισερχόμενη αλληλογραφία εξακολουθεί να γίνεται δεκτή)",
"domain": "Τομέας",
"domain_matches_hostname": "Ο τομέας %s είναι ο ίδιος με το όνομα του διακομιστή",
"domain_quota_m": "Συνολικό όριο τομέα (MiB)",
"dry": "Προσομοίωση συγχρονισμού",
"enc_method": "Μέθοδος κρυπτογράφησης",
"exclude": "Εξαίρεση αντικειμένων (regex)",
"full_name": "Πλήρες όνομα",
"gal": "Κοινόχρηστη λίστα διευθύνσεων"
},
"danger": {
"unknown": "Παρουσιάστηκε κάποιο άγωνστο σφάλμα",
"unknown_tfa_method": "Άγνωστη μέθοδος TFA",
"unlimited_quota_acl": "Το απεριόριστο όριο απαγορεύεται από την ACL",
"username_invalid": "Το όνομα χρήστη %s δεν μπορεί να χρησιμοποιηθεί",
"validity_missing": "Παρακαλώ ορίστε μία περίοδο εγκυρότητας",
"value_missing": "Παρακαλώ συμπληρώστε όλα τα δεδομένα",
"version_invalid": "Η έκδοση %s δεν είναι έγκυρη",
"yotp_verification_failed": "Η επαλήθευση μέσω Yubico OTP απέτυχε: %s"
},
"datatables": {
"collapse_all": "Σύμπτυξη όλων",
"decimal": ".",
"emptyTable": "Δεν υπάρχουν εγγραφές",
"expand_all": "Επέκταση όλων",
"info": "Εμφανίζονται _START_ εώς _END_ από _TOTAL_ εγγραφές",
"infoEmpty": "Εμφανίζονται 0 εώς 0 από 0 εγγραφές",
"infoFiltered": "(φιλτραρισμένες από _MAX_ συνολικές εγγραφές)",
"thousands": ",",
"lengthMenu": "Εμφάνιση _MENU_ εγγραφών",
"loadingRecords": "Γίνεται φόρτωση...",
"processing": "Παρακαλώ περιμένετε...",
"search": "Αναζήτηση:",
"zeroRecords": "Δε βρέθηκαν εγγραφές",
"paginate": {
"first": "Πρώτη",
"last": "Τελευταία",
"next": "Επόμενη",
"previous": "Προηγούμενη"
},
"aria": {
"sortAscending": ": ενεργοποίηση αύξουσας ταξινόμησης",
"sortDescending": ": ενεργοποίηση φθίνουσας ταξινόμησης"
}
},
"debug": {
"architecture": "Αρχιτεκτονική",
"chart_this_server": "Γράφημα (αυτός ο διακομιστής)",
"containers_info": "Πληροφορίες για τον container",
"container_running": "Εκτελείται",
"container_disabled": "Ο container έχει σταματήσει ή απενεργοποιηθεί",
"container_stopped": "Σταματημένος",
"cores": "Πυρήνες",
"current_time": "Ώρα συστήματος",
"disk_usage": "Χρήση αποθ. χώρου",
"docs": "Έγγραφα",
"error_show_ip": "Δεν είναι δυνατή η επίλυση της δημόσιας IP διεύθυνσης",
"external_logs": "Εξωτερικά αρχεία καταγραφής",
"history_all_servers": "Ιστορικό (Όλοι οι διακομιστές)",
"in_memory_logs": "Αρχεία καταγραφής στη μνήμη",
"last_modified": "Τελευταία τροποποίηση",
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\n <p><b>External logs</b> are collected via API of the given application.</p>\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
"login_time": "Ώρα",
"logs": "Αρχεία καταγραφής",
"memory": "Μνήμη",
"online_users": "Συνδεδεμένοι χρήστες",
"restart_container": "Επανεκκίνηση",
"service": "Υπηρεσία",
"show_ip": "Εμφάνιση δημόσιας IP",
"size": "Μέγεθος",
"started_at": "Ξεκίνησε στις",
"started_on": "Ξεκίνησε στις",
"static_logs": "Στατικά αρχεία καταγραφής",
"success": "Επιτυχία",
"system_containers": "Σύστημα και Containers",
"timezone": "Ζώνη ώρας",
"uptime": "Χρόνος λειτουργίας",
"update_available": "Υπάρχει διαθέσιμη ενημέρωση",
"no_update_available": "Έχετε τη τελευταία έκδοση του συστήματος",
"update_failed": "Δεν ήταν δυνατός ο έλεγχος για ενημερώσεις",
"username": "Όνομα χρήστη",
"wip": "Currently Work in Progress"
},
"diagnostics": {
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
"dns_records": "Εγγραφές DNS",
"dns_records_24hours": "Παρακαλώ σημειώστε ότι οι αλλαγές στο DNS μπορεί να χρειαστούν μέχρι 24 ώρες για να ενημερωθούν σωστά και να εμφανιστούν σε αυτή τη σελίδα. Ο σκοπός της είναι να δείτε πως μπορείτε να ρυθμίσετε σωστά τις εγγραφές DNS και να ελέγξετε αν είναι σωστές.",
"dns_records_data": "Σωστά δεδομένα",
"dns_records_docs": "Παρακαλώ συμβουλευτείτε επίσης <a target=\"_blank\" href=\"https://docs.mailcow.email/getstarted/prerequisite-dns\">την τεκμηρίωση</a>.",
"dns_records_name": "Όνομα",
"dns_records_status": "Τρέχουσα κατάσταση",
"dns_records_type": "Τύπος",
"optional": "Αυτή η εγγραφή είναι προαιρετική."
},
"edit": {
"acl": "ACL (Δικαίωμα)",
"active": "Ενεργό",
"admin": "Επεξεργασία διαχειριστή",
"advanced_settings": "Ρυθμίσεις για προχωρημένους",
"alias": "Επεξεργασία ψευδώνυμου",
"allow_from_smtp": "Επέτρεψε μόνο σε αυτές τις IPs να χρησιμοποιήσουν το <b>SMTP</b>",
"allow_from_smtp_info": "Αφήστε το κενό για να επιτρέψετε όλους τους αποστολείς.<br>IPv4/IPv6 διευθύνσεις και δίκτυα.",
"allowed_protocols": "Επιτρεπόμενα πρωτόκολλα για απ' ευθείας πρόσβαση από τους χρήστες (δεν επηρεάζει τα πρωτόκολλα κωδικών πρόσβασης εφαρμογής)",
"app_name": "Όνομα εφαρμογής",
"app_passwd": "Κωδικός πρόσβασης εφαρμογής",
"app_passwd_protocols": "Επιτρέπομενα πρωτόκολλα για τον κωδικό εφαρμογής",
"automap": "Αυτόματη αντιστοίχηση φακέλων (\"Απεσταλμένα μηνύματα\", \"Απεσταλμένα\" => \"Στάλθηκαν\" κ.τ.λ.)",
"backup_mx_options": "Επιλογές αναμετάδοσης"
}
}

View File

@@ -1187,6 +1187,7 @@
"created_on": "作成日",
"daily": "毎日",
"day": "日",
"description": "説明",
"delete_ays": "削除プロセスを確認してください。",
"direct_aliases": "直接エイリアスアドレス",
"direct_aliases_desc": "直接エイリアスアドレスは、スパムフィルターおよびTLSポリシー設定の影響を受けます。",
@@ -1201,7 +1202,9 @@
"encryption": "暗号化",
"excludes": "除外",
"expire_in": "有効期限まで",
"expire_never": "有効期限なし",
"fido2_webauthn": "FIDO2/WebAuthn",
"forever": "有効期限なし",
"force_pw_update": "グループウェア関連サービスにアクセスするには、新しいパスワードを<b>必ず</b>設定する必要があります。",
"from": "送信元",
"generate": "生成",

View File

@@ -185,11 +185,12 @@
"protocol_access": "Endre protokolltilgang",
"pushover": "Pushover",
"quarantine": "Karantenehandlinger",
"quarantine_attachments": "Sett vedlegg i karantene",
"quarantine_attachments": "Se vedlegg i karantene",
"quarantine_category": "Endre varslingskategori for karantene",
"quarantine_notification": "Endre karantenevarslinger",
"domain_desc": "Endre domenebeskrivelse",
"extend_sender_acl": "Tillat utvidelse av sender-ACL fra eksterne adresser"
"extend_sender_acl": "Tillat utvidelse av sender-ACL fra eksterne adresser",
"pw_reset": "Tillat endring av brukerpassord"
},
"add": {
"app_passwd_protocols": "Tillatte protokoller for app-passord",

File diff suppressed because it is too large Load Diff

View File

@@ -109,7 +109,9 @@
"timeout2": "Тайм-аут для подключения к локальному хосту",
"username": "Имя пользователя",
"validate": "Проверить",
"validation_success": "Проверка прошла успешно"
"validation_success": "Проверка прошла успешно",
"internal": "Внутренний",
"internal_info": "Внутренние псевдонимы доступны только из самого домена или доменов-псевдонимов."
},
"admin": {
"access": "Настройки доступа",
@@ -550,7 +552,11 @@
"generic_server_error": "На сервере произошла непредвиденная ошибка. Пожалуйста, свяжитесь с вашим администратором.",
"authsource_in_use": "Поставщик идентификационных данных не может быть изменен или удален, так как в данный момент он используется одним или несколькими пользователями.",
"iam_test_connection": "Ошибка соединения",
"required_data_missing": "Отсутствуют необходимые данные %s"
"required_data_missing": "Отсутствуют необходимые данные %s",
"max_age_invalid": "Максимальный возраст %s недействителен",
"mode_invalid": "Режим %s недействителен",
"mx_invalid": "Запись MX %s недействительна",
"version_invalid": "Версия %s недействительна"
},
"datatables": {
"collapse_all": "Свернуть все",
@@ -762,7 +768,20 @@
"title": "Изменение объекта",
"unchanged_if_empty": "Если без изменений - оставьте пустым",
"username": "Имя пользователя",
"validate_save": "Подтвердить и сохранить"
"validate_save": "Подтвердить и сохранить",
"internal": "Внутренний",
"internal_info": "Внутренние псевдонимы доступны только из самого домена или доменов-псевдонимов.",
"mta_sts": "MTA-STS",
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> — это стандарт, который обязывает почтовые серверы использовать TLS с подлинными сертификатами для доставки электронной почты.<br>Он используется, когда <a target='_blank' href='https://ru.wikipedia.org/wiki/DANE'>DANE</a> невозможен из-за неиспользуемого или неподдерживаемого DNSSEC.<br><b>Примечание</b>: если принимающий домен поддерживает DANE с DNSSEC, <b>всегда</b> предпочитается DANE — MTA-STS действует только как резервный вариант.",
"mta_sts_version": "Версия",
"mta_sts_version_info": "Определяет версию стандарта MTA-STS на данный момент существует только <code>STSv1</code>.",
"mta_sts_mode": "Режим",
"mta_sts_mode_info": "Есть три режима на выбор:<ul><li><em>testing</em> политика только наблюдается, нарушения не имеют последствий.</li><li><em>enforce</em> политика соблюдается строго, соединения без подлинного TLS отклоняются.</li><li><em>none</em> политика опубликована, но не применяется.</li></ul>",
"mta_sts_max_age": "Максимальный возраст",
"mta_sts_max_age_info": "Время в секундах, в течение которого принимающие почтовые серверы могут кэшировать эту политику перед повторной загрузкой.",
"mta_sts_mx": "Сервер MX",
"mta_sts_mx_info": "Разрешает отправку только на явно указанные имена хостов почтовых серверов; отправляющий MTA проверяет, соответствует ли DNS-имя MX-хоста списку политик, и разрешает доставку только с подлинным TLS-сертификатом (защита от MITM).",
"mta_sts_mx_notice": "Можно указать несколько MX-серверов (через запятую)."
},
"fido2": {
"confirm": "Подтвердить",
@@ -832,7 +851,8 @@
"login_admintext": "Войти как администратор",
"login_user": "Вход для пользователей",
"login_dadmin": "Вход для администраторов домена",
"login_admin": "Вход для администраторов"
"login_admin": "Вход для администраторов",
"email": "Email-адрес"
},
"mailbox": {
"action": "Действия",
@@ -1008,7 +1028,8 @@
"waiting": "В ожидании",
"weekly": "Раз в неделю",
"yes": "&#10003;",
"iam": "Поставщик идентификационных данных"
"iam": "Поставщик идентификационных данных",
"internal": "Внутренний"
},
"oauth2": {
"access_denied": "Пожалуйста, войдите в систему как владелец почтового аккаунта, чтобы получить доступ через OAuth2.",
@@ -1331,7 +1352,7 @@
"sogo_profile_reset": "Сбросить профиль SOGo",
"sogo_profile_reset_help": "<b>Внимание:</b> это удалит настройки профиля SOGo вместе с <b>всеми контактами, календарями и фильтрами безвозвратно</b>.",
"sogo_profile_reset_now": "Сбросить профиль сейчас",
"spam_aliases": "Временные псевдонимы электронной почты",
"spam_aliases": "Псевдонимы для спама",
"spam_score_reset": "Сброс на настройки по умолчанию",
"spamfilter": "Спам фильтр",
"spamfilter_behavior": "Фильтрация спама",
@@ -1387,7 +1408,10 @@
"authentication": "Аутентификация",
"tfa_info": "Двухфакторная аутентификация помогает защитить вашу учетную запись. Если вы включите эту функцию, вам понадобятся пароли приложений для входа в приложения или службы, которые не поддерживают двухфакторную аутентификацию (например, почтовые клиенты).",
"protocols": "Протоколы",
"overview": "Обзор"
"overview": "Обзор",
"expire_never": "Никогда не истекает",
"forever": "Навсегда",
"spam_aliases_info": "Псевдоним для спама — это временный адрес электронной почты, который можно использовать для защиты реальных адресов.<br>При желании можно установить срок действия, по истечении которого псевдоним будет автоматически деактивирован, что позволяет эффективно избавляться от адресов, которые были использованы не по назначению или стали доступны посторонним лицам."
},
"warning": {
"cannot_delete_self": "Вы не можете удалить сами себя",

View File

@@ -297,7 +297,7 @@
"forwarding_hosts_add_hint": "Lahko vpišete IPv4/IPv6 naslove, mreže v CIDR obliki, imena gostiteljev (kateri se prevedejo v IP naslove) ali imena domen (katera se prevedejo v IP naslove glede na poizvedbo po SPF zapisih, v primeru manjkajočih zapisov pa MX zapisih).",
"forwarding_hosts_hint": "Dohodna sporočila so brezpogojno sprejeta od katerih koli gostiteljev v tem seznamu. Ti gostitelji se ne bodo preverjali po DNSBL seznamih in ne bodo dodani v listo sivih. Prejeta neželena pošta s teh gostiteljev ni nikoli zavrnjena, opcijsko pa se lahko premakne v mapo neželene pošte. Najpogostejša uporaba za to je navedba poštnih strežnikov, iz katerih ste nastavili pravilo za posredovanje pošte na vaš mailcow strežnik.",
"license_info": "Licenca ni zahtevana, a pomaga pri nadaljnjem razvoju. <br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"Naročilo SAL\">Registrirajte svoj GUID tukaj</a> ali <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Naročilo podpore\">Kupite podporo za svojo namestitev Mailcow.</a>",
"lookup_mx": "Cilj je regularni izraz, ki se ujema z imenom MX (<code>.*\\.google\\.com</code> za usmerjanje vse pošte, usmerjene na MX, ki se konča na google.com, prek tega skoka).",
"lookup_mx": "Cilj je regularni izraz, ki se ujema z imenom MX (<code>.*\\.google\\.com</code> za usmerjanje vse pošte, usmerjene na MX, ki se konča na google.com, prek tega skoka)",
"main_name": "Naziv \"mailcow UI\"",
"merged_vars_hint": "Sive vrstice so združene iz <code>vars.(local.)inc.php</code> in jih ni mogoče spremeniti.",
"oauth2_info": "Implementacija OAuth2 podpira vrsto odobritve »Avtorizacijska koda« in izda osvežilne žetone.<br>\nStrežnik samodejno izda tudi nove osvežilne žetone, ko je žeton za osvežitev uporabljen.<br><br>\n&#8226; Privzeti obseg je <i>profile</i>. Prek OAuth2 je mogoče overiti samo uporabnike poštnega predala. Če parameter obsega izpustite, se vrne na <i>profile</i>.<br>\n&#8226; Parameter <i>state</i> mora odjemalec poslati kot del zahteve za avtorizacijo.<br><br>\nPoti za zahteve do API-ja OAuth2: <br>\n<ul>\n<li>Končna točka avtorizacije: <code>/oauth/authorize</code></li>\n<li>Končna točka žetona: <code>/oauth/token</code></li>\n<li>Stran z viri: <code>/oauth/profile</code></li>\n</ul>\nPonovno ustvarjanje skrivnosti odjemalca ne bo poteklo obstoječih kod za avtorizacijo, vendar ne bo obnovilo žetona.<br><br>\nPreklic žetonov odjemalca bo povzročil takojšnjo prekinitev vseh aktivnih sej. Vse stranke se morajo ponovno overiti.",
@@ -414,7 +414,7 @@
"needs_restart": "potreben je ponovni zagon"
},
"danger": {
"alias_goto_identical": "Vzdevek in ciljni naslov se ne smeta ujemati.",
"alias_goto_identical": "Vzdevek in ciljni naslov se ne smeta ujemati",
"aliasd_targetd_identical": "Vzdevek domene ne sme biti enak ciljni domeni: %s",
"bcc_exists": "Za tip %s obstaja BCC preslikava %s",
"dkim_domain_or_sel_exists": "DKIM ključ za \"%s\" obstaja in ne bo prepisan",
@@ -651,7 +651,7 @@
"pushover_title": "Naslov obvestila",
"domains": "Domene",
"extended_sender_acl_info": "Uvoziti je treba ključ domene DKIM, če je na voljo.<br>\n Ne pozabite dodati tega strežnika v ustrezni zapis SPF TXT.<br>\n Kadar koli je temu strežniku dodana domena ali vzdevek domene, ki se prekriva z zunanjim naslovom, se zunanji naslov odstrani.<br>\n Uporabite @domain.tld, da omogočite pošiljanje kot *@domain.tld.",
"lookup_mx": "Cilj je regularni izraz, ki se ujema z imenom MX (<code>.*\\.google\\.com</code> za usmerjanje vse pošte, usmerjene na MX, ki se konča na google.com, prek tega skoka).",
"lookup_mx": "Cilj je regularni izraz, ki se ujema z imenom MX (<code>.*\\.google\\.com</code> za usmerjanje vse pošte, usmerjene na MX, ki se konča na google.com, prek tega skoka)",
"maxbytespersecond": "Največ bajtov na sekundo <br><small>(0 = neomejeno)</small>",
"pushover_sender_array": "Upoštevaj samo sledeče e-poštne naslove pošiljateljev <small>(ločeni z vejico)</small>",
"mbox_rl_info": "Ta omejitev se uporabi za prijavno ime SASL in se ujema z naslovom \"od\", ki ga uporablja prijavljeni uporabnik. Omejitev poštnega nabiralnika preglasi omejitev za celotno domeno.",
@@ -1359,7 +1359,7 @@
"sogo_profile_reset": "Ponastavi profil SOGo",
"sogo_profile_reset_help": "S tem boste uničili uporabnikov profil SOGo in <b>nepovratno izbrisali vse stike in podatke koledarja</b>.",
"sogo_profile_reset_now": "Ponastavi profil zdaj",
"spam_aliases": "Začasni vzdevki e-pošte",
"spam_aliases": "Vzdevki neželene e-pošte",
"spam_score_reset": "Ponastavi na privzete nastavitve strežnika",
"spamfilter": "Filter neželene pošte",
"spamfilter_behavior": "Ocena",
@@ -1407,7 +1407,10 @@
"years": "leta",
"waiting": "Čakanje",
"q_all": "Vse kategorije",
"syncjob_EX_OK": "Uspeh"
"syncjob_EX_OK": "Uspeh",
"expire_never": "Nikoli ne poteče",
"forever": "Za vedno",
"spam_aliases_info": "Vzdevek za neželeno pošto je začasni e-poštni naslov, ki ga je mogoče uporabiti za zaščito pravih e-poštnih naslovov. <br>Po želji je mogoče nastaviti čas poteka veljavnosti, tako da se vzdevek po določenem obdobju samodejno deaktivira, s čimer se učinkovito znebite zlorabljenih ali razkritih naslovov."
},
"warning": {
"cannot_delete_self": "Prijavljenega uporabnika ni mogoče izbrisati",

View File

@@ -691,6 +691,7 @@
"internal": "Nội bộ",
"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"
"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)"
}
}

View File

@@ -8,6 +8,7 @@
</div>
<div id="collapse-tab-SpamAliases" class="card-body collapse" data-bs-parent="#user-content">
<div class="row">
<p>{{ lang.user.spam_aliases_info|raw }}</p>
<div class="col-md-12 col-sm-12 col-12">
<table id="tla_table" class="table table-striped dt-responsive w-100"></table>
</div>
@@ -18,12 +19,13 @@
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="tla" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"1"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.hour }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"24"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.day }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"168"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.week }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"744"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.month }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"8760"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.year }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"87600"}' href="#">{{ lang.user.expire_in }} 10 {{ lang.user.years }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"1","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.hour }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"24","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.day }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"168","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.week }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"744","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.month }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"8760","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.year }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"87600","permanent":"0"}' href="#">{{ lang.user.expire_in }} 10 {{ lang.user.years }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"permanent":"1"}' href="#">{{ lang.user.expire_never }}</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" data-action="delete_selected" data-id="tla" data-api-url='delete/time_limited_alias' href="#">{{ lang.mailbox.remove }}</a></li>
</ul>

View File

@@ -117,7 +117,7 @@ services:
- rspamd
php-fpm-mailcow:
image: ghcr.io/mailcow/phpfpm:1.94
image: ghcr.io/mailcow/phpfpm:8.2.29
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
@@ -188,10 +188,10 @@ services:
restart: always
labels:
ofelia.enabled: "true"
ofelia.job-exec.phpfpm_keycloak_sync.schedule: "@every 1m"
ofelia.job-exec.phpfpm_keycloak_sync.schedule: "0 * * * * *"
ofelia.job-exec.phpfpm_keycloak_sync.no-overlap: "true"
ofelia.job-exec.phpfpm_keycloak_sync.command: "/bin/bash -c \"php /crons/keycloak-sync.php || exit 0\""
ofelia.job-exec.phpfpm_ldap_sync.schedule: "@every 1m"
ofelia.job-exec.phpfpm_ldap_sync.schedule: "0 * * * * *"
ofelia.job-exec.phpfpm_ldap_sync.no-overlap: "true"
ofelia.job-exec.phpfpm_ldap_sync.command: "/bin/bash -c \"php /crons/ldap-sync.php || exit 0\""
networks:
@@ -200,7 +200,7 @@ services:
- phpfpm
sogo-mailcow:
image: ghcr.io/mailcow/sogo:1.136
image: ghcr.io/mailcow/sogo:5.12.4
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -236,13 +236,13 @@ services:
- sogo-userdata-backup-vol-1:/sogo_backup
labels:
ofelia.enabled: "true"
ofelia.job-exec.sogo_sessions.schedule: "@every 1m"
ofelia.job-exec.sogo_sessions.schedule: "0 * * * * *"
ofelia.job-exec.sogo_sessions.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool -v expire-sessions $${SOGO_EXPIRE_SESSION} || exit 0\""
ofelia.job-exec.sogo_ealarms.schedule: "@every 1m"
ofelia.job-exec.sogo_ealarms.schedule: "0 * * * * *"
ofelia.job-exec.sogo_ealarms.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-ealarms-notify -p /etc/sogo/cron.creds || exit 0\""
ofelia.job-exec.sogo_eautoreply.schedule: "@every 5m"
ofelia.job-exec.sogo_eautoreply.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/cron.creds || exit 0\""
ofelia.job-exec.sogo_backup.schedule: "@every 24h"
ofelia.job-exec.sogo_eautoreply.schedule: "0 */5 * * * *"
ofelia.job-exec.sogo_eautoreply.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds || exit 0\""
ofelia.job-exec.sogo_backup.schedule: "0 0 0 * * *"
ofelia.job-exec.sogo_backup.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool backup /sogo_backup ALL || exit 0\""
restart: always
networks:
@@ -252,7 +252,7 @@ services:
- sogo
dovecot-mailcow:
image: ghcr.io/mailcow/dovecot:2.35
image: ghcr.io/mailcow/dovecot:2.3.21.1
depends_on:
- mysql-mailcow
- netfilter-mailcow
@@ -310,22 +310,22 @@ services:
tty: true
labels:
ofelia.enabled: "true"
ofelia.job-exec.dovecot_imapsync_runner.schedule: "@every 1m"
ofelia.job-exec.dovecot_imapsync_runner.schedule: "0 * * * * *"
ofelia.job-exec.dovecot_imapsync_runner.no-overlap: "true"
ofelia.job-exec.dovecot_imapsync_runner.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu nobody /usr/local/bin/imapsync_runner.pl || exit 0\""
ofelia.job-exec.dovecot_trim_logs.schedule: "@every 1m"
ofelia.job-exec.dovecot_trim_logs.schedule: "0 * * * * *"
ofelia.job-exec.dovecot_trim_logs.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/trim_logs.sh || exit 0\""
ofelia.job-exec.dovecot_quarantine.schedule: "@every 20m"
ofelia.job-exec.dovecot_quarantine.schedule: "0 */20 * * * *"
ofelia.job-exec.dovecot_quarantine.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/quarantine_notify.py || exit 0\""
ofelia.job-exec.dovecot_clean_q_aged.schedule: "@every 24h"
ofelia.job-exec.dovecot_clean_q_aged.schedule: "0 0 0 * * *"
ofelia.job-exec.dovecot_clean_q_aged.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/clean_q_aged.sh || exit 0\""
ofelia.job-exec.dovecot_maildir_gc.schedule: "@every 30m"
ofelia.job-exec.dovecot_maildir_gc.schedule: "0 */30 * * * *"
ofelia.job-exec.dovecot_maildir_gc.command: "/bin/bash -c \"source /source_env.sh ; /usr/local/bin/gosu vmail /usr/local/bin/maildir_gc.sh\""
ofelia.job-exec.dovecot_sarules.schedule: "@every 24h"
ofelia.job-exec.dovecot_sarules.schedule: "0 0 0 * * *"
ofelia.job-exec.dovecot_sarules.command: "/bin/bash -c \"/usr/local/bin/sa-rules.sh\""
ofelia.job-exec.dovecot_fts.schedule: "@every 24h"
ofelia.job-exec.dovecot_fts.schedule: "0 0 0 * * *"
ofelia.job-exec.dovecot_fts.command: "/bin/bash -c \"/usr/local/bin/gosu vmail /usr/local/bin/optimize-fts.sh\""
ofelia.job-exec.dovecot_repl_health.schedule: "@every 5m"
ofelia.job-exec.dovecot_repl_health.schedule: "0 */5 * * * *"
ofelia.job-exec.dovecot_repl_health.command: "/bin/bash -c \"/usr/local/bin/gosu vmail /usr/local/bin/repl_health.sh\""
ulimits:
nproc: 65535
@@ -339,7 +339,7 @@ services:
- dovecot
postfix-mailcow:
image: ghcr.io/mailcow/postfix:1.81
image: ghcr.io/mailcow/postfix:3.7.11
depends_on:
mysql-mailcow:
condition: service_started
@@ -382,7 +382,7 @@ services:
- postfix
postfix-tlspol-mailcow:
image: ghcr.io/mailcow/postfix-tlspol:1.0
image: ghcr.io/mailcow/postfix-tlspol:1.8.22
depends_on:
unbound-mailcow:
condition: service_healthy

View File

@@ -110,32 +110,32 @@ function backup() {
docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_vmail.tar.gz /vmail
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="zstd --rsyncable -T${THREADS}" -Pcvpf /backup/backup_vmail.tar.zst /vmail
;;&
crypt|all)
docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="zstd --rsyncable -T${THREADS}" -Pcvpf /backup/backup_crypt.tar.zst /crypt
;;&
redis|all)
docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} --no-auth-warning save
docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_redis.tar.gz /redis
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="zstd --rsyncable -T${THREADS}" -Pcvpf /backup/backup_redis.tar.zst /redis
;;&
rspamd|all)
docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="zstd --rsyncable -T${THREADS}" -Pcvpf /backup/backup_rspamd.tar.zst /rspamd
;;&
postfix|all)
docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_postfix.tar.gz /postfix
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="zstd --rsyncable -T${THREADS}" -Pcvpf /backup/backup_postfix.tar.zst /postfix
;;&
mysql|all)
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
@@ -154,7 +154,7 @@ function backup() {
${SQLIMAGE} /bin/sh -c "mariabackup --host mysql --user root --password ${DBROOT} --backup --rsync --target-dir=/backup_mariadb ; \
mariabackup --prepare --target-dir=/backup_mariadb ; \
chown -R 999:999 /backup_mariadb ; \
/bin/tar --warning='no-file-ignored' --use-compress-program='gzip --rsyncable' -Pcvpf /backup/backup_mariadb.tar.gz /backup_mariadb ;"
/bin/tar --warning='no-file-ignored' --use-compress-program='zstd --rsyncable' -Pcvpf /backup/backup_mariadb.tar.zst /backup_mariadb ;"
fi
;;&
--delete-days)
@@ -170,6 +170,19 @@ function backup() {
done
}
function get_archive_info() {
local backup_name="$1"
local location="$2"
if [[ -f "${location}/${backup_name}.tar.zst" ]]; then
echo "${backup_name}.tar.zst|zstd -d -T${THREADS}"
elif [[ -f "${location}/${backup_name}.tar.gz" ]]; then
echo "${backup_name}.tar.gz|pigz -d -p ${THREADS}"
else
echo ""
fi
}
function restore() {
for bin in docker; do
if [[ -z $(which ${bin}) ]]; then
@@ -199,10 +212,17 @@ function restore() {
case "$1" in
vmail)
docker stop $(docker ps -qf name=dovecot-mailcow)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_vmail.tar.gz
ARCHIVE_INFO=$(get_archive_info "backup_vmail" "${RESTORE_LOCATION}")
if [[ -z "${ARCHIVE_INFO}" ]]; then
echo -e "\e[31mError: No backup file found for vmail (searched for .tar.zst and .tar.gz)\e[0m"
else
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
fi
docker start $(docker ps -aqf name=dovecot-mailcow)
echo
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
@@ -218,31 +238,50 @@ function restore() {
;;
redis)
docker stop $(docker ps -qf name=redis-mailcow)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_redis.tar.gz
ARCHIVE_INFO=$(get_archive_info "backup_redis" "${RESTORE_LOCATION}")
if [[ -z "${ARCHIVE_INFO}" ]]; then
echo -e "\e[31mError: No backup file found for redis (searched for .tar.zst and .tar.gz)\e[0m"
else
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
fi
docker start $(docker ps -aqf name=redis-mailcow)
;;
crypt)
docker stop $(docker ps -qf name=dovecot-mailcow)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_crypt.tar.gz
ARCHIVE_INFO=$(get_archive_info "backup_crypt" "${RESTORE_LOCATION}")
if [[ -z "${ARCHIVE_INFO}" ]]; then
echo -e "\e[31mError: No backup file found for crypt (searched for .tar.zst and .tar.gz)\e[0m"
else
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
fi
docker start $(docker ps -aqf name=dovecot-mailcow)
;;
rspamd)
if [[ $(find "${RESTORE_LOCATION}" \( -name '*x86*' -o -name '*aarch*' \) -exec basename {} \; | sed 's/^\.//' | sed 's/^\.//') == "" ]]; then
ARCHIVE_INFO=$(get_archive_info "backup_rspamd" "${RESTORE_LOCATION}")
if [[ -z "${ARCHIVE_INFO}" ]]; then
echo -e "\e[31mError: No backup file found for rspamd (searched for .tar.zst and .tar.gz)\e[0m"
elif [[ $(find "${RESTORE_LOCATION}" \( -name '*x86*' -o -name '*aarch*' \) -exec basename {} \; | sed 's/^\.//' | sed 's/^\.//') == "" ]]; then
echo -e "\e[33mCould not find a architecture signature of the loaded backup... Maybe the backup was done before the multiarch update?"
sleep 2
echo -e "Continuing anyhow. If rspamd is crashing upon boot try remove the rspamd volume with docker volume rm ${CMPS_PRJ}_rspamd-vol-1 after you've stopped the stack.\e[0m"
sleep 2
docker stop $(docker ps -qf name=rspamd-mailcow)
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_rspamd.tar.gz
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
docker start $(docker ps -aqf name=rspamd-mailcow)
elif [[ $ARCH != $(find "${RESTORE_LOCATION}" \( -name '*x86*' -o -name '*aarch*' \) -exec basename {} \; | sed 's/^\.//' | sed 's/^\.//') ]]; then
echo -e "\e[31mThe Architecture of the backed up mailcow OS is different then your restoring mailcow OS..."
@@ -250,19 +289,28 @@ function restore() {
echo -e "Skipping rspamd due to compatibility issues!\e[0m"
else
docker stop $(docker ps -qf name=rspamd-mailcow)
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_rspamd.tar.gz
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
docker start $(docker ps -aqf name=rspamd-mailcow)
fi
;;
postfix)
docker stop $(docker ps -qf name=postfix-mailcow)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_postfix.tar.gz
ARCHIVE_INFO=$(get_archive_info "backup_postfix" "${RESTORE_LOCATION}")
if [[ -z "${ARCHIVE_INFO}" ]]; then
echo -e "\e[31mError: No backup file found for postfix (searched for .tar.zst and .tar.gz)\e[0m"
else
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run -i --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="${DECOMPRESS_PROG}" -Pxvf /backup/${ARCHIVE_FILE}
fi
docker start $(docker ps -aqf name=postfix-mailcow)
;;
mysql|mariadb)
@@ -305,14 +353,19 @@ function restore() {
echo Restoring... && \
gunzip < backup/backup_mysql.gz | mysql -uroot && \
mysql -uroot -e SHUTDOWN;"
elif [[ -f "${RESTORE_LOCATION}/backup_mariadb.tar.gz" ]]; then
docker run --name mailcow-backup --rm \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_mysql-vol-1$):/backup_mariadb/:rw,z \
--entrypoint= \
-v ${RESTORE_LOCATION}:/backup:z \
${SQLIMAGE} /bin/bash -c "shopt -s dotglob ; \
/bin/rm -rf /backup_mariadb/* ; \
/bin/tar -Pxvzf /backup/backup_mariadb.tar.gz"
else
ARCHIVE_INFO=$(get_archive_info "backup_mariadb" "${RESTORE_LOCATION}")
if [[ -n "${ARCHIVE_INFO}" ]]; then
ARCHIVE_FILE=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f1)
DECOMPRESS_PROG=$(echo "${ARCHIVE_INFO}" | cut -d'|' -f2)
docker run --name mailcow-backup --rm \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_mysql-vol-1$):/backup_mariadb/:rw,z \
--entrypoint= \
-v ${RESTORE_LOCATION}:/backup:z \
${SQLIMAGE} /bin/bash -c "shopt -s dotglob ; \
/bin/rm -rf /backup_mariadb/* ; \
/bin/tar --use-compress-program='${DECOMPRESS_PROG}' -Pxvf /backup/${ARCHIVE_FILE}"
fi
fi
echo "Modifying mailcow.conf..."
source ${RESTORE_LOCATION}/mailcow.conf
@@ -363,8 +416,8 @@ elif [[ ${1} == "restore" ]]; then
fi
echo "[ 0 ] - all"
# find all files in folder with *.gz extension, print their base names, remove backup_, remove .tar (if present), remove .gz
FILE_SELECTION[0]=$(find "${FOLDER_SELECTION[${input_sel}]}" -maxdepth 1 \( -type d -o -type f \) \( -name '*.gz' -o -name 'mysql' \) -printf '%f\n' | sed 's/backup_*//' | sed 's/\.[^.]*$//' | sed 's/\.[^.]*$//')
# find all files in folder with *.zst or *.gz extension, print their base names, remove backup_, remove .tar (if present), remove .zst/.gz
FILE_SELECTION[0]=$(find "${FOLDER_SELECTION[${input_sel}]}" -maxdepth 1 \( -type d -o -type f \) \( -name '*.zst' -o -name '*.gz' -o -name 'mysql' \) -printf '%f\n' | sed 's/backup_*//' | sed 's/\.[^.]*$//' | sed 's/\.[^.]*$//' | sort -u)
for file in $(ls -f "${FOLDER_SELECTION[${input_sel}]}"); do
if [[ ${file} =~ vmail ]]; then
echo "[ ${i} ] - Mail directory (/var/vmail)"

View File

@@ -0,0 +1,301 @@
#!/usr/bin/env bash
# Test script for backup_and_restore.sh
# Tests backward compatibility with .tar.gz and new .tar.zst format
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BACKUP_IMAGE="${BACKUP_IMAGE:-ghcr.io/mailcow/backup:latest}"
TEST_DIR="/tmp/mailcow_backup_test_$$"
THREADS=2
echo "=== Mailcow Backup & Restore Test Suite ==="
echo "Test directory: ${TEST_DIR}"
echo "Backup image: ${BACKUP_IMAGE}"
echo ""
# Cleanup function
cleanup() {
echo "Cleaning up test files..."
rm -rf "${TEST_DIR}"
docker rmi mailcow-backup-test 2>/dev/null || true
}
trap cleanup EXIT
# Create test directory structure
mkdir -p "${TEST_DIR}"/{test_data,backup_zst,backup_gz,restore_zst,restore_gz,backup_large_zst,backup_large_gz}
echo "Test data for mailcow backup compatibility test" > "${TEST_DIR}/test_data/test.txt"
echo "Additional file to verify complete restore" > "${TEST_DIR}/test_data/test2.txt"
# Build test backup image with zstd support
echo "=== Building backup image with zstd support ==="
docker build -t mailcow-backup-test "${SCRIPT_DIR}/../data/Dockerfiles/backup/" || {
echo "ERROR: Failed to build backup image"
exit 1
}
# Test 1: Create .tar.zst backup
echo ""
echo "=== Test 1: Creating .tar.zst backup ==="
docker run --rm \
-w /data \
-v "${TEST_DIR}/test_data:/data:ro" \
-v "${TEST_DIR}/backup_zst:/backup" \
mailcow-backup-test \
/bin/tar --use-compress-program="zstd --rsyncable -T${THREADS}" \
-cvpf /backup/backup_test.tar.zst . \
> /dev/null
echo "✓ .tar.zst backup created: $(ls -lh ${TEST_DIR}/backup_zst/backup_test.tar.zst | awk '{print $5}')"
# Test 2: Create .tar.gz backup
echo ""
echo "=== Test 2: Creating .tar.gz backup (legacy) ==="
docker run --rm \
-w /data \
-v "${TEST_DIR}/test_data:/data:ro" \
-v "${TEST_DIR}/backup_gz:/backup" \
mailcow-backup-test \
/bin/tar --use-compress-program="pigz --rsyncable -p ${THREADS}" \
-cvpf /backup/backup_test.tar.gz . \
> /dev/null
echo "✓ .tar.gz backup created: $(ls -lh ${TEST_DIR}/backup_gz/backup_test.tar.gz | awk '{print $5}')"
# Test 3: Test get_archive_info function
echo ""
echo "=== Test 3: Testing get_archive_info function ==="
# Extract and test the function directly
get_archive_info() {
local backup_name="$1"
local location="$2"
if [[ -f "${location}/${backup_name}.tar.zst" ]]; then
echo "${backup_name}.tar.zst|zstd -d -T${THREADS}"
elif [[ -f "${location}/${backup_name}.tar.gz" ]]; then
echo "${backup_name}.tar.gz|pigz -d -p ${THREADS}"
else
echo ""
fi
}
# Test with .tar.zst
result=$(get_archive_info "backup_test" "${TEST_DIR}/backup_zst")
if [[ "${result}" =~ "zstd" ]]; then
echo "✓ Correctly detects .tar.zst and returns zstd decompressor"
else
echo "✗ Failed to detect .tar.zst"
exit 1
fi
# Test with .tar.gz
result=$(get_archive_info "backup_test" "${TEST_DIR}/backup_gz")
if [[ "${result}" =~ "pigz" ]]; then
echo "✓ Correctly detects .tar.gz and returns pigz decompressor"
else
echo "✗ Failed to detect .tar.gz"
exit 1
fi
# Test with no file
result=$(get_archive_info "backup_test" "${TEST_DIR}")
if [[ -z "${result}" ]]; then
echo "✓ Correctly returns empty when no backup file found"
else
echo "✗ Should return empty but got: ${result}"
exit 1
fi
# Test 4: Restore from .tar.zst
echo ""
echo "=== Test 4: Restoring from .tar.zst ==="
docker run --rm \
-w /restore \
-v "${TEST_DIR}/backup_zst:/backup:ro" \
-v "${TEST_DIR}/restore_zst:/restore" \
mailcow-backup-test \
/bin/tar --use-compress-program="zstd -d -T${THREADS}" -xvpf /backup/backup_test.tar.zst \
> /dev/null 2>&1
if [[ -f "${TEST_DIR}/restore_zst/test.txt" ]] && \
[[ -f "${TEST_DIR}/restore_zst/test2.txt" ]]; then
echo "✓ Successfully restored from .tar.zst"
else
echo "✗ Failed to restore from .tar.zst"
ls -la "${TEST_DIR}/restore_zst/" || true
exit 1
fi
# Test 5: Restore from .tar.gz
echo ""
echo "=== Test 5: Restoring from .tar.gz (backward compatibility) ==="
docker run --rm \
-w /restore \
-v "${TEST_DIR}/backup_gz:/backup:ro" \
-v "${TEST_DIR}/restore_gz:/restore" \
mailcow-backup-test \
/bin/tar --use-compress-program="pigz -d -p ${THREADS}" -xvpf /backup/backup_test.tar.gz \
> /dev/null 2>&1
if [[ -f "${TEST_DIR}/restore_gz/test.txt" ]] && \
[[ -f "${TEST_DIR}/restore_gz/test2.txt" ]]; then
echo "✓ Successfully restored from .tar.gz (backward compatible)"
else
echo "✗ Failed to restore from .tar.gz"
ls -la "${TEST_DIR}/restore_gz/" || true
exit 1
fi
# Test 6: Verify content integrity
echo ""
echo "=== Test 6: Verifying content integrity ==="
original_content=$(cat "${TEST_DIR}/test_data/test.txt")
zst_content=$(cat "${TEST_DIR}/restore_zst/test.txt")
gz_content=$(cat "${TEST_DIR}/restore_gz/test.txt")
if [[ "${original_content}" == "${zst_content}" ]] && \
[[ "${original_content}" == "${gz_content}" ]]; then
echo "✓ Content integrity verified for both formats"
else
echo "✗ Content mismatch detected"
exit 1
fi
# Test 7: Compare compression ratios
echo ""
echo "=== Test 7: Compression comparison ==="
zst_size=$(stat -f%z "${TEST_DIR}/backup_zst/backup_test.tar.zst" 2>/dev/null || stat -c%s "${TEST_DIR}/backup_zst/backup_test.tar.zst")
gz_size=$(stat -f%z "${TEST_DIR}/backup_gz/backup_test.tar.gz" 2>/dev/null || stat -c%s "${TEST_DIR}/backup_gz/backup_test.tar.gz")
improvement=$(echo "scale=2; (${gz_size} - ${zst_size}) * 100 / ${gz_size}" | bc)
echo " Small files - .tar.gz size: ${gz_size} bytes"
echo " Small files - .tar.zst size: ${zst_size} bytes"
echo " Small files - Improvement: ${improvement}% smaller with zstd"
# Test 8: Error handling - missing backup file
echo ""
echo "=== Test 8: Error handling - Missing backup file ==="
result=$(get_archive_info "nonexistent_backup" "${TEST_DIR}/backup_zst")
if [[ -z "${result}" ]]; then
echo "✓ Correctly handles missing backup files"
else
echo "✗ Should return empty for missing files"
exit 1
fi
# Test 9: Error handling - Empty directory
echo ""
echo "=== Test 9: Error handling - Empty directory ==="
mkdir -p "${TEST_DIR}/empty_dir"
result=$(get_archive_info "backup_test" "${TEST_DIR}/empty_dir")
if [[ -z "${result}" ]]; then
echo "✓ Correctly handles empty directories"
else
echo "✗ Should return empty for empty directories"
exit 1
fi
# Test 10: Priority test - .tar.zst preferred over .tar.gz
echo ""
echo "=== Test 10: Format priority - .tar.zst preferred ==="
mkdir -p "${TEST_DIR}/both_formats"
touch "${TEST_DIR}/both_formats/backup_test.tar.gz"
touch "${TEST_DIR}/both_formats/backup_test.tar.zst"
result=$(get_archive_info "backup_test" "${TEST_DIR}/both_formats")
if [[ "${result}" =~ "zstd" ]]; then
echo "✓ Correctly prefers .tar.zst when both formats exist"
else
echo "✗ Should prefer .tar.zst over .tar.gz"
exit 1
fi
# Test 11: Large file compression test
echo ""
echo "=== Test 11: Large file compression test ==="
mkdir -p "${TEST_DIR}/large_data"
# Create ~10MB of compressible data (log-like content)
for i in {1..50000}; do
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: Processing email message $i from user@example.com to recipient@domain.com" >> "${TEST_DIR}/large_data/maillog.txt"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] DEBUG: SMTP connection established from 192.168.1.$((i % 255))" >> "${TEST_DIR}/large_data/maillog.txt"
done 2>/dev/null
# Get size (portable: works on Linux and macOS)
if du --version 2>/dev/null | grep -q GNU; then
original_size=$(du -sb "${TEST_DIR}/large_data" | cut -f1)
else
# macOS
original_size=$(find "${TEST_DIR}/large_data" -type f -exec stat -f%z {} \; | awk '{sum+=$1} END {print sum}')
fi
echo " Original data size: $(echo "scale=2; ${original_size} / 1024 / 1024" | bc) MB"
# Backup with zstd
docker run --rm \
-w /data \
-v "${TEST_DIR}/large_data:/data:ro" \
-v "${TEST_DIR}/backup_large_zst:/backup" \
mailcow-backup-test \
/bin/tar --use-compress-program="zstd --rsyncable -T${THREADS}" \
-cvpf /backup/backup_large.tar.zst . \
> /dev/null 2>&1
# Backup with pigz
docker run --rm \
-w /data \
-v "${TEST_DIR}/large_data:/data:ro" \
-v "${TEST_DIR}/backup_large_gz:/backup" \
mailcow-backup-test \
/bin/tar --use-compress-program="pigz --rsyncable -p ${THREADS}" \
-cvpf /backup/backup_large.tar.gz . \
> /dev/null 2>&1
zst_large_size=$(stat -f%z "${TEST_DIR}/backup_large_zst/backup_large.tar.zst" 2>/dev/null || stat -c%s "${TEST_DIR}/backup_large_zst/backup_large.tar.zst" 2>/dev/null || echo "0")
gz_large_size=$(stat -f%z "${TEST_DIR}/backup_large_gz/backup_large.tar.gz" 2>/dev/null || stat -c%s "${TEST_DIR}/backup_large_gz/backup_large.tar.gz" 2>/dev/null || echo "0")
if [[ ${zst_large_size} -gt 0 ]] && [[ ${gz_large_size} -gt 0 ]]; then
large_improvement=$(echo "scale=2; (${gz_large_size} - ${zst_large_size}) * 100 / ${gz_large_size}" | bc)
echo " .tar.gz compressed: $(echo "scale=2; ${gz_large_size} / 1024 / 1024" | bc) MB"
echo " .tar.zst compressed: $(echo "scale=2; ${zst_large_size} / 1024 / 1024" | bc) MB"
echo " Improvement: ${large_improvement}% smaller with zstd"
else
echo " ✗ Failed to get file sizes"
exit 1
fi
if [[ $(echo "${large_improvement} > 0" | bc) -eq 1 ]]; then
echo "✓ zstd provides better compression on realistic data"
else
echo "⚠ zstd compression similar or worse than gzip (unusual but not critical)"
fi
# Test 12: Thread scaling test
echo ""
echo "=== Test 12: Multi-threading verification ==="
# This test verifies that different thread counts work (not measuring speed difference)
for thread_count in 1 4; do
THREADS=${thread_count}
result=$(get_archive_info "backup_test" "${TEST_DIR}/backup_zst")
if [[ "${result}" =~ "-T${thread_count}" ]]; then
echo "✓ Thread count ${thread_count} correctly configured"
else
echo "✗ Thread count not properly applied"
exit 1
fi
done
echo ""
echo "=== All tests passed! ==="
echo ""
echo "Summary:"
echo " ✓ zstd compression working"
echo " ✓ pigz compression working (legacy)"
echo " ✓ zstd decompression working"
echo " ✓ pigz decompression working (backward compatible)"
echo " ✓ Archive detection working"
echo " ✓ Content integrity verified"
echo " ✓ Format priority correct (.tar.zst preferred)"
echo " ✓ Error handling for missing files"
echo " ✓ Error handling for empty directories"
echo " ✓ Multi-threading configuration verified"
echo " ✓ Large file compression: ${large_improvement}% improvement"
echo " ✓ Small file compression: ${improvement}% improvement"

View File

@@ -25,6 +25,6 @@ services:
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
mysql-mailcow:
image: alpine:3.22
image: alpine:3.23
command: /bin/true
restart: "no"