Compare commits

..

17 Commits

Author SHA1 Message Date
FreddleSpl0it
d22b9510fc [PHP-FPM] Use transactions for batch deletion of sasl_log data 2024-09-05 10:36:15 +02:00
FreddleSpl0it
b54a9c7bb3 [PHP-FPM] Use php script instead of sql event to clean sasl_log table 2024-09-04 11:02:02 +02:00
Finn Hoffhenke
710cec996c feat: Added check for newer version tags on remote (#6054) 2024-09-02 15:40:29 +02:00
Niklas Meyer
0129f84a32 Merge pull request #6056 from mailcow/update/postscreen_access.cidr
[Postfix] update postscreen_access.cidr
2024-09-02 15:37:24 +02:00
milkmaker
af0c61b90a update postscreen_access.cidr 2024-09-01 00:19:09 +00:00
milkmaker
7203735532 [Web] Updated lang.it-it.json (#6053)
Co-authored-by: Stefano <stefano.vassena@gmail.com>
2024-08-29 20:27:23 +02:00
milkmaker
0066040bdc Translations update from Weblate (#6049)
* [Web] Updated lang.cs-cz.json

Co-authored-by: Kristian Feldsam <feldsam@gmail.com>

* [Web] Updated lang.fr-fr.json

Co-authored-by: Samuel F <20537389+samuelfranzini@users.noreply.github.com>

---------

Co-authored-by: Kristian Feldsam <feldsam@gmail.com>
Co-authored-by: Samuel F <20537389+samuelfranzini@users.noreply.github.com>
2024-08-24 14:09:28 +02:00
milkmaker
cc5138da13 Translations update from Weblate (#6039)
* [Web] Updated lang.fr-fr.json

[Web] Updated lang.fr-fr.json

Co-authored-by: GeistFighter <lorentzjohan1@gmail.com>
Co-authored-by: Samuel F <20537389+samuelfranzini@users.noreply.github.com>

* [Web] Updated lang.fi-fi.json

Co-authored-by: Berttas <mika@tarh.fi>

* [Web] Updated lang.ru-ru.json

Co-authored-by: Habetdin <15926758+Habetdin@users.noreply.github.com>

* [Web] Updated lang.uk-ua.json

Co-authored-by: DRago_Angel <dragoangel@users.noreply.translate.mailcow.email>

* [Web] Updated lang.pt-br.json

Co-authored-by: xmacaba <lixo@macaba.com.br>

---------

Co-authored-by: GeistFighter <lorentzjohan1@gmail.com>
Co-authored-by: Samuel F <20537389+samuelfranzini@users.noreply.github.com>
Co-authored-by: Berttas <mika@tarh.fi>
Co-authored-by: Habetdin <15926758+Habetdin@users.noreply.github.com>
Co-authored-by: DRago_Angel <dragoangel@users.noreply.translate.mailcow.email>
Co-authored-by: xmacaba <lixo@macaba.com.br>
2024-08-20 21:34:04 +02:00
Hassan A Hashim
bb7fd483f7 Fix: Escape a ' character in update.sh (#6034) 2024-08-20 14:08:08 +02:00
Délano
567ebbc324 Pushover/Quarantine utf 8 fix - fixes #6028 (#6031)
* Decode rspamd-subject for pushover notifications

Fixes #6028

* Apply iconv_mime_decode to the quarantine function as well
This might contain utf-8 encoded text as well

* Moved the iconv_mime_decode "fix" back to pipe.php
2024-08-20 13:39:20 +02:00
Hassan A Hashim
f9a7712025 Replace weird character to the correct ' (#6029)
* Replace weird character to the correct `'`

* Replace final weird character, just found.
2024-08-20 08:08:34 +02:00
Hassan A Hashim
3d62869664 Fix: bash variables are not quoted (#6022)
* Fix: Double quote variables to prevent word splitting

* Fix `update.sh`: Double quote to prevent word splitting

* Refactor: Remove unnecessary white-spaces.
2024-08-19 15:47:55 +02:00
Niklas Meyer
b70bcd36fb containers: use mariadb-admin instead of deprecated mysqladmin (#6026)
* dockerfiles: use mariadb-admin instead of deprecated mysqladmin command

* compose: bump compose tags
2024-08-19 11:33:28 +02:00
Niklas Meyer
cb50d08605 dovecot: added timeout option when sa-rules cannot be downloaded (#6025)
* dovecot: added timeout option when sa-rules cannot be downloaded

* dovecot: changed sa-rules exit code to 0 to allow dovecot to start afterwards
2024-08-19 11:08:13 +02:00
Hassan A Hashim
f3da8bb85f Refactor/Change Dockerfiles cmd from shell to exec form (#6019)
* Update `dockerapi/Dockerfile` CMD from shell to exec format

* Update `postfix/Dockerfile` CMD from shell to exec format

* Update `sogo/Dockerfile` CMD from shell to exec format

* Update `unbound/Dockerfile` CMD from shell to exec format

* Update `watchdog/Dockerfile` CMD from shell to exec format
2024-08-19 10:42:11 +02:00
Niklas Meyer
12e4d639f0 Merge pull request #6016 from jkrgr0/fix/ParseDockerVersion 2024-08-16 10:50:04 +02:00
Janek
eb3f88fc91 fix: 🚑 Fixed version parsing of docker
Only the first result (the major version) is relevant

Closes #6015
2024-08-16 08:47:03 +02:00
26 changed files with 524 additions and 249 deletions

View File

@@ -117,7 +117,7 @@ fi
chmod 600 ${ACME_BASE}/key.pem
log_f "Waiting for database..."
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent > /dev/null; do
while ! /usr/bin/mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent > /dev/null; do
sleep 2
done
log_f "Database OK"

View File

@@ -24,4 +24,4 @@ COPY main.py /app/main.py
COPY modules/ /app/modules/
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]
CMD exec python main.py
CMD ["python", "main.py"]

View File

@@ -2,7 +2,7 @@
set -e
# Wait for MySQL to warm-up
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for database to come up..."
sleep 2
done

View File

@@ -11,10 +11,14 @@ else
fi
# Deploy
curl --connect-timeout 15 --retry 10 --max-time 30 https://www.spamassassin.heinlein-support.de/$(dig txt 1.4.3.spamassassin.heinlein-support.de +short | tr -d '"' | tr -dc '0-9').tar.gz --output /tmp/sa-rules-heinlein.tar.gz
if gzip -t /tmp/sa-rules-heinlein.tar.gz; then
tar xfvz /tmp/sa-rules-heinlein.tar.gz -C /tmp/sa-rules-heinlein
cat /tmp/sa-rules-heinlein/*cf > /etc/rspamd/custom/sa-rules
if curl --connect-timeout 15 --retry 10 --max-time 30 https://www.spamassassin.heinlein-support.de/$(dig txt 1.4.3.spamassassin.heinlein-support.de +short | tr -d '"' | tr -dc '0-9').tar.gz --output /tmp/sa-rules-heinlein.tar.gz; then
if gzip -t /tmp/sa-rules-heinlein.tar.gz; then
tar xfvz /tmp/sa-rules-heinlein.tar.gz -C /tmp/sa-rules-heinlein
cat /tmp/sa-rules-heinlein/*cf > /etc/rspamd/custom/sa-rules
fi
else
echo "Failed to download SA rules. Exiting."
exit 0 # Must be 0 otherwise dovecot would not start at all
fi
sed -i -e 's/\([^\\]\)\$\([^\/]\)/\1\\$\2/g' /etc/rspamd/custom/sa-rules

View File

@@ -3,7 +3,7 @@
function array_by_comma { local IFS=","; echo "$*"; }
# Wait for containers
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL..."
sleep 2
done
@@ -44,7 +44,7 @@ until [[ ${SQL_UPGRADE_STATUS} == 'success' ]]; do
echo "MySQL applied an upgrade, debug output:"
echo ${SQL_FULL_UPGRADE_RETURN}
sleep 3
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL to return, please wait"
sleep 2
done
@@ -174,23 +174,6 @@ END;
//
DELIMITER ;
DROP EVENT IF EXISTS clean_sasl_log;
DELIMITER //
CREATE EVENT clean_sasl_log
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE sasl_log.* FROM sasl_log
LEFT JOIN (
SELECT username, service, MAX(datetime) AS lastdate
FROM sasl_log
GROUP BY username, service
) AS last ON sasl_log.username = last.username AND sasl_log.service = last.service
WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY) AND datetime < lastdate;
DELETE FROM sasl_log
WHERE username NOT IN (SELECT username FROM mailbox) AND
datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
END;
//
DELIMITER ;
EOF
fi

View File

@@ -60,4 +60,4 @@ EXPOSE 588
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

View File

@@ -5,7 +5,7 @@ trap "postfix stop" EXIT
[[ ! -d /opt/postfix/conf/sql/ ]] && mkdir -p /opt/postfix/conf/sql/
# Wait for MySQL to warm-up
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for database to come up..."
sleep 2
done

View File

@@ -55,4 +55,4 @@ RUN chmod +x /bootstrap-sogo.sh \
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Wait for MySQL to warm-up
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for database to come up..."
sleep 2
done

View File

@@ -33,4 +33,4 @@ HEALTHCHECK --interval=30s --timeout=10s \
CMD sh -c '[ -f /tmp/healthcheck_status ] && [ "$(cat /tmp/healthcheck_status)" -eq 0 ] || exit 1'
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

View File

@@ -37,4 +37,4 @@ RUN apk add --update \
COPY watchdog.sh /watchdog.sh
COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh
CMD /watchdog.sh
CMD ["/watchdog.sh"]

View File

@@ -33,7 +33,7 @@ if [[ ! -p /tmp/com_pipe ]]; then
fi
# Wait for containers
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL..."
sleep 2
done

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Thu Aug 1 00:16:45 UTC 2024
# Whitelist generated by Postwhite v3.4 on Sun Sep 1 00:19:07 UTC 2024
# https://github.com/stevejenkins/postwhite/
# 1954 total rules
# 1994 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit
@@ -19,6 +19,8 @@
8.20.114.31 permit
8.25.194.0/23 permit
8.25.196.0/23 permit
8.39.54.0/23 permit
8.40.222.0/23 permit
10.162.0.0/16 permit
12.130.86.238 permit
13.110.208.0/21 permit
@@ -200,6 +202,7 @@
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
@@ -324,6 +327,7 @@
65.110.161.77 permit
65.123.29.213 permit
65.123.29.220 permit
65.154.166.0/24 permit
65.212.180.36 permit
66.102.0.0/20 permit
66.119.150.192/26 permit
@@ -1283,6 +1287,9 @@
117.120.16.0/21 permit
119.42.242.52/31 permit
119.42.242.156 permit
121.244.91.48 permit
121.244.91.52 permit
122.15.156.182 permit
123.126.78.64/29 permit
124.108.96.24/31 permit
124.108.96.28/31 permit
@@ -1338,7 +1345,18 @@
134.170.141.64/26 permit
134.170.143.0/24 permit
134.170.174.0/24 permit
135.84.80.0/24 permit
135.84.81.0/24 permit
135.84.82.0/24 permit
135.84.83.0/24 permit
135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
136.143.188.0/24 permit
136.143.190.0/23 permit
136.147.128.0/20 permit
136.147.135.0/24 permit
136.147.176.0/20 permit
@@ -1353,6 +1371,7 @@
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
141.148.159.229 permit
141.193.32.0/23 permit
@@ -1452,7 +1471,9 @@
163.114.132.120 permit
163.114.134.16 permit
163.114.135.16 permit
164.152.23.32 permit
164.177.132.168/30 permit
165.173.128.0/24 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
@@ -1480,9 +1501,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.142.10 permit
169.148.144.0/25 permit
169.148.144.10 permit
170.10.68.0/22 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.0.0/19 permit
172.217.32.0/20 permit
@@ -1634,7 +1662,15 @@
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.101.161.130 permit
199.101.162.0/25 permit
199.122.120.0/21 permit
@@ -1691,6 +1727,8 @@
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
204.141.32.0/23 permit
204.141.42.0/23 permit
204.220.160.0/20 permit
204.232.168.0/24 permit
205.139.110.0/24 permit
@@ -1942,6 +1980,8 @@
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:f8b0:4000::/36 permit
2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit

View File

@@ -52,7 +52,7 @@ $headers = getallheaders();
$qid = $headers['X-Rspamd-Qid'];
$fuzzy = $headers['X-Rspamd-Fuzzy'];
$subject = $headers['X-Rspamd-Subject'];
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
$score = $headers['X-Rspamd-Score'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$user = $headers['X-Rspamd-User'];

View File

@@ -53,7 +53,7 @@ $qid = $headers['X-Rspamd-Qid'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$sender = $headers['X-Rspamd-From'];
$ip = $headers['X-Rspamd-Ip'];
$subject = $headers['X-Rspamd-Subject'];
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
$messageid= $json_body->message_id;
$priority = 0;

View File

@@ -0,0 +1,114 @@
<?php
require_once "/web/inc/vars.inc.php";
if (file_exists('/web/inc/vars.local.inc.php')) {
include_once('/web/inc/vars.local.inc.php');
}
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
echo($e->getMessage() . PHP_EOL);
exit(1);
}
$dateThreshold = new DateTime();
$dateThreshold->modify('-31 days');
$dateThresholdFormatted = $dateThreshold->format('Y-m-d H:i:s');
$batchSize = 1000;
$lastProcessedDatetime = null;
$lastProcessedUsername = "";
$lastProcessedService = "";
$loopCounter = 0;
$rowCounter = 0;
$clearedRowCounter = 0;
try {
do {
$loopCounter++;
echo("Processing batch $loopCounter\n");
$stmt = $pdo->prepare("
SELECT service, real_rip, username, datetime
FROM sasl_log
WHERE datetime < :dateThreshold
AND (:lastProcessedDatetime IS NULL OR datetime >= :lastProcessedDatetime2)
ORDER BY datetime ASC
LIMIT :limit
");
$stmt->execute(array(
':dateThreshold' => $dateThresholdFormatted,
':lastProcessedDatetime' => $lastProcessedDatetime,
':lastProcessedDatetime2' => $lastProcessedDatetime,
':limit' => $batchSize
));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$rowCount = count($rows);
$rowCounter += $rowCount;
echo("Fetched $rowCount rows (total of $rowCounter)\n");
$pdo->beginTransaction();
foreach ($rows as $row) {
$stmt = $pdo->prepare("
SELECT MAX(datetime) as max_date
FROM sasl_log
WHERE datetime < :dateThreshold AND service = :service AND username = :username
");
$stmt->execute(array(
':dateThreshold' => $dateThresholdFormatted,
':service' => $row['service'],
':username' => $row['username']
));
$subrow = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row['datetime'] < $subrow['max_date']) {
$stmt = $pdo->prepare("
DELETE FROM sasl_log
WHERE username = :username AND service = :service AND datetime = :datetime
");
$stmt->execute(array(
':username' => $row['username'],
':service' => $row['service'],
':datetime' => $row['datetime']
));
$clearedRowCounter++;
}
}
$pdo->commit();
if ($lastProcessedDatetime == $rows[$rowCount - 1]['datetime'] &&
$lastProcessedUsername == $rows[$rowCount - 1]['username'] &&
$lastProcessedService == $rows[$rowCount - 1]['service'] ||
$rowCount != $batchSize) {
$rowCount = 0;
}
// Update last processed datetime
if ($rowCount > 0) {
$lastProcessedDatetime = $rows[$rowCount - 1]['datetime'];
$lastProcessedUsername = $rows[$rowCount - 1]['username'];
$lastProcessedService = $rows[$rowCount - 1]['service'];
}
} while ($rowCount > 0);
}
catch (PDOException $e) {
echo($e->getMessage() . PHP_EOL);
exit(1);
}
echo("Succesfully cleared $clearedRowCounter rows of $rowCounter rows");
exit(0);

View File

@@ -28,7 +28,8 @@
"spam_score": "Skóre spamu",
"syncjobs": "Synchronizační úlohy",
"tls_policy": "Pravidla TLS",
"unlimited_quota": "Neomezené kvóty pro mailové schránky"
"unlimited_quota": "Neomezené kvóty pro mailové schránky",
"pw_reset": "Povolit obnovení hesla uživatele mailcow"
},
"add": {
"activate_filter_warn": "Pokud je zaškrtlá volba \"Aktivní\", budou všechny ostatní filtry deaktivovány.",
@@ -346,7 +347,14 @@
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
"f2b_max_ban_time": "Maximální délka banu (s)",
"cors_settings": "Nastavení CORS",
"queue_unban": "zrušit ban"
"queue_unban": "zrušit ban",
"password_reset_info": "Pokud není zadán žádný e-mail pro obnovení, nelze tuto funkci použít.",
"password_reset_settings": "Nastavení obnovení hesla",
"password_settings": "Nastavení hesel",
"password_reset_tmpl_html": "HTML šablona",
"password_reset_tmpl_text": "Textová šablona",
"reset_password_vars": "<code>{{link}}</code> Vygenerovaný odkaz pro obnovení hesla<br><code>{{username}}</code> Název mailboxu uživatele, který požádal o resetování hesla.<br><code>{{username2}}</code> Název schránky pro obnovení<br><code>{{date}}</code> Datum podání žádosti o obnovení hesla<br><code>{{token_lifetime}}</code> Délka životnosti tokenu v minutách<br><code>{{hostname}}</code> Název serveru mailcow",
"restore_template": "Ponechte prázdné pro obnovení výchozí šablony."
},
"danger": {
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
@@ -475,7 +483,10 @@
"webauthn_publickey_failed": "Pro vybraný ověřovací prostředek nebyl uložen žádný veřejný klíč",
"webauthn_username_failed": "Zvolený ověřovací prostředek patří k jinému účtu",
"extended_sender_acl_denied": "chybějící ACL pro nastavení externích adres odesílatele",
"demo_mode_enabled": "Demo režim je zapnutý"
"demo_mode_enabled": "Demo režim je zapnutý",
"recovery_email_failed": "Nepodařilo se odeslat e-mail pro obnovení. Obraťte se prosím na svého správce.",
"password_reset_invalid_user": "Mailbox nebyl nalezen nebo není nastaven žádný e-mail pro obnovu",
"password_reset_na": "Obnovení hesla není v současné době k dispozici. Obraťte se prosím na svého správce."
},
"datatables": {
"emptyTable": "Tabulka neobsahuje žádná data",
@@ -672,12 +683,17 @@
"auth_user": "{= auth_user =} - Ověřené uživatelské jméno zadané MTA",
"from_user": "{= from_user =} - uživatelská část odesílatele, např. pro \"moo@mailcow.tld\" vrátí \"moo\"",
"from_domain": "{= from_domain =} - Doména odesílatele",
"from_addr": "{= from_addr =} - E-mailová adresa odesílatele"
"from_addr": "{= from_addr =} - E-mailová adresa odesílatele",
"custom": "{= foo =} - Pokud má schránka vlastní atribut „foo“ s hodnotou „bar“, vrátí „bar“"
},
"domain_footer": "Patička pro celou doménu",
"domain_footer_html": "HTML text",
"domain_footer_plain": "Prostý text",
"pushover_sound": "Zvukové upozornění"
"pushover_sound": "Zvukové upozornění",
"custom_attributes": "Vlastní atributy",
"footer_exclude": "Vyloučit ze zápatí",
"domain_footer_skip_replies": "Ignorovat patičku u odpovědí na e-maily",
"password_recovery_email": "E-mail pro obnovu hesla"
},
"fido2": {
"confirm": "Potvrdit",
@@ -733,7 +749,14 @@
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
"other_logins": "Přihlášení klíčem",
"password": "Heslo",
"username": "Uživatelské jméno"
"username": "Uživatelské jméno",
"back_to_mailcow": "Zpět do mailcow",
"forgot_password": "> Zapomněli jste heslo?",
"invalid_pass_reset_token": "Token pro obnovení hesla je neplatný nebo jeho platnost vypršela.<br>Prosím, vyžádejte si nový odkaz pro obnovení hesla.",
"new_password": "Nové heslo",
"new_password_confirm": "Ověření nového hesla",
"reset_password": "Obnovit heslo",
"request_reset_password": "Požádat o změnu hesla"
},
"mailbox": {
"action": "Akce",
@@ -1081,7 +1104,8 @@
"verified_webauthn_login": "WebAuthn přihlášení ověřeno",
"verified_yotp_login": "Yubico OTP přihlášení ověřeno",
"cors_headers_edited": "Nastavení CORS byla uložena",
"domain_footer_modified": "Změny patičky domény %s byly uloženy"
"domain_footer_modified": "Změny patičky domény %s byly uloženy",
"recovery_email_sent": "E-mail k obnovení byl odeslán na adresu %s"
},
"tfa": {
"api_register": "%s používá Yubico Cloud API. Prosím získejte API klíč pro své Yubico <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ZDE</a>",
@@ -1268,7 +1292,9 @@
"with_app_password": "s heslem aplikace",
"year": "rok",
"years": "let",
"pushover_sound": "Zvukové upozornění"
"pushover_sound": "Zvukové upozornění",
"password_reset_info": "Pokud není zadán e-mail pro obnovení hesla, nelze tuto funkci použít.",
"pw_recovery_email": "E-mail pro obnovení hesla"
},
"warning": {
"cannot_delete_self": "Nelze smazat právě přihlášeného uživatele",

View File

@@ -267,7 +267,11 @@
"upload": "Lataa",
"username": "Käyttäjätunnus",
"validate_license_now": "Vahvista GUID-tunnus lisenssi palvelinta vastaan",
"yes": "&#10003;"
"yes": "&#10003;",
"allowed_methods": "Kulunvalvonta-salli-menetelmät",
"admins": "Järjestelmänvalvojat",
"admins_ldap": "LDAP-ylläpitäjät",
"advanced_settings": "Lisäasetukset"
},
"danger": {
"access_denied": "Käyttö estetty tai lomake tiedot eivät kelpaa",

View File

@@ -28,7 +28,8 @@
"unlimited_quota": "Quota illimité pour les boîtes de réception",
"domain_desc": "Modifier la description du domaine",
"domain_relayhost": "Changer le relais pour un domaine",
"mailbox_relayhost": "Changer le relais dune boîte de réception"
"mailbox_relayhost": "Changer le relais dune boîte de réception",
"pw_reset": "Autoriser la réinitialisation du mot de passe de l'utilisateur"
},
"add": {
"activate_filter_warn": "Tous les autres filtres seront désactivés, quand activé est coché.",
@@ -343,7 +344,15 @@
"f2b_manage_external": "Gérer Fail2Ban en externe",
"transport_test_rcpt_info": "&#8226 ; Utilisez null@hosted.mailcow.de pour tester le relais vers une destination étrangère.",
"relay_rcpt": "Adresse \"À :\"",
"is_mx_based": "Basé sur MX"
"is_mx_based": "Basé sur MX",
"password_reset_info": "Si aucune adresse de messagerie de récupération n'est fournie, cette fonction ne peut pas être utilisée.",
"password_settings": "Paramètres des mots de passe",
"reset_password_vars": "<code>{{link}}</code> Le lien généré pour la réinitialisation du mot de passe<br><code>{{username}}</code> L'adresse de la boîte mail de l'utilisateur qui a demandé la réinitialisation du mot de passe ayant un compte mailcow<br><code>{{username2}}</code> L'adresse de la boîte mail de récupération<br><code>{{date}}</code> La date à laquelle la demande de réinitialisation du mot de passe a été faite<br><code>{{token_lifetime}}</code> La durée de vie du jeton en minutes<br><code>{{hostname}}</code> Le nom d'hôte de votre serveur mailcow",
"password_reset_settings": "Paramètres de récupération des mots de passe",
"password_reset_tmpl_html": "Modèle HTML",
"password_reset_tmpl_text": "Modèle en texte",
"restore_template": "Laisser vide pour restaurer le modèle par défaut.",
"admins_ldap": "Si aucune adresse de messagerie de récupération n'est fournie, cette fonction ne peut pas être utilisée."
},
"danger": {
"access_denied": "Accès refusé ou données de formulaire non valides",
@@ -474,7 +483,13 @@
"cors_invalid_method": "Allow-Method specifiée invalide",
"cors_invalid_origin": "Allow-Origin spécifiée invalide",
"extended_sender_acl_denied": "ACL manquante pour définir les adresses des expéditeurs externes",
"webauthn_username_failed": "L'authentificateur sélectionné appartient à un autre compte"
"webauthn_username_failed": "L'authentificateur sélectionné appartient à un autre compte",
"recovery_email_failed": "Impossible d'envoyer un email de réinitialisation. Veuillez contacter votre administrateur.",
"invalid_reset_token": "Jeton de réinitialisation invalide",
"password_reset_invalid_user": "Boîte mail introuvable ou aucune adresse de récupération n'a été définie",
"password_reset_na": "La réinitialisation des mots de passe est actuellement indisponible. Veuillez contacter votre administrateur.",
"reset_token_limit_exceeded": "Le nombre limite de jetons de réinitialisation a été dépassé. Veuillez réessayer plus tard.",
"to_invalid": "Le destinataire ne doit pas être vide"
},
"debug": {
"chart_this_server": "Graphique (ce serveur)",
@@ -641,8 +656,9 @@
"none_inherit": "Aucun / Héritage",
"quota_warning_bcc": "Avertissement sur les quotas BCC",
"quota_warning_bcc_info": "Les avertissements seront envoyés en copies séparées aux destinataires suivants. Le sujet sera précédé du nom d'utilisateur correspondant entre parenthèses, par exemple : <code>Avertissement sur les quotas (user@example.com)</code>.",
"sogo_access_info": "L'authentification unique à partir de l'interface de messagerie reste opérationnelle. Ce paramètre n'affecte pas l'accès à tous les autres services et ne supprime ni ne modifie le profil SOGo existant d'un utilisateur.",
"admin": "Modifier l'administrateur"
"sogo_access_info": "L'authentification unique à partir de l'interface de messagerie reste opérationnelle. Ce paramètre n'affecte pas l'accès à tous les autres services et ne supprime ni, ne modifie le profil SOGo existant d'un utilisateur.",
"admin": "Modifier l'administrateur",
"password_recovery_email": "Adresse email de récupération"
},
"footer": {
"cancel": "Annuler",
@@ -681,7 +697,14 @@
"mobileconfig_info": "Veuillez vous connecter en tant quutilisateur de la boîte de réception pour télécharger le profil de connexion Apple demandé.",
"other_logins": "Clé d'authentification",
"password": "Mot de passe",
"username": "Nom d'utilisateur"
"username": "Nom d'utilisateur",
"back_to_mailcow": "Revenir sur mailcow",
"forgot_password": "> Mot de passe oublié ?",
"invalid_pass_reset_token": "Le jeton de réinitialisation du mot de passe est invalide ou a expiré.<br>Veuillez demander un nouveau lien de réinitialisation de mot de passe.",
"new_password": "Nouveau mot de passe",
"new_password_confirm": "Confirmer le nouveau mot de passe",
"reset_password": "Réinitialiser le mot de passe",
"request_reset_password": "Demander le changement du mot de passe"
},
"mailbox": {
"action": "Action",
@@ -1004,7 +1027,9 @@
"template_added": "Modèles ajoutés %s",
"template_removed": "Le modèle ayant l'ID %s a été supprimé",
"domain_add_dkim_available": "A DKIM key did already exist",
"ip_check_opt_in_modified": "Le contrôle de l'IP a été enregistré avec succès"
"ip_check_opt_in_modified": "Le contrôle de l'IP a été enregistré avec succès",
"password_changed_success": "Le mot de passe a été modifié avec succès",
"recovery_email_sent": "Email de réinitialisation envoyé à %s"
},
"tfa": {
"api_register": "%s utilise l'API Yubico Cloud. Veuillez obtenir une clé API pour votre clé <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ici</a>",
@@ -1202,7 +1227,9 @@
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Nom d'utilisateur ou mot de passe incorrect",
"value": "Valeur",
"allowed_protocols": "Protocoles autorisés",
"mailbox": "Boîte de réception"
"mailbox": "Boîte de réception",
"password_reset_info": "Si aucun email pour la récupération du mot de passe n'est fourni, cette fonction ne peut pas être utilisée.",
"pw_recovery_email": "Email de récupération pour son mot de passe"
},
"warning": {
"cannot_delete_self": "Impossible de supprimer lutilisateur connecté",
@@ -1234,7 +1261,8 @@
"lengthMenu": "Afficher les entrées _MENU_",
"loadingRecords": "Chargement…",
"processing": "Veuillez patienter…",
"collapse_all": "Tout réduire"
"collapse_all": "Tout réduire",
"info": "Affichage de _START_ de _END_ sur _TOTAL_ entrées"
},
"ratelimit": {
"disabled": "Désactivé"

View File

@@ -28,7 +28,8 @@
"spam_score": "Punteggio SPAM",
"syncjobs": "Processi di sync",
"tls_policy": "Politica TLS",
"unlimited_quota": "Spazio illimitato per le caselle di posta"
"unlimited_quota": "Spazio illimitato per le caselle di posta",
"pw_reset": "Permettere di reimpostare la password dell'utente mailcow"
},
"add": {
"activate_filter_warn": "Tutti gli altri filtri saranno disattivati, quando è attivo.",
@@ -116,7 +117,7 @@
"activate_api": "Attiva API",
"activate_send": "Attiva bottone di invio",
"active": "Attiva",
"active_rspamd_settings_map": "Active settings map",
"active_rspamd_settings_map": "Mappa delle impostazioni attive",
"add": "Aggiungi",
"add_admin": "Aggiungi amministratore",
"add_domain_admin": "Aggiungi amministratore di dominio",
@@ -124,7 +125,7 @@
"add_relayhost": "Add sender-dependent transport",
"add_relayhost_hint": "Tieni presente che i dati di autenticazione, se presenti, verranno archiviati come testo semplice.",
"add_row": "Aggiungi riga",
"add_settings_rule": "Add settings rule",
"add_settings_rule": "Aggiungi regola delle impostazioni",
"add_transport": "Aggiungi transport",
"add_transports_hint": "Tieni presente che i dati di autenticazione, se presenti, verranno archiviati come testo semplice.",
"additional_rows": " righe aggiuntive inserite",
@@ -134,7 +135,7 @@
"admins": "Amministratori",
"admins_ldap": "Amministratori LDAP",
"advanced_settings": "Impostazioni avanzate",
"api_allow_from": "Allow API access from these IPs/CIDR network notations",
"api_allow_from": "Consenti l'accesso API da questi indirizzi IP/notazione di rete CIDR",
"api_info": "Questa API è in modifica. La documentazione può essere trovata su <a href=\"/api\">/api</a>",
"api_key": "Chiave API",
"api_skip_ip_check": "Salta il controllo dell'IP per l'API",
@@ -156,7 +157,7 @@
"dkim_domains_selector": "Selettore",
"dkim_domains_wo_keys": "Seleziona i domini senza chiavi",
"dkim_from": "Da",
"dkim_from_title": "Source domain to copy data from",
"dkim_from_title": "Dominio di origine da cui copiare i dati",
"dkim_key_length": "Lunghezza chiave DKIM (bits)",
"dkim_key_missing": "Chiave mancante",
"dkim_key_unused": "Chiave non usata",
@@ -182,8 +183,8 @@
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
"f2b_max_attempts": "Tentativi massimi",
"f2b_max_ban_time": "Tempo massimo di blocco (s)",
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
"f2b_netban_ipv4": "Dimensione della subnet IPv4 su cui applicare il blocco (8-32)",
"f2b_netban_ipv6": "Dimensione della subnet IPv6 su cui applicare il blocco (8-128)",
"f2b_parameters": "Parametri Fail2ban",
"f2b_regex_info": "Log presi in considerazione: SOGo, Postfix, Dovecot, PHP-FPM.",
"f2b_retry_window": "Retry window (s) for max. attempts",
@@ -219,16 +220,16 @@
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
"message": "Messaggio",
"message_size": "Dimensione mesaggio",
"nexthop": "Next hop",
"nexthop": "Prossimo hop",
"no": "&#10005;",
"no_active_bans": "Nessun ban attivo",
"no_new_rows": "Nessuna ulteriore riga disponibile",
"no_record": "Nessun risultato",
"oauth2_client_id": "ID cliente",
"oauth2_client_secret": "Client secret",
"oauth2_client_secret": "Chiave segreta del client",
"oauth2_info": "The OAuth2 implementation supports the grant type \"Authorization Code\" and issues refresh tokens.<br>\r\nThe server also automatically issues new refresh tokens, after a refresh token has been used.<br><br>\r\n&#8226; The default scope is <i>profile</i>. Only mailbox users can be authenticated against OAuth2. If the scope parameter is omitted, it falls back to <i>profile</i>.<br>\r\n&#8226; The <i>state</i> parameter is required to be sent by the client as part of the authorize request.<br><br>\r\nPaths for requests to the OAuth2 API: <br>\r\n<ul>\r\n <li>Authorization endpoint: <code>/oauth/authorize</code></li>\r\n <li>Token endpoint: <code>/oauth/token</code></li>\r\n <li>Resource page: <code>/oauth/profile</code></li>\r\n</ul>\r\nRegenerating the client secret will not expire existing authorization codes, but they will fail to renew their token.<br><br>\r\nRevoking client tokens will cause immediate termination of all active sessions. All clients need to re-authenticate.",
"oauth2_redirect_uri": "URI di reindirizzamento",
"oauth2_renew_secret": "Generate new client secret",
"oauth2_renew_secret": "Genera una nuova chiave segreta per il client",
"oauth2_revoke_tokens": "Revoca tutti i token del client",
"optional": "facoltativo",
"password": "Password",
@@ -269,7 +270,7 @@
"recipients": "Destinatari",
"refresh": "Aggiorna",
"regen_api_key": "Rinnova la chiave delle API",
"regex_maps": "Regex maps",
"regex_maps": "Mappe Regex",
"relay_from": "\"Da:\" indirizzi",
"relay_rcpt": "\"A:\" indirizzi",
"relay_run": "Esegui test",
@@ -286,7 +287,7 @@
"rsetting_no_selection": "Seleziona una regola",
"rsetting_none": "Nessuna regola presente",
"rsettings_insert_preset": "Insert example preset \"%s\"",
"rsettings_preset_1": "Disable all but DKIM and rate limit for authenticated users",
"rsettings_preset_1": "Disattivare tutto tranne DKIM e il limite di velocità per gli utenti autenticati",
"rsettings_preset_2": "I postmaster vogliono lo spam",
"rsettings_preset_3": "Consenti solo mittenti specifici per una casella di posta (ad esempio: utilizzo solo come casella di posta interna)",
"rspamd_com_settings": "A setting name will be auto-generated, please see the example presets below. For more details see <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd docs</a>",
@@ -341,7 +342,18 @@
"rsettings_preset_4": "Disattivare Rspamd per un dominio",
"options": "Opzioni",
"cors_settings": "Impostazioni CORS",
"copy_to_clipboard": "Testo copiato negli appunti!"
"copy_to_clipboard": "Testo copiato negli appunti!",
"f2b_manage_external": "Gestione esterna di Fail2Ban",
"password_reset_info": "Se non viene fornita alcuna e-mail di recupero, questa funzione non può essere utilizzata.",
"password_reset_tmpl_text": "Template testuale",
"logo_dark_label": "Invertito per la modalità scura",
"ip_check": "Controllo IP",
"password_reset_settings": "Impostazioni per il recupero della password",
"password_reset_tmpl_html": "Template HTML",
"password_settings": "Impostazioni della password",
"queue_unban": "sblocca",
"restore_template": "Lasciare vuoto per ripristinare il modello predefinito.",
"logo_normal_label": "Normale"
},
"danger": {
"access_denied": "Accesso negato o form di login non corretto",
@@ -466,7 +478,13 @@
"template_exists": "Il template %s esiste già",
"template_id_invalid": "Il template con ID %s non è valido",
"img_dimensions_exceeded": "L'immagine supera la dimensione massima consentita",
"img_size_exceeded": "L'immagine supera la dimensione massima del file"
"img_size_exceeded": "L'immagine supera la dimensione massima del file",
"extended_sender_acl_denied": "Autorizzazioni ACL mancanti per configurare indirizzi esterni come mittente",
"invalid_reset_token": "Token di reset non valido",
"password_reset_invalid_user": "La casella di posta elettronica non è stata trovata o non è stata impostata un'e-mail di recupero",
"password_reset_na": "Il recupero della password non è attualmente disponibile. Contattare l'amministratore.",
"recovery_email_failed": "Impossibile inviare un'e-mail di recupero. Contattare l'amministratore.",
"to_invalid": "Il destinatario non deve essere vuoto"
},
"debug": {
"chart_this_server": "Grafico (questo server)",
@@ -503,7 +521,11 @@
"memory": "Memoria",
"timezone": "Fuso orario",
"no_update_available": "Il sistema è aggiornato all'ultima versione",
"update_failed": "Impossibile verificare la presenza di un aggiornamento"
"update_failed": "Impossibile verificare la presenza di un aggiornamento",
"architecture": "Architettura",
"error_show_ip": "Impossibile risolvere gli indirizzi IP pubblici",
"show_ip": "Mostra IP pubblico",
"wip": "Attualmente in lavorazione"
},
"diagnostics": {
"cname_from_a": "Valore letto dal record A/AAAA. Questo è supportato finché il record punta alla risorsa corretta.",
@@ -632,7 +654,11 @@
"last_modified": "Ultima modifica",
"pushover_sound": "Suono",
"custom_attributes": "Attributi personalizzati",
"domain_footer_skip_replies": "Ignora il piè di pagina nelle e-mail di risposta"
"domain_footer_skip_replies": "Ignora il piè di pagina nelle e-mail di risposta",
"domain_footer_html": "Piè di pagina HTML",
"domain_footer_plain": "Piè di pagina PLAIN",
"footer_exclude": "Escludi dal piè di pagina",
"password_recovery_email": "E-mail di recupero password"
},
"fido2": {
"confirm": "Conferma",
@@ -688,7 +714,13 @@
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
"other_logins": "Key login",
"password": "Password",
"username": "Nome utente"
"username": "Nome utente",
"request_reset_password": "Richiesta di modifica della password",
"back_to_mailcow": "Torna a mailcow",
"forgot_password": "> Password dimenticata?",
"new_password": "Nuova password",
"new_password_confirm": "Conferma la nuova password",
"reset_password": "Ripristino della password"
},
"mailbox": {
"action": "Azione",
@@ -939,7 +971,9 @@
"show_message": "Mostra messaggio",
"unhold_mail": "Sblocca",
"hold_mail_legend": "Blocca le mail selezionate. (Previene ulteriori tentativi di consegna)",
"legend": "Funzioni delle azioni della coda di posta:"
"legend": "Funzioni delle azioni della coda di posta:",
"unban": "Coda di sblocco",
"unhold_mail_legend": "Rilascia le mail selezionate per la consegna. (Richiede una prenotazione preventiva)"
},
"start": {
"help": "Mostra/Nascondi pannello di aiuto",
@@ -1028,7 +1062,12 @@
"template_added": "Aggiunto template %s",
"template_modified": "Le modifiche al template %s sono state salvate",
"template_removed": "Il template con ID %s è stato cancellato",
"f2b_banlist_refreshed": "L'ID della lista blocchi è stato aggiornato con successo."
"f2b_banlist_refreshed": "L'ID della lista blocchi è stato aggiornato con successo.",
"domain_footer_modified": "Le modifiche al piè di pagina del dominio %s sono state salvate",
"cors_headers_edited": "Le impostazioni CORS sono state salvate",
"ip_check_opt_in_modified": "Il controllo dell'indirizzo IP è stato salvato con successo",
"password_changed_success": "La password è stata modificata con successo",
"recovery_email_sent": "Email di recupero inviata a %s"
},
"tfa": {
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
@@ -1220,7 +1259,9 @@
"direct_protocol_access": "Questo utente della mailbox ha <b>accesso diretto ed esterno</b> ai seguenti protocolli e applicazioni. Questa impostazione è controllata dal tuo amministratore. Le password delle applicazioni possono essere create per garantire l'accesso ai singoli protocolli e applicazioni.<br>Il pulsante \"Accedi alla webmail\" fornisce un singolo accesso a SOGo ed è sempre disponibile.",
"pushover_sound": "Suono",
"attribute": "Attributo",
"value": "Valore"
"value": "Valore",
"password_reset_info": "Se non viene fornita alcuna e-mail per il recupero della password, questa funzione non può essere utilizzata.",
"pw_recovery_email": "Email di recupero password"
},
"warning": {
"cannot_delete_self": "Cannot delete logged in user",
@@ -1264,6 +1305,7 @@
"aria": {
"sortAscending": ": attivare l'ordinamento crescente delle colonne",
"sortDescending": ": attivare l'ordinamento decrescente delle colonne"
}
},
"decimal": "."
}
}

View File

@@ -28,7 +28,8 @@
"spam_score": "Pontuação de spam",
"syncjobs": "Trabalhos de sincronização",
"tls_policy": "Política de TLS",
"unlimited_quota": "Cota ilimitada para mailboxes"
"unlimited_quota": "Cota ilimitada para mailboxes",
"pw_reset": "Permite redefinir a senha do usuário"
},
"add": {
"activate_filter_warn": "Todos os outros filtros serão desativados quando a opção ativa estiver marcada.",

View File

@@ -28,7 +28,8 @@
"spam_score": "Политика фильтрации спама",
"syncjobs": "Задания синхронизации",
"tls_policy": "Политика шифрования",
"unlimited_quota": "Неограниченная квота для почтовых ящиков"
"unlimited_quota": "Неограниченная квота для почтовых ящиков",
"pw_reset": "Разрешить сброс пароля пользователей mailcow"
},
"add": {
"activate_filter_warn": "Активация этого фильтра отключит все остальные фильтры этого типа.",

View File

@@ -28,13 +28,14 @@
"app_passwds": "Паролі додатків",
"domain_relayhost": "Змінити relayhost для домену",
"login_as": "Увійти як користувач поштової скриньки",
"sogo_profile_reset": "Скинути профіль SOGo"
"sogo_profile_reset": "Скинути профіль SOGo",
"pw_reset": "Скидання паролю користувача"
},
"add": {
"app_name": "Назва додатка",
"app_password": "Додати пароль додатка",
"app_passwd_protocols": "Дозволені протоколи для пароля додатка",
"comment_info": "Приватний коментар не видно користувачам, а публічний відображається поряд із псевдонімом в особистому кабінеті користувача.",
"comment_info": "Приватний коментар не видно користувачам, а публічний відображається поряд із псевдонімом в особистому кабінеті користувача",
"custom_params": "Налаштування користувача",
"gal": "GAL - Глобальна адресна книга",
"mailbox_quota_m": "Максимальна квота поштового акаунту (MiB)",
@@ -77,7 +78,7 @@
"password_repeat": "Підтвердження пароля (повтор)",
"post_domain_add": "Після додавання нового домену контейнер SOGo (\"sogo-mailcow\") необхідно перезапустити!<br><br>Крім того, слід перевірити конфігурацію DNS доменів. Після затвердження конфігурації DNS перезапустіть контейнер \"acme-mailcow\", щоб автоматично згенерувати сертифікати для вашого нового домену.<br>Цей крок не є обов'язковим і повторюватиметься кожні 24 години.",
"relay_all_info": "Якщо ви вирішите <b>не</b> ретранслювати всіх одержувачів, вам потрібно буде додати (\"сліпу\") поштову адресу для кожного одержувача, якого слід ретранслювати.",
"relay_transport_info": "<div class=\"label label-info\">Інфо</div> Ви можете налаштувати власний транспорт для домену. Якщо такої установки немає, то доставка буде виконана на основі MX-записів.",
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Інфо</div> Ви можете налаштувати власний транспорт для домену. Якщо такої установки немає, то доставка буде виконана на основі MX-записів.",
"relayhost_wrapped_tls_info": "Будь ласка, <b>не</b> використовуйте TLS порти (в основному це 465 порт).<br>\nВикористовуйте будь-який <b>не</b> порт TLS, який підтримує STARTTLS. А для захисту від downgrate атак - налаштуйте примусову політику TLS.",
"syncjob_hint": "Паролі до вашого акаунту будуть збережені на сервері у вигляді простого тексту!",
"timeout1": "Тайм-аут для підключення до віддаленого хоста",
@@ -299,7 +300,7 @@
"api_allow_from": "Список IP-адрес для доступу до API (розділених комою або новим рядком)",
"api_skip_ip_check": "Пропустити перевірку IP для API",
"arrival_time": "Время получения (час. пояс сервера)",
"ban_list_info": "Список заблокованих IP-адрес: <b>підмережа (час, що залишився) - [дія]</b>.<br />IP-адреси, що знаходяться в черзі на розблокування, будуть видалені зі списку активних блокувань протягом декількох секунд.<br> />Червона мітка означає, що підмережа/хост знаходиться в чорному списку.",
"ban_list_info": "Список заблокованих IP-адрес: <b>підмережа (час, що залишився) - [дія]</b>.<br />IP-адреси, що знаходяться в черзі на розблокування, будуть видалені зі списку активних блокувань протягом декількох секунд.<br>Червона мітка означає, що підмережа/хост знаходиться в чорному списку.",
"credentials_transport_warning": "<b>Попередження</b>: додавання нового запису перезапише облікові дані для всіх записів з таким самим <i>наступним хостом</i>.",
"dkim_to_title": "Цільовий домен(и) (DKIM буде перезаписаний)",
"duplicate_dkim": "Копіювання DKIM запису",
@@ -349,7 +350,13 @@
"queue_unban": "розблокувати",
"f2b_manage_external": "Керування Fail2Ban ззовні",
"f2b_manage_external_info": "Fail2ban буде підтримувати список заборонених, але не буде активно встановлювати правила для блокування трафіку. Використовуйте згенерований список заборон нижче для зовнішнього блокування трафіку.",
"copy_to_clipboard": "Текст скопійовано в буфер обміну!"
"copy_to_clipboard": "Текст скопійовано в буфер обміну!",
"password_reset_tmpl_text": "Plain-text шаблон",
"password_reset_info": "Якщо електронну адресу для відновлення не надано, ця функція не може бути використана.",
"logo_dark_label": "Темна тема",
"password_reset_settings": "Налаштування відновлення паролів",
"password_reset_tmpl_html": "HTML шаблон",
"logo_normal_label": "Світла тема"
},
"danger": {
"alias_domain_invalid": "Неприпустимий псевдонім домену: %s",
@@ -479,7 +486,8 @@
"template_exists": "Шаблон %s вже існує",
"template_id_invalid": "Ідентифікатор шаблону %s недійсний",
"template_name_invalid": "Ім'я шаблону невірне",
"img_size_exceeded": "Зображення перевищує максимальний розмір файлу"
"img_size_exceeded": "Зображення перевищує максимальний розмір файлу",
"img_dimensions_exceeded": "Зображення перевищує максимальний розмір"
},
"debug": {
"chart_this_server": "Діаграма (цей сервер)",
@@ -661,7 +669,8 @@
},
"domain_footer_html": "Нижній колонтитул HTML",
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул",
"custom_attributes": "Користувацькі атрибути"
"custom_attributes": "Користувацькі атрибути",
"domain_footer_skip_replies": "Ігнорувати нижній колонтитул у листах-відповідях"
},
"fido2": {
"confirm": "Підтвердити",
@@ -1068,7 +1077,8 @@
"cors_headers_edited": "Налаштування CORS збережено",
"ip_check_opt_in_modified": "Перевірка IP-адреси успішно збережено",
"template_removed": "Шаблону із ID %s видалено",
"f2b_banlist_refreshed": "Ідентифікатор списку заборонених успішно оновлено."
"f2b_banlist_refreshed": "Ідентифікатор списку заборонених успішно оновлено.",
"domain_footer_modified": "Зміни в нижньому колонтитулі домену %s збережено"
},
"tfa": {
"confirm": "Підтвердьте",
@@ -1095,7 +1105,8 @@
"set_tfa": "Встановити метод двофакторної перевірки",
"u2f_deprecated": "Схоже, ваш ключ був зареєстрований за допомогою застарілого методу U2F. Ми дезактивуємо двофакторну автентифікацію для вас і видалимо ваш ключ.",
"waiting_usb_auth": "<i>Очікування пристрою USB...</i><br><br>Будь ласка, натисніть зараз кнопку на USB пристрої.",
"waiting_usb_register": "<i>Очікування USB-пристрою...</i><br><br>Будь ласка, введіть пароль вище та підтвердіть реєстрацію, натиснувши кнопку на USB пристрої."
"waiting_usb_register": "<i>Очікування USB-пристрою...</i><br><br>Будь ласка, введіть пароль вище та підтвердіть реєстрацію, натиснувши кнопку на USB пристрої.",
"authenticators": "Аутентифікатори"
},
"user": {
"action": "Дії",

View File

@@ -111,13 +111,14 @@ services:
- rspamd
php-fpm-mailcow:
image: mailcow/phpfpm:1.88
image: mailcow/phpfpm:1.90
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
volumes:
- ./data/hooks/phpfpm:/hooks:Z
- ./data/web:/web:z
- ./data/cron/phpfpm:/cron:z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/conf/rspamd/custom/:/rspamd_custom_maps:z
- rspamd-vol-1:/var/lib/rspamd
@@ -169,6 +170,10 @@ services:
- WEBAUTHN_ONLY_TRUSTED_VENDORS=${WEBAUTHN_ONLY_TRUSTED_VENDORS:-n}
- CLUSTERMODE=${CLUSTERMODE:-}
- FLATCURVE_EXPERIMENTAL=${FLATCURVE_EXPERIMENTAL:-}
labels:
ofelia.enabled: "true"
ofelia.job-exec.php_clear_sasl_log.schedule: "@every 1d"
ofelia.job-exec.php_clear_sasl_log.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && php /cron/clear_sasl_log.php || exit 0\""
restart: always
networks:
mailcow-network:
@@ -176,7 +181,7 @@ services:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.124
image: mailcow/sogo:1.125
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -223,7 +228,7 @@ services:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:2.0
image: mailcow/dovecot:2.1
depends_on:
- mysql-mailcow
- netfilter-mailcow
@@ -307,7 +312,7 @@ services:
- dovecot
postfix-mailcow:
image: mailcow/postfix:1.75
image: mailcow/postfix:1.76
depends_on:
mysql-mailcow:
condition: service_started
@@ -407,7 +412,7 @@ services:
condition: service_started
unbound-mailcow:
condition: service_healthy
image: mailcow/acme:1.89
image: mailcow/acme:1.90
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
@@ -463,7 +468,7 @@ services:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
image: mailcow/watchdog:2.04
image: mailcow/watchdog:2.05
dns:
- ${IPV4_NETWORK:-172.22.1}.254
tmpfs:
@@ -552,7 +557,7 @@ services:
aliases:
- dockerapi
##### Will be removed soon #####
solr-mailcow:
image: mailcow/solr:1.8.3

View File

@@ -9,7 +9,7 @@ if [[ "$(uname -r)" =~ ^4\.15\.0-60 ]]; then
fi
if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
if grep -q Ubuntu <<< $(uname -a); then
if grep -q Ubuntu <<< "$(uname -a)"; then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!";
echo "Please update to linux-generic-hwe-16.04 by running \"apt-get install --install-recommends linux-generic-hwe-16.04\""
exit 1
@@ -41,7 +41,7 @@ if docker compose > /dev/null 2>&1; then
echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
sleep 2
echo -e "\e[33mNotice: You´ll have to update this Compose Version via your Package Manager manually!\e[0m"
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
@@ -158,7 +158,7 @@ done
MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
if [ -z "${SKIP_CLAMD}" ]; then
if [ ${MEM_TOTAL} -le "2621440" ]; then
if [ "${MEM_TOTAL}" -le "2621440" ]; then
echo "Installed memory is <= 2.5 GiB. It is recommended to disable ClamAV to prevent out-of-memory situations."
echo "ClamAV can be re-enabled by setting SKIP_CLAMD=n in mailcow.conf."
read -r -p "Do you want to disable ClamAV now? [Y/n] " response
@@ -176,10 +176,10 @@ if [ -z "${SKIP_CLAMD}" ]; then
fi
if [ -z "${SKIP_SOLR}" ]; then
if [ ${MEM_TOTAL} -le "2097152" ]; then
if [ "${MEM_TOTAL}" -le "2097152" ]; then
echo "Disabling Solr on low-memory system."
SKIP_SOLR=y
elif [ ${MEM_TOTAL} -le "3670016" ]; then
elif [ "${MEM_TOTAL}" -le "3670016" ]; then
echo "Installed memory is <= 3.5 GiB. It is recommended to disable Solr to prevent out-of-memory situations."
echo "Solr is a prone to run OOM and should be monitored. The default Solr heap size is 1024 MiB and should be set in mailcow.conf according to your expected load."
echo "Solr can be re-enabled by setting SKIP_SOLR=n in mailcow.conf but will refuse to start with less than 2 GB total memory."
@@ -206,7 +206,7 @@ if [[ ${SKIP_BRANCH} != y ]]; then
sleep 1
while [ -z "${MAILCOW_BRANCH}" ]; do
read -r -p "Choose the Branch with it´s number [1/2] " branch
read -r -p "Choose the Branch with it's number [1/2] " branch
case $branch in
[2])
MAILCOW_BRANCH="nightly"
@@ -218,7 +218,7 @@ if [[ ${SKIP_BRANCH} != y ]]; then
done
git fetch --all
git checkout -f $MAILCOW_BRANCH
git checkout -f "$MAILCOW_BRANCH"
elif [[ ${SKIP_BRANCH} == y ]]; then
echo -e "\033[33mEnabled Dev Mode.\033[0m"

312
update.sh
View File

@@ -22,13 +22,13 @@ prefetch_images() {
fi
fi
RET_C=0
until docker pull ${image}; do
until docker pull "${image}"; do
RET_C=$((RET_C + 1))
echo -e "\e[33m\nError pulling $image, retrying...\e[0m"
[ ${RET_C} -gt 3 ] && { echo -e "\e[31m\nToo many failed retries, exiting\e[0m"; exit 1; }
sleep 1
done
done < <(git show origin/${BRANCH}:docker-compose.yml | grep "image:" | awk '{ gsub("image:","", $3); print $2 }')
done < <(git show "origin/${BRANCH}:docker-compose.yml" | grep "image:" | awk '{ gsub("image:","", $3); print $2 }')
}
docker_garbage() {
@@ -39,9 +39,9 @@ docker_garbage() {
COMPOSE_IMAGES=($(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml"))
for existing_image in $(docker images --format "{{.ID}}:{{.Repository}}:{{.Tag}}" | grep 'mailcow/'); do
ID=$(echo $existing_image | cut -d ':' -f 1)
REPOSITORY=$(echo $existing_image | cut -d ':' -f 2)
TAG=$(echo $existing_image | cut -d ':' -f 3)
ID=$(echo "$existing_image" | cut -d ':' -f 1)
REPOSITORY=$(echo "$existing_image" | cut -d ':' -f 2)
TAG=$(echo "$existing_image" | cut -d ':' -f 3)
if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then
continue
@@ -109,12 +109,12 @@ migrate_docker_nat() {
echo -e "\e[33mWarning:\e[0m You seem to have modified the /etc/docker/daemon.json configuration by yourself and not fully/correctly activated the native IPv6 NAT implementation."
echo "You will need to merge your existing configuration manually or fix/delete the existing daemon.json configuration before trying the update process again."
echo -e "Please merge the following content and restart the Docker daemon:\n"
echo ${NAT_CONFIG}
echo "${NAT_CONFIG}"
return 1
fi
else
echo "Working on IPv6 NAT, please wait..."
echo ${NAT_CONFIG} > /etc/docker/daemon.json
echo "${NAT_CONFIG}" > /etc/docker/daemon.json
ip6tables -F -t nat
[[ -e /etc/rc.conf ]] && rc-service docker restart || systemctl restart docker.service
if [[ $? -ne 0 ]]; then
@@ -165,7 +165,7 @@ remove_obsolete_nginx_ports() {
fi
fi
fi
done
done
}
detect_docker_compose_command(){
@@ -176,11 +176,11 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
COMPOSE_COMMAND="docker compose"
echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' "$SCRIPT_DIR/mailcow.conf"
sleep 2
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
@@ -191,58 +191,58 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
COMPOSE_COMMAND="docker-compose"
echo -e "\e[33mFound Docker Compose Standalone.\e[0m"
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' "$SCRIPT_DIR/mailcow.conf"
sleep 2
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
fi
else
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
COMPOSE_COMMAND="docker compose"
# Check if Native Compose works and has not been deleted
# Check if Native Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker-compose"
if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
# IF it cannot find Standalone in > 2.X, then script stops
echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m"
echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
# If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from native to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' "$SCRIPT_DIR/mailcow.conf"
sleep 2
fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
COMPOSE_COMMAND="docker-compose"
# Check if Standalone Compose works and has not been deleted
# Check if Standalone Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1 && ! $COMPOSE_COMMAND --version > /dev/null 2>&1 | grep "^2." > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker compose"
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it cannot find Native in > 2.X, then script stops
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
# If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from standalone to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' "$SCRIPT_DIR/mailcow.conf"
sleep 2
fi
fi
@@ -297,7 +297,7 @@ if [[ "$(uname -r)" =~ ^4\.15\.0-60 ]]; then
fi
if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
if grep -q Ubuntu <<< $(uname -a); then
if grep -q Ubuntu <<< "$(uname -a)"; then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!"
echo "Please update to linux-generic-hwe-16.04 by running \"apt-get install --install-recommends linux-generic-hwe-16.04\""
exit 1
@@ -322,14 +322,14 @@ unset COMPOSE_COMMAND
unset DOCKER_COMPOSE_VERSION
for bin in curl docker git awk sha1sum grep cut; do
if [[ -z $(command -v ${bin}) ]]; then
echo "Cannot find ${bin}, exiting..."
if [[ -z $(command -v ${bin}) ]]; then
echo "Cannot find ${bin}, exiting..."
exit 1;
fi
fi
done
# Check Docker Version (need at least 24.X)
docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | cut -d '.' -f 1)
docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | cut -d '.' -f 1 | head -1)
if [[ $docker_version -lt 24 ]]; then
echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"
@@ -340,20 +340,35 @@ fi
export LC_ALL=C
DATE=$(date +%Y-%m-%d_%H_%M_%S)
BRANCH=$(cd ${SCRIPT_DIR}; git rev-parse --abbrev-ref HEAD)
BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"
while (($#)); do
case "${1}" in
--check|-c)
echo "Checking remote code for updates..."
LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
if [ $? -ne 0 ]; then
LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized "${BRANCH}" | cut -f1)
if [ "$?" -ne 0 ]; then
echo "A problem occurred while trying to fetch the latest revision from github."
exit 99
fi
if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
echo -e "Updated code is available.\nThe changes can be found here: https://github.com/mailcow/mailcow-dockerized/commits/master"
git log --date=short --pretty=format:"%ad - %s" $(git rev-parse --short HEAD)..origin/master
git log --date=short --pretty=format:"%ad - %s" "$(git rev-parse --short HEAD)"..origin/master
exit 0
else
echo "No updates available."
exit 3
fi
;;
--check-tags)
echo "Checking remote tags for updates..."
LATEST_TAG_REV=$(git ls-remote --exit-code --quiet --tags origin | tail -1 | cut -f1)
if [ "$?" -ne 0 ]; then
echo "A problem occurred while trying to fetch the latest tag from github."
exit 99
fi
if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_TAG_REV}") ]]; then
echo -e "New tag is available.\nThe changes can be found here: https://github.com/mailcow/mailcow-dockerized/releases/latest"
exit 0
else
echo "No updates available."
@@ -370,7 +385,7 @@ while (($#)); do
SKIP_PING_CHECK=y
;;
--stable)
CURRENT_BRANCH="$(cd ${SCRIPT_DIR}; git rev-parse --abbrev-ref HEAD)"
CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"
NEW_BRANCH="master"
;;
--gc)
@@ -379,7 +394,7 @@ while (($#)); do
exit 0
;;
--nightly)
CURRENT_BRANCH="$(cd ${SCRIPT_DIR}; git rev-parse --abbrev-ref HEAD)"
CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"
NEW_BRANCH="nightly"
;;
--prefetch)
@@ -396,15 +411,16 @@ while (($#)); do
DEV=y
;;
--help|-h)
echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
echo './update.sh [-c|--check, --check-tags, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
-c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
--check-tags - Check for newer tags and exit (exit codes => 0: newer tag available, 3: no newer tag)
--ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
--gc - Run garbage collector to delete old image tags
--nightly - Switch your mailcow updates to the unstable (nightly) branch. FOR TESTING PURPOSES ONLY!!!!
--prefetch - Only prefetch new images and exit (useful to prepare updates)
--skip-start - Do not start mailcow after update
--skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine)
--skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you'\''ve blocked any ICMP Connections to your mailcow machine)
--stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly.
-f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
@@ -499,19 +515,19 @@ CONFIG_ARRAY=(
detect_bad_asn
sed -i --follow-symlinks '$a\' mailcow.conf
for option in ${CONFIG_ARRAY[@]}; do
for option in "${CONFIG_ARRAY[@]}"; do
if [[ ${option} == "ADDITIONAL_SAN" ]]; then
if ! grep -q ${option} mailcow.conf; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=" >> mailcow.conf
fi
elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "COMPOSE_PROJECT_NAME" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "COMPOSE_PROJECT_NAME=mailcowdockerized" >> mailcow.conf
fi
elif [[ ${option} == "DOCKER_COMPOSE_VERSION" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "DOCKER_COMPOSE_VERSION" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "# Used Docker Compose version" >> mailcow.conf
echo "# Switch here between native (compose plugin) and standalone" >> mailcow.conf
@@ -521,73 +537,73 @@ for option in ${CONFIG_ARRAY[@]}; do
echo "" >> mailcow.conf
echo "DOCKER_COMPOSE_VERSION=${DOCKER_COMPOSE_VERSION}" >> mailcow.conf
fi
elif [[ ${option} == "DOVEADM_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "DOVEADM_PORT" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_NOTIFY_EMAIL" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf
fi
elif [[ ${option} == "LOG_LINES" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "LOG_LINES" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf
echo "LOG_LINES=9999" >> mailcow.conf
fi
elif [[ ${option} == "IPV4_NETWORK" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "IPV4_NETWORK" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf
echo "IPV4_NETWORK=172.22.1" >> mailcow.conf
fi
elif [[ ${option} == "IPV6_NETWORK" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "IPV6_NETWORK" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Internal IPv6 subnet in fc00::/7' >> mailcow.conf
echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf
fi
elif [[ ${option} == "SQL_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SQL_PORT" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
echo "SQL_PORT=127.0.0.1:13306" >> mailcow.conf
fi
elif [[ ${option} == "API_KEY" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "API_KEY" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create or override API key for web UI' >> mailcow.conf
echo "#API_KEY=" >> mailcow.conf
fi
elif [[ ${option} == "API_KEY_READ_ONLY" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "API_KEY_READ_ONLY" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create or override read-only API key for web UI' >> mailcow.conf
echo "#API_KEY_READ_ONLY=" >> mailcow.conf
fi
elif [[ ${option} == "API_ALLOW_FROM" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "API_ALLOW_FROM" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Must be set for API_KEY to be active' >> mailcow.conf
echo '# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
echo "#API_ALLOW_FROM=" >> mailcow.conf
fi
elif [[ ${option} == "SNAT_TO_SOURCE" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SNAT_TO_SOURCE" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
echo "#SNAT_TO_SOURCE=" >> mailcow.conf
fi
elif [[ ${option} == "SNAT6_TO_SOURCE" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SNAT6_TO_SOURCE" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
echo "#SNAT6_TO_SOURCE=" >> mailcow.conf
fi
elif [[ ${option} == "MAILDIR_GC_TIME" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "MAILDIR_GC_TIME" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Garbage collector cleanup' >> mailcow.conf
echo '# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
@@ -595,8 +611,8 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Check interval is hourly' >> mailcow.conf
echo 'MAILDIR_GC_TIME=1440' >> mailcow.conf
fi
elif [[ ${option} == "ACL_ANYONE" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "ACL_ANYONE" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
echo '# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
@@ -604,96 +620,96 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
echo 'ACL_ANYONE=disallow' >> mailcow.conf
fi
elif [[ ${option} == "SOLR_HEAP" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SOLR_HEAP" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Solr heap size, there is no recommendation, please see Solr docs.' >> mailcow.conf
echo '# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended.' >> mailcow.conf
echo '# Solr will refuse to start with total system memory below or equal to 2 GB.' >> mailcow.conf
echo "SOLR_HEAP=1024" >> mailcow.conf
fi
elif [[ ${option} == "SKIP_SOLR" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SKIP_SOLR" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Solr is disabled by default after upgrading from non-Solr to Solr-enabled mailcows.' >> mailcow.conf
echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
echo "SKIP_SOLR=y" >> mailcow.conf
fi
elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "ENABLE_SSL_SNI" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create seperate certificates for all domains - y/n' >> mailcow.conf
echo '# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
echo '# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
echo "ENABLE_SSL_SNI=n" >> mailcow.conf
fi
elif [[ ${option} == "SKIP_SOGO" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SKIP_SOGO" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
echo "SKIP_SOGO=n" >> mailcow.conf
fi
elif [[ ${option} == "MAILDIR_SUB" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "MAILDIR_SUB" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
echo "MAILDIR_SUB=" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_WEBHOOK" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_NOTIFY_WEBHOOK" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Send notifications to a webhook URL that receives a POST request with the content type "application/json".' >> mailcow.conf
echo '# You can use this to send notifications to services like Discord, Slack and others.' >> mailcow.conf
echo '#WATCHDOG_NOTIFY_WEBHOOK=https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_WEBHOOK_BODY" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_NOTIFY_WEBHOOK_BODY" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# JSON body included in the webhook POST request. Needs to be in single quotes.' >> mailcow.conf
echo '# Following variables are available: SUBJECT, BODY' >> mailcow.conf
WEBHOOK_BODY='{"username": "mailcow Watchdog", "content": "**${SUBJECT}**\n${BODY}"}'
echo "#WATCHDOG_NOTIFY_WEBHOOK_BODY='${WEBHOOK_BODY}'" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_BAN" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_NOTIFY_BAN" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Notify about banned IP. Includes whois lookup.' >> mailcow.conf
echo "WATCHDOG_NOTIFY_BAN=y" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_START" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_NOTIFY_START" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Send a notification when the watchdog is started.' >> mailcow.conf
echo "WATCHDOG_NOTIFY_START=y" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_SUBJECT" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_SUBJECT" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Subject for watchdog mails. Defaults to "Watchdog ALERT" followed by the error message.' >> mailcow.conf
echo "#WATCHDOG_SUBJECT=" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_EXTERNAL_CHECKS" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_EXTERNAL_CHECKS" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.' >> mailcow.conf
echo '# No data is collected. Opt-in and anonymous.' >> mailcow.conf
echo '# Will only work with unmodified mailcow setups.' >> mailcow.conf
echo "WATCHDOG_EXTERNAL_CHECKS=n" >> mailcow.conf
fi
elif [[ ${option} == "SOGO_EXPIRE_SESSION" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SOGO_EXPIRE_SESSION" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# SOGo session timeout in minutes' >> mailcow.conf
echo "SOGO_EXPIRE_SESSION=480" >> mailcow.conf
fi
elif [[ ${option} == "REDIS_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "REDIS_PORT" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "REDIS_PORT=127.0.0.1:7654" >> mailcow.conf
fi
elif [[ ${option} == "DOVECOT_MASTER_USER" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "DOVECOT_MASTER_USER" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# DOVECOT_MASTER_USER and _PASS must _both_ be provided. No special chars.' >> mailcow.conf
echo '# Empty by default to auto-generate master user and password on start.' >> mailcow.conf
@@ -701,22 +717,22 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
echo "DOVECOT_MASTER_USER=" >> mailcow.conf
fi
elif [[ ${option} == "DOVECOT_MASTER_PASS" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "DOVECOT_MASTER_PASS" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
echo "DOVECOT_MASTER_PASS=" >> mailcow.conf
fi
elif [[ ${option} == "MAILCOW_PASS_SCHEME" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "MAILCOW_PASS_SCHEME" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Password hash algorithm' >> mailcow.conf
echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
echo '# see https://docs.mailcow.email/models/model-passwd/' >> mailcow.conf
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
fi
elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "ADDITIONAL_SERVER_NAMES" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Additional server names for mailcow UI' >> mailcow.conf
echo '#' >> mailcow.conf
@@ -728,8 +744,8 @@ for option in ${CONFIG_ARRAY[@]}; do
echo 'ADDITIONAL_SERVER_NAMES=' >> mailcow.conf
fi
elif [[ ${option} == "AUTODISCOVER_SAN" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "AUTODISCOVER_SAN" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Obtain certificates for autodiscover.* and autoconfig.* domains.' >> mailcow.conf
echo '# This can be useful to switch off in case you are in a scenario where a reverse proxy already handles those.' >> mailcow.conf
@@ -739,8 +755,8 @@ for option in ${CONFIG_ARRAY[@]}; do
echo 'AUTODISCOVER_SAN=y' >> mailcow.conf
fi
elif [[ ${option} == "ACME_CONTACT" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "ACME_CONTACT" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Lets Encrypt registration contact information' >> mailcow.conf
echo '# Optional: Leave empty for none' >> mailcow.conf
@@ -749,16 +765,16 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# https://docs.mailcow.email/troubleshooting/debug-reset_tls/' >> mailcow.conf
echo 'ACME_CONTACT=' >> mailcow.conf
fi
elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "# WebAuthn device manufacturer verification" >> mailcow.conf
echo '# After setting WEBAUTHN_ONLY_TRUSTED_VENDORS=y only devices from trusted manufacturers are allowed' >> mailcow.conf
echo '# root certificates can be placed for validation under mailcow-dockerized/data/web/inc/lib/WebAuthn/rootCertificates' >> mailcow.conf
echo 'WEBAUTHN_ONLY_TRUSTED_VENDORS=n' >> mailcow.conf
fi
elif [[ ${option} == "SPAMHAUS_DQS_KEY" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SPAMHAUS_DQS_KEY" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "# Spamhaus Data Query Service Key" >> mailcow.conf
echo '# Optional: Leave empty for none' >> mailcow.conf
@@ -767,32 +783,32 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Otherwise it will work as usual.' >> mailcow.conf
echo 'SPAMHAUS_DQS_KEY=' >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_VERBOSE" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "WATCHDOG_VERBOSE" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Enable watchdog verbose logging' >> mailcow.conf
echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf
fi
elif [[ ${option} == "SKIP_UNBOUND_HEALTHCHECK" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "SKIP_UNBOUND_HEALTHCHECK" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n' >> mailcow.conf
echo 'SKIP_UNBOUND_HEALTHCHECK=n' >> mailcow.conf
fi
elif [[ ${option} == "DISABLE_NETFILTER_ISOLATION_RULE" ]]; then
if ! grep -q ${option} mailcow.conf; then
elif [[ "${option}" == "DISABLE_NETFILTER_ISOLATION_RULE" ]]; then
if ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf
echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf
fi
elif ! grep -q ${option} mailcow.conf; then
fi
elif ! grep -q "${option}" mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> mailcow.conf
fi
done
if [[( ${SKIP_PING_CHECK} == "y")]]; then
if [[ ("${SKIP_PING_CHECK}" == "y") ]]; then
echo -e "\e[32mSkipping Ping Check...\e[0m"
else
@@ -805,14 +821,14 @@ else
fi
fi
if ! [ $NEW_BRANCH ]; then
if ! [ "$NEW_BRANCH" ]; then
echo -e "\e[33mDetecting which build your mailcow runs on...\e[0m"
sleep 1
if [ ${BRANCH} == "master" ]; then
if [ "${BRANCH}" == "master" ]; then
echo -e "\e[32mYou are receiving stable updates (master).\e[0m"
echo -e "\e[33mTo change that run the update.sh Script one time with the --nightly parameter to switch to nightly builds.\e[0m"
elif [ ${BRANCH} == "nightly" ]; then
elif [ "${BRANCH}" == "nightly" ]; then
echo -e "\e[31mYou are receiving unstable updates (nightly). These are for testing purposes only!!!\e[0m"
sleep 1
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
@@ -823,12 +839,12 @@ if ! [ $NEW_BRANCH ]; then
echo -e "\e[33mThe mailcow stack might still work but it is recommended to switch to the master branch (stable builds).\e[0m"
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
fi
elif [ $FORCE ]; then
elif [ "$FORCE" ]; then
echo -e "\e[31mYou are running in forced mode!\e[0m"
echo -e "\e[31mA Branch Switch can only be performed manually (monitored).\e[0m"
echo -e "\e[31mPlease rerun the update.sh Script without the --force/-f parameter.\e[0m"
sleep 1
elif [ $NEW_BRANCH == "master" ] && [ $CURRENT_BRANCH != "master" ]; then
elif [ "$NEW_BRANCH" == "master" ] && [ "$CURRENT_BRANCH" != "master" ]; then
echo -e "\e[33mYou are about to switch your mailcow updates to the stable (master) branch.\e[0m"
sleep 1
echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no data is lost...\e[0m"
@@ -842,21 +858,21 @@ elif [ $NEW_BRANCH == "master" ] && [ $CURRENT_BRANCH != "master" ]; then
echo "OK. If you prepared yourself for that please run the update.sh Script with the --stable parameter again to trigger this process here."
exit 0
fi
BRANCH=$NEW_BRANCH
BRANCH="$NEW_BRANCH"
DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_upgrade_to_master_$(date +"%Y-%m-%d-%H-%M-%S")
mv diff_before_upgrade* ${DIFF_DIRECTORY}/ 2> /dev/null
DIFF_FILE="${DIFF_DIRECTORY}/diff_before_upgrade_to_master_$(date +"%Y-%m-%d-%H-%M-%S")"
mv diff_before_upgrade* "${DIFF_DIRECTORY}/" 2> /dev/null
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff ${BRANCH} --stat > ${DIFF_FILE}
git diff ${BRANCH} >> ${DIFF_FILE}
mkdir -p "${DIFF_DIRECTORY}"
git diff "${BRANCH}" --stat > "${DIFF_FILE}"
git diff "${BRANCH}" >> "${DIFF_FILE}"
fi
echo -e "\e[32mSwitching Branch to ${BRANCH}...\e[0m"
git fetch origin
git checkout -f ${BRANCH}
git checkout -f "${BRANCH}"
elif [ $NEW_BRANCH == "nightly" ] && [ $CURRENT_BRANCH != "nightly" ]; then
elif [ "$NEW_BRANCH" == "nightly" ] && [ "$CURRENT_BRANCH" != "nightly" ]; then
echo -e "\e[33mYou are about to switch your mailcow Updates to the unstable (nightly) branch.\e[0m"
sleep 1
echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no Data is lost...\e[0m"
@@ -874,27 +890,27 @@ elif [ $NEW_BRANCH == "nightly" ] && [ $CURRENT_BRANCH != "nightly" ]; then
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff ${BRANCH} --stat > ${DIFF_FILE}
git diff ${BRANCH} >> ${DIFF_FILE}
git diff "${BRANCH}" --stat > "${DIFF_FILE}"
git diff "${BRANCH}" >> "${DIFF_FILE}"
fi
git fetch origin
git checkout -f ${BRANCH}
git checkout -f "${BRANCH}"
fi
if [ ! $DEV ]; then
if [ ! "$DEV" ]; then
echo -e "\e[32mChecking for newer update script...\e[0m"
SHA1_1=$(sha1sum update.sh)
SHA1_1="$(sha1sum update.sh)"
git fetch origin #${BRANCH}
git checkout origin/${BRANCH} update.sh
git checkout "origin/${BRANCH}" update.sh
SHA1_2=$(sha1sum update.sh)
if [[ ${SHA1_1} != ${SHA1_2} ]]; then
if [[ "${SHA1_1}" != "${SHA1_2}" ]]; then
echo "update.sh changed, please run this script again, exiting."
chmod +x update.sh
exit 2
fi
fi
if [ ! $FORCE ]; then
if [ ! "$FORCE" ]; then
read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response
if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK, exiting."
@@ -916,8 +932,8 @@ fi
echo -e "\e[32mChecking for conflicting bridges...\e[0m"
MAILCOW_BRIDGE=$($COMPOSE_COMMAND config | grep -i com.docker.network.bridge.name | cut -d':' -f2)
while read NAT_ID; do
iptables -t nat -D POSTROUTING $NAT_ID
done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1)
iptables -t nat -D POSTROUTING "$NAT_ID"
done < <(iptables -L -vn -t nat --line-numbers | grep "$IPV4_NETWORK" | grep -E 'MASQUERADE.*all' | grep -v "${MAILCOW_BRIDGE}" | cut -d' ' -f1)
DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S")
@@ -925,8 +941,8 @@ mv diff_before_update* ${DIFF_DIRECTORY}/ 2> /dev/null
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff --stat > ${DIFF_FILE}
git diff >> ${DIFF_FILE}
git diff --stat > "${DIFF_FILE}"
git diff >> "${DIFF_FILE}"
fi
echo -e "\e[32mPrefetching images...\e[0m"
@@ -948,13 +964,13 @@ done
# Silently fixing remote url from andryyy to mailcow
# git remote set-url origin https://github.com/mailcow/mailcow-dockerized
DEFAULT_REPO=https://github.com/mailcow/mailcow-dockerized
DEFAULT_REPO="https://github.com/mailcow/mailcow-dockerized"
CURRENT_REPO=$(git config --get remote.origin.url)
if [ "$CURRENT_REPO" != "$DEFAULT_REPO" ]; then
if [ "$CURRENT_REPO" != "$DEFAULT_REPO" ]; then
echo "The Repository currently used is not the default Mailcow Repository."
echo "Currently Repository: $CURRENT_REPO"
echo "Default Repository: $DEFAULT_REPO"
if [ ! $FORCE ]; then
if [ ! "$FORCE" ]; then
read -r -p "Should it be changed back to default? [y/N] " repo_response
if [[ "$repo_response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
git remote set-url origin $DEFAULT_REPO
@@ -965,7 +981,7 @@ if [ "$CURRENT_REPO" != "$DEFAULT_REPO" ]; then
fi
fi
if [ ! $DEV ]; then
if [ ! "$DEV" ]; then
echo -e "\e[32mCommitting current status...\e[0m"
[[ -z "$(git config user.name)" ]] && git config user.name moo
[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
@@ -976,7 +992,7 @@ if [ ! $DEV ]; then
git fetch origin #${BRANCH}
echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
git config merge.defaultToUpstream true
git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
git merge -X"${MERGE_STRATEGY:-theirs}" -Xpatience -m "After update on ${DATE}"
# Need to use a variable to not pass return codes of if checks
MERGE_RETURN=$?
if [[ ${MERGE_RETURN} == 128 ]]; then
@@ -995,7 +1011,7 @@ if [ ! $DEV ]; then
echo "Run $COMPOSE_COMMAND up -d to restart your stack without updates or try again after fixing the mentioned errors."
exit 1
fi
elif [ $DEV ]; then
elif [ "$DEV" ]; then
echo -e "\e[33mDEVELOPER MODE: Not creating a git diff and commiting it to prevent development stuff within a backup diff...\e[0m"
fi
@@ -1042,7 +1058,7 @@ fi
# Set app_info.inc.php
if [ ${BRANCH} == "master" ]; then
mailcow_git_version=$(git describe --tags `git rev-list --tags --max-count=1`)
mailcow_git_version=$(git describe --tags $(git rev-list --tags --max-count=1))
elif [ ${BRANCH} == "nightly" ]; then
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
mailcow_last_git_version=""
@@ -1051,7 +1067,7 @@ else
mailcow_last_git_version=""
fi
mailcow_git_commit=$(git rev-parse origin/${BRANCH})
mailcow_git_commit=$(git rev-parse "origin/${BRANCH}")
mailcow_git_commit_date=$(git log -1 --format=%ci @{upstream} )
if [ $? -eq 0 ]; then