Compare commits

...

88 Commits

Author SHA1 Message Date
Niklas Meyer
2024cda560 Merge pull request #5578 from mailcow/staging
2023-11a
2023-12-07 12:52:32 +01:00
DerLinkman
03aaf4ad76 Update Rspamd Image to 1.94 2023-12-07 12:50:10 +01:00
DerLinkman
550b88861f [UI] Fixed showing of "disabled" placeholder for ratelimits in domains 2023-12-07 12:10:04 +01:00
Niklas Meyer
02ae5fa007 Merge pull request #5577 from mailcow/fix/rspamd-ratelimiting
[Rspamd] Fixed Ratelimit forced by global ratelimits
2023-12-07 12:07:58 +01:00
DerLinkman
d81f105ed7 [Rspamd] Added customizable global ratelimit file (disabled by default) 2023-12-07 12:04:45 +01:00
DerLinkman
d3ed225675 [Rspamd] Removed global ratelimit override 2023-12-07 12:04:06 +01:00
Niklas Meyer
9ffc83f0f6 Merge pull request #5570 from mailcow/update/postscreen_access.cidr
[Postfix] update postscreen_access.cidr
2023-12-04 10:50:23 +01:00
milkmaker
981c7d5974 [Web] Updated lang.pt-br.json (#5573)
Co-authored-by: Abner Santana <abnerss@outlook.com>
2023-12-02 15:22:45 +01:00
milkmaker
5da089ccd7 update postscreen_access.cidr 2023-12-01 00:15:24 +00:00
milkmaker
91e00f7d97 Translations update from Weblate (#5569)
* [Web] Updated lang.ru-ru.json

Co-authored-by: Oleksii Kruhlenko <a.kruglenko@gmail.com>

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

Co-authored-by: Oleksii Kruhlenko <a.kruglenko@gmail.com>

---------

Co-authored-by: Oleksii Kruhlenko <a.kruglenko@gmail.com>
2023-11-30 21:14:42 +01:00
milkmaker
3a675fb541 [Web] Updated lang.fi-fi.json (#5567)
Co-authored-by: Mika Ruohomäki <mika.ruohomaki@ix1.fi>
2023-11-28 21:00:59 +01:00
Niklas Meyer
9a5d8d2d22 Merge pull request #5562 from startnow65/master
Detect docker compose version of form v2.x
2023-11-28 08:30:35 +01:00
DerLinkman
de812221ef Implemented improved check in update.sh as well. 2023-11-28 08:29:54 +01:00
Patrick Schult
f68a28fa2b Merge pull request #5555 from mailcow/feat/custom-footer-vars
[Web][Rspamd] domain wide footer improvements and custom mailbox attributes
2023-11-27 17:06:06 +01:00
FreddleSpl0it
7b7798e8c4 [Web] check if mbox exists before excluding it from domain wide footer 2023-11-27 17:04:29 +01:00
FreddleSpl0it
b3ac94115e [Rspamd] fix excluding alias from domain wide footer 2023-11-27 16:20:44 +01:00
DerLinkman
b1a172cad9 Use full mastodon name instead 2023-11-27 14:35:09 +01:00
DerLinkman
f2e21c68d0 Add Mastodon Links 2023-11-27 14:34:56 +01:00
DerLinkman
8b784c0eb1 Use full mastodon name instead 2023-11-27 14:34:15 +01:00
DerLinkman
bc59f32b96 Add Mastodon Links 2023-11-27 14:32:51 +01:00
Josiah Adenegan
a4fa8a4fae Detect docker compose version of form v2.x 2023-11-25 20:36:40 +00:00
Niklas Meyer
f730192c98 Merge pull request #5559 from mailcow/renovate/nextcloud-server-27.x
Update dependency nextcloud/server to v27.1.4
2023-11-24 11:16:00 +01:00
Patrick Schult
f994501296 Merge pull request #5482 from mailcow/feat/get-spam-score
[Web] add /api/v1/get/spam-score endpoint
2023-11-24 09:39:43 +01:00
renovate[bot]
9c3e73606c Update dependency nextcloud/server to v27.1.4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-23 21:31:24 +00:00
milkmaker
5619e16b70 [Web] Updated lang.cs-cz.json (#5557)
Co-authored-by: Peter <magic@kthx.at>
2023-11-23 19:12:11 +01:00
FreddleSpl0it
d2e3867893 [Web][Rspamd] implement custom mailbox attributes and improve domain wide footer 2023-11-23 16:12:43 +01:00
Niklas Meyer
979f5475c3 Merge pull request #5552 from mailcow/staging
[Update.sh] Fix repo change when running in forced mode
2023-11-21 15:42:25 +01:00
DerLinkman
5a10f2dd7c Fix repo change when running in forced mode 2023-11-21 15:37:53 +01:00
Niklas Meyer
a80b5b7dd0 Merge pull request #5551 from mailcow/staging
2023-11
2023-11-21 10:39:05 +01:00
FreddleSpl0it
392967d664 [Rspamd] domain wide footer check for empty strings 2023-11-21 10:19:00 +01:00
Niklas Meyer
3b3c2b7141 Merge pull request #5546 from mailcow/fix/domain-wide-footer
Fix: Domain Wide Disclaimer breaks attachments visualization on Gmail and Outlook #5529
2023-11-21 10:01:38 +01:00
Niklas Meyer
f55c3c0887 Merge pull request #5548 from mailcow/fix-5547
[Web] escape quarantine html
2023-11-21 10:01:04 +01:00
FreddleSpl0it
f423ad77f3 [Web] escape quarantine html 2023-11-21 08:49:18 +01:00
FreddleSpl0it
8ba1e1ba9e [Rspamd] workaround - remove "--\x0D\x0A" prefix from rewritten cts 2023-11-20 12:38:37 +01:00
Niklas Meyer
55576084fc Merge pull request #5544 from mailcow/feat/update-renovate 2023-11-18 12:33:12 +01:00
Peter
03311b06c9 Ignore everything in vendor subdirs 2023-11-18 11:40:57 +01:00
milkmaker
b5c3d01834 Translations update from Weblate (#5538)
* [Web] Updated lang.cs-cz.json

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

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

Co-authored-by: Quiwy <github@quiwy.ninja>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Quiwy <github@quiwy.ninja>
2023-11-16 17:07:33 +01:00
Niklas Meyer
f398ecbe39 Merge pull request #5487 from artemislena/master
Add a helper script for generating CAA records
2023-11-16 11:42:11 +01:00
Niklas Meyer
8f1ae0f099 Merge pull request #5530 from Quiwy/staging
fix: support utf-8 in password synchronization
2023-11-16 11:21:27 +01:00
Niklas Meyer
c8bee57732 Merge pull request #5521 from raph-topo/fix/impasync-options
Add `--dry` IMAPsync Parameter as Button to select for SyncJobs
2023-11-16 11:19:47 +01:00
DerLinkman
85641794c3 Added f1f2 + sorted whitelist for imapsync 2023-11-16 11:18:50 +01:00
Niklas Meyer
849decaa59 Merge pull request #5532 from mailcow/renovate/actions-cache-3.x
Update actions/cache action to v3
2023-11-16 10:46:28 +01:00
Niklas Meyer
6e88550f92 Merge pull request #5533 from mailcow/renovate/actions-checkout-4.x
Update actions/checkout action to v4
2023-11-16 10:46:03 +01:00
renovate[bot]
7c52483887 Update actions/checkout action to v4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-16 09:29:22 +00:00
renovate[bot]
0aa520c030 Update actions/cache action to v3
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-16 09:29:15 +00:00
Niklas Meyer
548999f163 Merge pull request #5498 from mailcow:feat/fix-5497
Update nextcloud.conf when updating nextcloud
2023-11-16 10:28:54 +01:00
DerLinkman
63df547306 Tweaked German Translation 2023-11-15 16:45:27 +01:00
DerLinkman
547d2ca308 Add Dry Mode Option for ImapSyncs (Button) 2023-11-15 16:18:18 +01:00
Quiwy
46b995f9e3 fix: support utf-8 in password synchronization 2023-11-14 10:11:25 +01:00
Niklas Meyer
1fdf704cb4 Merge pull request #5524 from mailcow/feat/fix-renovate 2023-11-12 18:28:42 +01:00
Peter
5ec9c4c750 Fix renovate regex 2023-11-12 18:00:20 +01:00
Raphael
afed94cc0e Allow --dry IMAPsync 2023-11-09 15:24:16 +01:00
Niklas Meyer
6f48c5ace0 Merge pull request #5513 from mailcow/feat/new-sieve-template
[UI] Added a new Sieve Rule as Template
2023-11-02 17:17:19 +01:00
DerLinkman
9a7e1c2b5a Added new Sieve Template. Thanks to @EricThi 2023-11-02 17:15:10 +01:00
Niklas Meyer
2ef7539d55 Merge pull request #5509 from mailcow/update/postscreen_access.cidr
[Postfix] update postscreen_access.cidr
2023-11-02 17:08:04 +01:00
Burak Buylu
4e52542e33 Update lang.tr-tr.json (#5510)
Every day I will translate :)
2023-11-01 09:26:05 +01:00
milkmaker
a1895ad924 update postscreen_access.cidr 2023-11-01 00:14:31 +00:00
Niklas Meyer
d5a2c96887 Merge pull request #5459 from SecT0uch/patch-1 2023-10-30 21:55:58 +01:00
Niklas Meyer
3f30fe3113 Merge pull request #5508 from BandhiyaHardik/staging 2023-10-30 21:54:29 +01:00
HardikBandhiya
d89f24a1a3 Merge branch 'mailcow:staging' into staging 2023-10-31 02:18:14 +05:30
HardikBandhiya
413354ff29 Update README.md
changed the name of Twitter to 𝕏
2023-10-31 02:07:46 +05:30
FreddleSpl0it
a28ba5bebb [Web] fix broken github links in changelog 2023-10-30 16:07:10 +01:00
milkmaker
b93375b671 [Web] Updated lang.hu-hu.json (#5505)
Co-authored-by: Bence Kócsi <ttcrafttt@gmail.com>
2023-10-30 12:05:10 +01:00
Kristian Feldsam
b568a33581 [web] sk and cz translations (#5502)
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
2023-10-30 10:09:22 +01:00
Niklas Meyer
b05ef8edac Merge pull request #5500 from mailcow/renovate/nextcloud-server-27.x 2023-10-28 20:37:13 +02:00
renovate[bot]
015f9b663f Update dependency nextcloud/server to v27.1.3
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-10-28 14:40:08 +00:00
Niklas Meyer
b6167257c9 Merge pull request #5455 from mailcow/feat/rspamd-3.7.1 2023-10-28 16:39:53 +02:00
milkmaker
687fe044b2 [Web] Updated lang.si-si.json (#5499)
Co-authored-by: gomiunik <boris@gomiunik.net>
2023-10-28 15:10:30 +02:00
Peter
cfa47eb873 Update nextcloud.conf 2023-10-27 22:59:46 +02:00
Peter
7079000ee0 Update nextcloud.conf when updating nextcloud 2023-10-27 22:56:51 +02:00
milkmaker
f60c4f39ee [Web] Updated lang.si-si.json (#5494)
Co-authored-by: gomiunik <boris@gomiunik.net>
2023-10-25 19:46:19 +02:00
yvan-algoo
473713219f Update lang.fr-fr.json (#5492)
- Fix typos
- Replace "..." by "…"
2023-10-25 18:38:01 +02:00
artemislena
03ed81dc3f T.: Added a script for generating CAA records 2023-10-23 19:44:28 +02:00
renovate[bot]
53543ccf26 Update thollander/actions-comment-pull-request action to v2.4.3 (#5484)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-21 12:10:34 +02:00
FreddleSpl0it
3b183933e3 [Web] add api get spam-score endpoint 2023-10-20 10:48:04 +02:00
DerLinkman
6c6fde8e2e Improved docker image pruning 2023-10-19 12:31:13 +02:00
DerLinkman
61e23b6b81 Added Dev Mode option for git diff creation 2023-10-19 12:14:27 +02:00
DerLinkman
6c649debc9 Update DockerAPI to implement CPU load fix 2023-10-18 10:31:49 +02:00
milkmaker
87b0683f77 Translations update from Weblate (#5472)
* [Web] Updated lang.cs-cz.json

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

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

Co-authored-by: Peter <magic@kthx.at>

---------

Co-authored-by: Peter <magic@kthx.at>
2023-10-14 22:58:28 +02:00
milkmaker
59c1e7a18a [Web] Updated lang.pt-br.json (#5471)
Co-authored-by: Peter <magic@kthx.at>
2023-10-14 14:26:06 +02:00
Pedro Lucca S.C
4f9dad5dd3 pt-br translation (#5470) 2023-10-14 14:16:07 +02:00
DerLinkman
adc6a0054c Updated compose version info color from red to yellow 2023-10-13 15:37:37 +02:00
milkmaker
8a70cdb48b Translations update from Weblate (#5460)
* [Web] Added lang.pt-br.json

Co-authored-by: Peter <magic@kthx.at>

* Add pt-br in vars.inc.php

---------

Co-authored-by: Peter <magic@kthx.at>
2023-10-12 18:27:04 +02:00
Jordan ERNST
bb4bc11383 Fix for git < v1.7.5
This change should be compatible with all git version.
(get-url available from v1.7.5)
2023-10-12 15:55:53 +02:00
Niklas Meyer
a366494c34 Merge pull request #5458 from mailcow/staging
2023-10a
2023-10-12 15:45:40 +02:00
DerLinkman
99de302ec9 Reverted restart action removal in docker-compose.yml for older 2.X compatibility 2023-10-12 15:38:58 +02:00
DerLinkman
907912046f Fix Clamd Version image in compose 2023-10-12 15:18:19 +02:00
DerLinkman
2c0d379dc5 [Rspamd] Update to 3.7.1 2023-10-12 13:05:27 +02:00
55 changed files with 2724 additions and 411 deletions

View File

@@ -12,7 +12,7 @@
"baseBranches": ["staging"],
"enabledManagers": ["github-actions", "regex", "docker-compose"],
"ignorePaths": [
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**"
"data\/web\/inc\/lib\/vendor\/**"
],
"regexManagers": [
{

View File

@@ -10,7 +10,7 @@ jobs:
if: github.event.pull_request.base.ref != 'staging' #check if the target branch is not staging
steps:
- name: Send message
uses: thollander/actions-comment-pull-request@v2.4.2
uses: thollander/actions-comment-pull-request@v2.4.3
with:
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
message: |

View File

@@ -2,6 +2,8 @@
[![Translation status](https://translate.mailcow.email/widgets/mailcow-dockerized/-/translation/svg-badge.svg)](https://translate.mailcow.email/engage/mailcow-dockerized/)
[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/mailcow_email.svg?style=social&label=Follow%20%40mailcow_email)](https://twitter.com/mailcow_email)
![Mastodon Follow](https://img.shields.io/mastodon/follow/109388212176073348?domain=https%3A%2F%2Fmailcow.social&label=Follow%20%40doncow%40mailcow.social&link=https%3A%2F%2Fmailcow.social%2F%40doncow)
## Want to support mailcow?
@@ -25,7 +27,9 @@ Please see [the official documentation](https://docs.mailcow.email/) for install
[Telegram mailcow Off-Topic channel](https://t.me/mailcowOfftopic)
[Official Twitter Account](https://twitter.com/mailcow_email)
[Official 𝕏 (Twitter) Account](https://twitter.com/mailcow_email)
[Official Mastodon Account](https://mailcow.social/@doncow)
Telegram desktop clients are available for [multiple platforms](https://desktop.telegram.org). You can search the groups history for keywords.
@@ -38,4 +42,4 @@ mailcow is a registered word mark of The Infrastructure Company GmbH, Parkstr. 4
The project is managed and maintained by The Infrastructure Company GmbH.
Originated from @andryyy (André)
Originated from @andryyy (André)

View File

@@ -1,6 +1,6 @@
FROM clamav/clamav:1.0.3_base
LABEL maintainer "André Peters <andre.peters@servercow.de>"
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \

View File

@@ -2,9 +2,9 @@ FROM debian:bullseye-slim
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=(?<version>.*)$
ARG DOVECOT=2.3.21
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
ARG GOSU_VERSION=1.16
ENV LC_ALL C

View File

@@ -75,7 +75,8 @@ my $sth = $dbh->prepare("SELECT id,
custom_params,
subscribeall,
timeout1,
timeout2
timeout2,
dry
FROM imapsync
WHERE active = 1
AND is_running = 0
@@ -111,13 +112,16 @@ while ($row = $sth->fetchrow_arrayref()) {
$subscribeall = @$row[18];
$timeout1 = @$row[19];
$timeout2 = @$row[20];
$dry = @$row[21];
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
my $template = $run_dir . '/imapsync.XXXXXXX';
my $passfile1 = File::Temp->new(TEMPLATE => $template);
my $passfile2 = File::Temp->new(TEMPLATE => $template);
binmode( $passfile1, ":utf8" );
print $passfile1 "$password1\n";
print $passfile2 trim($master_pass) . "\n";
@@ -148,6 +152,7 @@ while ($row = $sth->fetchrow_arrayref()) {
"--host2", "localhost",
"--user2", $user2 . '*' . trim($master_user),
"--passfile2", $passfile2->filename,
($dry eq "1" ? ('--dry') : ()),
'--no-modulesversion',
'--noreleasecheck'];

View File

@@ -3,15 +3,15 @@ LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG APCU_PECL_VERSION=5.1.22
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.7.0
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.1.6
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.0.1
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.6.5
RUN apk add -U --no-cache autoconf \

View File

@@ -79,6 +79,9 @@ EOF
redis-cli -h redis-mailcow SLAVEOF NO ONE
fi
# Provide additional lua modules
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so
chown -R _rspamd:_rspamd /var/lib/rspamd \
/etc/rspamd/local.d \
/etc/rspamd/override.d \

View File

@@ -3,7 +3,7 @@ LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.16
ENV LC_ALL C

View File

@@ -2,7 +2,7 @@ FROM solr:7.7-slim
USER root
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
ARG GOSU_VERSION=1.16
COPY solr.sh /

View File

@@ -86,7 +86,7 @@ server {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
@@ -105,7 +105,7 @@ server {
fastcgi_read_timeout 1200;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Sun Oct 1 00:14:59 UTC 2023
# Whitelist generated by Postwhite v3.4 on Fri Dec 1 00:15:18 UTC 2023
# https://github.com/stevejenkins/postwhite/
# 2019 total rules
# 2038 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit
@@ -10,10 +10,10 @@
2a02:a60:0:5::/64 permit
2c0f:fb50:4000::/36 permit
2.207.151.53 permit
3.14.230.16 permit
3.70.123.177 permit
3.93.157.0/24 permit
3.129.120.190 permit
3.137.78.75 permit
3.210.190.0/24 permit
8.20.114.31 permit
8.25.194.0/23 permit
@@ -183,8 +183,6 @@
50.18.125.237 permit
50.18.126.162 permit
50.31.32.0/19 permit
50.31.156.96/27 permit
50.31.205.0/24 permit
51.137.58.21 permit
51.140.75.55 permit
51.144.100.179 permit
@@ -211,6 +209,7 @@
52.96.222.194 permit
52.96.222.226 permit
52.96.223.2 permit
52.96.228.130 permit
52.96.229.242 permit
52.100.0.0/14 permit
52.103.0.0/17 permit
@@ -303,22 +302,31 @@
64.147.123.27 permit
64.147.123.28 permit
64.147.123.29 permit
64.147.123.128/27 permit
64.207.219.7 permit
64.207.219.8 permit
64.207.219.9 permit
64.207.219.10 permit
64.207.219.11 permit
64.207.219.12 permit
64.207.219.13 permit
64.207.219.14 permit
64.207.219.15 permit
64.207.219.71 permit
64.207.219.72 permit
64.207.219.73 permit
64.207.219.74 permit
64.207.219.75 permit
64.207.219.76 permit
64.207.219.77 permit
64.207.219.78 permit
64.207.219.79 permit
64.207.219.135 permit
64.207.219.136 permit
64.207.219.137 permit
64.207.219.138 permit
64.207.219.139 permit
64.207.219.140 permit
64.207.219.141 permit
64.207.219.142 permit
64.207.219.143 permit
@@ -396,7 +404,6 @@
66.196.81.232/31 permit
66.196.81.234 permit
66.211.168.230/31 permit
66.211.170.86/31 permit
66.211.170.88/29 permit
66.211.184.0/23 permit
66.218.74.64/30 permit
@@ -620,7 +627,9 @@
82.165.229.130 permit
82.165.230.21 permit
82.165.230.22 permit
84.116.6.0/23 permit
84.116.36.0/24 permit
84.116.50.0/23 permit
85.158.136.0/21 permit
86.61.88.25 permit
87.198.219.130 permit
@@ -1192,7 +1201,6 @@
104.130.96.0/28 permit
104.130.122.0/23 permit
104.214.25.77 permit
104.245.209.192/26 permit
106.10.144.64/27 permit
106.10.144.100/31 permit
106.10.144.103 permit
@@ -1369,6 +1377,8 @@
128.245.0.0/20 permit
128.245.64.0/20 permit
128.245.176.0/20 permit
128.245.240.0/24 permit
128.245.241.0/24 permit
128.245.242.0/24 permit
128.245.242.16 permit
128.245.242.17 permit
@@ -1378,6 +1388,7 @@
128.245.245.0/24 permit
128.245.246.0/24 permit
128.245.247.0/24 permit
128.245.248.0/21 permit
129.41.77.70 permit
129.41.169.249 permit
129.80.5.164 permit
@@ -1418,6 +1429,7 @@
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
@@ -1454,7 +1466,6 @@
146.20.215.0/24 permit
146.20.215.182 permit
146.88.28.0/24 permit
147.160.158.0/24 permit
147.243.1.47 permit
147.243.1.48 permit
147.243.1.153 permit
@@ -1492,6 +1503,8 @@
158.101.211.207 permit
158.120.80.0/21 permit
158.247.16.0/20 permit
159.92.154.0/24 permit
159.92.155.0/24 permit
159.92.157.0/24 permit
159.92.157.16 permit
159.92.157.17 permit
@@ -1501,6 +1514,9 @@
159.92.160.0/24 permit
159.92.161.0/24 permit
159.92.162.0/24 permit
159.92.163.0/24 permit
159.92.164.0/22 permit
159.92.168.0/21 permit
159.112.240.0/20 permit
159.112.242.162 permit
159.135.132.128/25 permit
@@ -1549,6 +1565,8 @@
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
170.10.68.0/22 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
@@ -1700,13 +1718,14 @@
198.244.60.0/22 permit
198.245.80.0/20 permit
198.245.81.0/24 permit
199.15.176.173 permit
199.15.213.187 permit
199.15.226.37 permit
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.84.0/24 permit
199.67.86.0/24 permit
199.67.88.0/24 permit

View File

@@ -0,0 +1,91 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
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) {
error_log("FOOTER: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
// Read headers
$headers = getallheaders();
// Get Domain
$domain = $headers['Domain'];
// Get Username
$username = $headers['Username'];
// Get From
$from = $headers['From'];
// define empty footer
$empty_footer = json_encode(array(
'html' => '',
'plain' => '',
'vars' => array()
));
error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL);
try {
$stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude` FROM `domain_wide_footer`
WHERE `domain` = :domain");
$stmt->execute(array(
':domain' => $domain
));
$footer = $stmt->fetch(PDO::FETCH_ASSOC);
if (in_array($from, json_decode($footer['mbox_exclude']))){
$footer = false;
}
if (empty($footer)){
echo $empty_footer;
exit;
}
error_log("FOOTER: " . json_encode($footer) . PHP_EOL);
$stmt = $pdo->prepare("SELECT `custom_attributes` FROM `mailbox` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username
));
$custom_attributes = $stmt->fetch(PDO::FETCH_ASSOC)['custom_attributes'];
if (empty($custom_attributes)){
$custom_attributes = (object)array();
}
}
catch (Exception $e) {
error_log("FOOTER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
// return footer
$footer["vars"] = $custom_attributes;
echo json_encode($footer);

View File

@@ -0,0 +1,9 @@
# Uncomment below to apply the ratelimits globally. Use Ratelimits inside mailcow UI to overwrite them for a specific domain/mailbox.
# rates {
# # Format: "1 / 1h" or "20 / 1m" etc.
# to = "100 / 1s";
# to_ip = "100 / 1s";
# to_ip_from = "100 / 1s";
# bounce_to = "100 / 1h";
# bounce_to_ip = "7 / 1m";
# }

View File

@@ -527,20 +527,21 @@ rspamd_config:register_symbol({
name = 'MOO_FOOTER',
type = 'prefilter',
callback = function(task)
local cjson = require "cjson"
local lua_mime = require "lua_mime"
local lua_util = require "lua_util"
local rspamd_logger = require "rspamd_logger"
local rspamd_redis = require "rspamd_redis"
local ucl = require "ucl"
local redis_params = rspamd_parse_redis_server('footer')
local rspamd_http = require "rspamd_http"
local envfrom = task:get_from(1)
local uname = task:get_user()
if not envfrom or not uname then
return false
end
local uname = uname:lower()
local env_from_domain = envfrom[1].domain:lower() -- get smtp from domain in lower case
local env_from_domain = envfrom[1].domain:lower()
local env_from_addr = envfrom[1].addr:lower()
-- determine newline type
local function newline(task)
local t = task:get_newlines_type()
@@ -552,20 +553,19 @@ rspamd_config:register_symbol({
return '\r\n'
end
local function redis_cb_footer(err, data)
-- retrieve footer
local function footer_cb(err_message, code, data, headers)
if err or type(data) ~= 'string' then
rspamd_logger.infox(rspamd_config, "domain wide footer request for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
else
-- parse json string
local parser = ucl.parser()
local res,err = parser:parse_string(data)
if not res then
local footer = cjson.decode(data)
if not footer then
rspamd_logger.infox(rspamd_config, "parsing domain wide footer for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
else
local footer = parser:get_object()
if footer and type(footer) == "table" and (footer.html or footer.plain) then
rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s", uname, footer.html, footer.plain)
if footer and type(footer) == "table" and (footer.html and footer.html ~= "" or footer.plain and footer.plain ~= "") then
rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s, vars=%s", uname, footer.html, footer.plain, footer.vars)
local envfrom_mime = task:get_from(2)
local from_name = ""
@@ -575,6 +575,7 @@ rspamd_config:register_symbol({
from_name = envfrom[1].name
end
-- default replacements
local replacements = {
auth_user = uname,
from_user = envfrom[1].user,
@@ -582,10 +583,20 @@ rspamd_config:register_symbol({
from_addr = envfrom[1].addr,
from_domain = envfrom[1].domain:lower()
}
if footer.html then
-- add custom mailbox attributes
if footer.vars and type(footer.vars) == "string" then
local footer_vars = cjson.decode(footer.vars)
if type(footer_vars) == "table" then
for key, value in pairs(footer_vars) do
replacements[key] = value
end
end
end
if footer.html and footer.html ~= "" then
footer.html = lua_util.jinja_template(footer.html, replacements, true)
end
if footer.plain then
if footer.plain and footer.plain ~= "" then
footer.plain = lua_util.jinja_template(footer.plain, replacements, true)
end
@@ -631,15 +642,19 @@ rspamd_config:register_symbol({
end
local out_parts = {}
for _,o in ipairs(out) do
if type(o) ~= 'table' then
out_parts[#out_parts + 1] = o
out_parts[#out_parts + 1] = newline_s
else
out_parts[#out_parts + 1] = o[1]
if o[2] then
out_parts[#out_parts + 1] = newline_s
end
end
if type(o) ~= 'table' then
out_parts[#out_parts + 1] = o
out_parts[#out_parts + 1] = newline_s
else
local removePrefix = "--\x0D\x0AContent-Type"
if string.lower(string.sub(tostring(o[1]), 1, string.len(removePrefix))) == string.lower(removePrefix) then
o[1] = string.sub(tostring(o[1]), string.len("--\x0D\x0A") + 1)
end
out_parts[#out_parts + 1] = o[1]
if o[2] then
out_parts[#out_parts + 1] = newline_s
end
end
end
task:set_message(out_parts)
else
@@ -649,17 +664,14 @@ rspamd_config:register_symbol({
end
end
local redis_ret_footer = rspamd_redis_make_request(task,
redis_params, -- connect params
env_from_domain, -- hash key
false, -- is write
redis_cb_footer, --callback
'HGET', -- command
{"DOMAIN_WIDE_FOOTER", env_from_domain} -- arguments
)
if not redis_ret_footer then
rspamd_logger.infox(rspamd_config, "cannot make request to load footer for domain")
end
-- fetch footer
rspamd_http.request({
task=task,
url='http://nginx:8081/footer.php',
body='',
callback=footer_cb,
headers={Domain=env_from_domain,Username=uname,From=env_from_addr},
})
return true
end,

View File

@@ -1,11 +1,3 @@
rates {
# Format: "1 / 1h" or "20 / 1m" etc. - global ratelimits are disabled by default
to = "100 / 1s";
to_ip = "100 / 1s";
to_ip_from = "100 / 1s";
bounce_to = "100 / 1h";
bounce_to_ip = "7 / 1m";
}
whitelisted_rcpts = "postmaster,mailer-daemon";
max_rcpt = 25;
custom_keywords = "/etc/rspamd/lua/ratelimit.lua";

View File

@@ -3137,6 +3137,86 @@ paths:
type: string
type: object
summary: Update domain
/api/v1/edit/domain/footer:
post:
responses:
"401":
$ref: "#/components/responses/Unauthorized"
"200":
content:
application/json:
examples:
response:
value:
- log:
- mailbox
- edit
- domain_wide_footer
- domains:
- mailcow.tld
html: "<br>foo {= foo =}"
plain: "<foo {= foo =}"
mbox_exclude:
- moo@mailcow.tld
- null
msg:
- domain_footer_modified
- mailcow.tld
type: success
schema:
properties:
log:
description: contains request object
items: {}
type: array
msg:
items: {}
type: array
type:
enum:
- success
- danger
- error
type: string
type: object
description: OK
headers: {}
tags:
- Domains
description: >-
You can update the footer of one or more domains per request.
operationId: Update domain wide footer
requestBody:
content:
application/json:
schema:
example:
attr:
html: "<br>foo {= foo =}"
plain: "foo {= foo =}"
mbox_exclude:
- moo@mailcow.tld
items: mailcow.tld
properties:
attr:
properties:
html:
description: Footer text in HTML format
type: string
plain:
description: Footer text in PLAIN text format
type: string
mbox_exclude:
description: Array of mailboxes to exclude from domain wide footer
type: object
type: object
items:
description: contains a list of domain names where you want to update the footer
type: array
items:
type: string
type: object
summary: Update domain wide footer
/api/v1/edit/fail2ban:
post:
responses:
@@ -3336,6 +3416,86 @@ paths:
type: object
type: object
summary: Update mailbox
/api/v1/edit/mailbox/custom-attribute:
post:
responses:
"401":
$ref: "#/components/responses/Unauthorized"
"200":
content:
application/json:
examples:
response:
value:
- log:
- mailbox
- edit
- mailbox_custom_attribute
- mailboxes:
- moo@mailcow.tld
attribute:
- role
- foo
value:
- cow
- bar
- null
msg:
- mailbox_modified
- moo@mailcow.tld
type: success
schema:
properties:
log:
description: contains request object
items: {}
type: array
msg:
items: {}
type: array
type:
enum:
- success
- danger
- error
type: string
type: object
description: OK
headers: {}
tags:
- Mailboxes
description: >-
You can update custom attributes of one or more mailboxes per request.
operationId: Update mailbox custom attributes
requestBody:
content:
application/json:
schema:
example:
attr:
attribute:
- role
- foo
value:
- cow
- bar
items:
- moo@mailcow.tld
properties:
attr:
properties:
attribute:
description: Array of attribute keys
type: object
value:
description: Array of attribute values
type: object
type: object
items:
description: contains list of mailboxes you want update
type: object
type: object
summary: Update mailbox custom attributes
/api/v1/edit/mailq:
post:
responses:
@@ -5581,6 +5741,7 @@ paths:
sogo_access: "1"
tls_enforce_in: "0"
tls_enforce_out: "0"
custom_attributes: {}
domain: domain3.tld
is_relayed: 0
local_part: info
@@ -5646,6 +5807,40 @@ paths:
items:
type: string
summary: Edit Cross-Origin Resource Sharing (CORS) settings
"/api/v1/get/spam-score/{mailbox}":
get:
parameters:
- description: name of mailbox or empty for current user - admin user will retrieve the global spam filter score
in: path
name: mailbox
required: true
schema:
type: string
- description: e.g. api-key-string
example: api-key-string
in: header
name: X-API-Key
required: false
schema:
type: string
responses:
"401":
$ref: "#/components/responses/Unauthorized"
"200":
content:
application/json:
examples:
response:
value:
spam_score: "8,15"
description: OK
headers: {}
tags:
- Mailboxes
description: >-
Using this endpoint you can get the global spam filter score or the spam filter score of a certain mailbox.
operationId: Get mailbox or global spam filter score
summary: Get mailbox or global spam filter score
tags:
- name: Domains

View File

@@ -58,6 +58,8 @@ if (isset($_SESSION['mailcow_cc_role'])) {
'dkim' => dkim('details', $domain),
'domain_details' => $result,
'domain_footer' => $domain_footer,
'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address')
];
}
}
@@ -218,6 +220,7 @@ $js_minifier->add('/web/js/site/pwgen.js');
$template_data['result'] = $result;
$template_data['return_to'] = $_SESSION['return_to'];
$template_data['lang_user'] = json_encode($lang['user']);
$template_data['lang_admin'] = json_encode($lang['admin']);
$template_data['lang_datatables'] = json_encode($lang['datatables']);
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';

View File

@@ -325,6 +325,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$timeout2 = intval($_data['timeout2']);
$skipcrossduplicates = intval($_data['skipcrossduplicates']);
$automap = intval($_data['automap']);
$dry = intval($_data['dry']);
$port1 = $_data['port1'];
$host1 = strtolower($_data['host1']);
$password1 = $_data['password1'];
@@ -435,8 +436,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `dry`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :dry, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
$stmt->execute(array(
':user2' => $username,
':custom_params' => $custom_params,
@@ -450,6 +451,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':skipcrossduplicates' => $skipcrossduplicates,
':maxbytespersecond' => $maxbytespersecond,
':subscribeall' => $subscribeall,
':dry' => $dry,
':subfolder2' => $subfolder2,
':host1' => $host1,
':authmech1' => 'PLAIN',
@@ -2031,6 +2033,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$success = (isset($_data['success'])) ? NULL : $is_now['success'];
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
$subscribeall = (isset($_data['subscribeall'])) ? intval($_data['subscribeall']) : $is_now['subscribeall'];
$dry = (isset($_data['dry'])) ? intval($_data['dry']) : $is_now['dry'];
$delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
$automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap'];
@@ -2164,6 +2167,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`timeout1` = :timeout1,
`timeout2` = :timeout2,
`subscribeall` = :subscribeall,
`dry` = :dry,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
@@ -2189,6 +2193,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':timeout1' => $timeout1,
':timeout2' => $timeout2,
':subscribeall' => $subscribeall,
':dry' => $dry,
':active' => $active,
));
$_SESSION['return'][] = array(
@@ -3259,6 +3264,62 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return true;
break;
case 'mailbox_custom_attribute':
$_data['attribute'] = isset($_data['attribute']) ? $_data['attribute'] : array();
$_data['attribute'] = is_array($_data['attribute']) ? $_data['attribute'] : array($_data['attribute']);
$_data['attribute'] = array_map(function($value) { return str_replace(' ', '', $value); }, $_data['attribute']);
$_data['value'] = isset($_data['value']) ? $_data['value'] : array();
$_data['value'] = is_array($_data['value']) ? $_data['value'] : array($_data['value']);
$attributes = (object)array_combine($_data['attribute'], $_data['value']);
$mailboxes = is_array($_data['mailboxes']) ? $_data['mailboxes'] : array($_data['mailboxes']);
foreach ($mailboxes as $mailbox) {
if (!filter_var($mailbox, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('username_invalid', $mailbox)
);
continue;
}
$is_now = mailbox('get', 'mailbox_details', $mailbox);
if(!empty($is_now)){
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $is_now['domain'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("UPDATE `mailbox`
SET `custom_attributes` = :custom_attributes
WHERE username = :username");
$stmt->execute(array(
":username" => $mailbox,
":custom_attributes" => json_encode($attributes)
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('mailbox_modified', $mailbox)
);
}
return true;
break;
case 'resource':
if (!is_array($_data['name'])) {
$names = array();
@@ -3338,44 +3399,89 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
}
break;
case 'domain_wide_footer':
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'domain_invalid'
);
return false;
case 'domain_wide_footer':
if (!is_array($_data['domains'])) {
$domains = array();
$domains[] = $_data['domains'];
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
else {
$domains = $_data['domains'];
}
$footers = array();
$footers['html'] = isset($_data['footer_html']) ? $_data['footer_html'] : '';
$footers['plain'] = isset($_data['footer_plain']) ? $_data['footer_plain'] : '';
try {
$redis->hSet('DOMAIN_WIDE_FOOTER', $domain, json_encode($footers));
$footers['html'] = isset($_data['html']) ? $_data['html'] : '';
$footers['plain'] = isset($_data['plain']) ? $_data['plain'] : '';
$footers['mbox_exclude'] = array();
if (isset($_data["mbox_exclude"])){
if (!is_array($_data["mbox_exclude"])) {
$_data["mbox_exclude"] = array($_data["mbox_exclude"]);
}
foreach ($_data["mbox_exclude"] as $mailbox) {
if (!filter_var($mailbox, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('username_invalid', $mailbox)
);
continue;
}
$is_now = mailbox('get', 'mailbox_details', $mailbox);
if(empty($is_now)){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('username_invalid', $mailbox)
);
continue;
}
array_push($footers['mbox_exclude'], $mailbox);
}
}
catch (RedisException $e) {
foreach ($domains as $domain) {
$domain = idn_to_ascii(strtolower(trim($domain)), 0, INTL_IDNA_VARIANT_UTS46);
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'domain_invalid'
);
return false;
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
try {
$stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain");
$stmt->execute(array(':domain' => $domain));
$stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`) VALUES (:domain, :html, :plain, :mbox_exclude)");
$stmt->execute(array(
':domain' => $domain,
':html' => $footers['html'],
':plain' => $footers['plain'],
':mbox_exclude' => json_encode($footers['mbox_exclude']),
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'danger',
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('redis_error', $e)
'msg' => array('domain_footer_modified', htmlspecialchars($domain))
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('domain_footer_modified', htmlspecialchars($domain))
);
break;
}
break;
@@ -3929,13 +4035,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$stmt = $pdo->prepare("SELECT `id` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
$stmt = $pdo->prepare("SELECT `id`, `address` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
$stmt->execute(array(
':domain' => $_data,
));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$aliases[] = $row['id'];
if ($_extra == "address"){
$aliases[] = $row['address'];
} else {
$aliases[] = $row['id'];
}
}
return $aliases;
break;
@@ -4287,6 +4397,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`modified`,
`quota2`.`bytes`,
`attributes`,
`custom_attributes`,
`quota2`.`messages`
FROM `mailbox`, `quota2`, `domain`
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
@@ -4307,6 +4418,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`modified`,
`quota2replica`.`bytes`,
`attributes`,
`custom_attributes`,
`quota2replica`.`messages`
FROM `mailbox`, `quota2replica`, `domain`
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
@@ -4329,6 +4441,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$mailboxdata['quota'] = $row['quota'];
$mailboxdata['messages'] = $row['messages'];
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
$mailboxdata['custom_attributes'] = json_decode($row['custom_attributes'], true);
$mailboxdata['quota_used'] = intval($row['bytes']);
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
$mailboxdata['created'] = $row['created'];
@@ -4509,19 +4622,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
try {
$footers = $redis->hGet('DOMAIN_WIDE_FOOTER', $domain);
$footers = json_decode($footers, true);
$stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude` FROM `domain_wide_footer`
WHERE `domain` = :domain");
$stmt->execute(array(
':domain' => $domain
));
$footer = $stmt->fetch(PDO::FETCH_ASSOC);
}
catch (RedisException $e) {
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('redis_error', $e)
'msg' => $e->getMessage()
);
return false;
}
return $footers;
return $footer;
break;
}
break;

View File

@@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "14022023_1000";
$db_version = "21112023_1644";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -267,6 +267,20 @@ function init_db_schema() {
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"domain_wide_footer" => array(
"cols" => array(
"domain" => "VARCHAR(255) NOT NULL",
"html" => "LONGTEXT",
"plain" => "LONGTEXT",
"mbox_exclude" => "JSON NOT NULL DEFAULT ('[]')",
),
"keys" => array(
"primary" => array(
"" => array("domain")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"tags_domain" => array(
"cols" => array(
"tag_name" => "VARCHAR(255) NOT NULL",
@@ -344,6 +358,7 @@ function init_db_schema() {
"local_part" => "VARCHAR(255) NOT NULL",
"domain" => "VARCHAR(255) NOT NULL",
"attributes" => "JSON",
"custom_attributes" => "JSON NOT NULL DEFAULT ('{}')",
"kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
"multiple_bookings" => "INT NOT NULL DEFAULT -1",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
@@ -704,6 +719,7 @@ function init_db_schema() {
"timeout1" => "SMALLINT NOT NULL DEFAULT '600'",
"timeout2" => "SMALLINT NOT NULL DEFAULT '600'",
"subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'",
"dry" => "TINYINT(1) NOT NULL DEFAULT '0'",
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
"returned_text" => "LONGTEXT",
"last_run" => "TIMESTAMP NULL DEFAULT NULL",

View File

@@ -19,10 +19,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.composer/cache/files
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
@@ -52,10 +52,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.composer/cache/files
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}

View File

@@ -12,7 +12,7 @@ jobs:
dependency-version: [prefer-lowest, prefer-stable]
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Install dependencies
run: composer update --no-progress --ignore-platform-reqs
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@@ -13,7 +13,7 @@ jobs:
php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2
@@ -86,7 +86,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Set-up PHP"
uses: shivammathur/setup-php@v2
@@ -33,7 +33,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@@ -54,7 +54,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Run DOCtor-RST"
uses: docker://oskarstark/doctor-rst

View File

@@ -0,0 +1,18 @@
headline: lang.sieve_preset_8
content: |
require "fileinto";
require "mailbox";
require "variables";
require "subaddress";
require "envelope";
require "duplicate";
require "imap4flags";
if header :matches "To" "*mail@domain.tld*" {
redirect "anothermail@anotherdomain.tld";
setflag "\\seen"; /* Mark mail as read */
fileInto "INBOX/SubFolder"; /* Move mail on subfolder after */
} else {
# The rest goes into INBOX
# default is "implicit keep", we do it explicitly here
keep;
}

View File

@@ -97,6 +97,7 @@ $AVAILABLE_LANGUAGES = array(
'lv-lv' => 'latviešu (Latvian)',
'nl-nl' => 'Nederlands (Dutch)',
'pl-pl' => 'Język Polski (Polish)',
'pt-br' => 'Português brasileiro (Brazilian Portuguese)',
'pt-pt' => 'Português (Portuguese)',
'ro-ro' => 'Română (Romanian)',
'ru-ru' => 'Pусский (Russian)',
@@ -235,118 +236,120 @@ $RSPAMD_MAPS = array(
$IMAPSYNC_OPTIONS = array(
'whitelist' => array(
'abort',
'authmd51',
'authmd52',
'authmech1',
'authmech2',
'authuser1',
'authuser2',
'debugcontent',
'disarmreadreceipts',
'logdir',
'debugcrossduplicates',
'maxsize',
'minsize',
'minage',
'search',
'noabletosearch',
'pidfile',
'pidfilelocking',
'search1',
'search2',
'sslargs1',
'sslargs2',
'syncduplicates',
'usecache',
'synclabels',
'truncmess',
'domino2',
'expunge1',
'filterbuggyflags',
'justconnect',
'justfolders',
'maxlinelength',
'useheader',
'noabletosearch1',
'nolog',
'prefix1',
'prefix2',
'sep1',
'sep2',
'nofoldersizesatend',
'justfoldersizes',
'proxyauth1',
'skipemptyfolders',
'include',
'subfolder1',
'subscribed',
'subscribe',
'debug',
'debugcontent',
'debugcrossduplicates',
'debugflags',
'debugfolders',
'debugimap',
'debugimap1',
'debugimap2',
'debugmemory',
'debugssl',
'delete1emptyfolders',
'delete2folders',
'disarmreadreceipts',
'domain1',
'domain2',
'domino1',
'domino2',
'dry',
'errorsmax',
'exchange1',
'exchange2',
'exitwhenover',
'expunge1',
'f1f2',
'filterbuggyflags',
'folder',
'folderfirst',
'folderlast',
'folderrec',
'gmail1',
'gmail2',
'idatefromheader',
'include',
'inet4',
'inet6',
'justconnect',
'justfolders',
'justfoldersizes',
'justlogin',
'keepalive1',
'keepalive2',
'log',
'logdir',
'logfile',
'maxbytesafter',
'maxlinelength',
'maxmessagespersecond',
'maxsize',
'maxsleep',
'minage',
'minsize',
'noabletosearch',
'noabletosearch1',
'noabletosearch2',
'noexpunge1',
'noexpunge2',
'nofoldersizesatend',
'noid',
'nolog',
'nomixfolders',
'noresyncflags',
'nossl1',
'nouidexpunge2',
'syncinternaldates',
'idatefromheader',
'useuid',
'debugflags',
'debugimap',
'delete1emptyfolders',
'delete2folders',
'gmail2',
'office1',
'testslive6',
'debugimap1',
'errorsmax',
'tests',
'gmail1',
'maxmessagespersecond',
'maxbytesafter',
'maxsleep',
'abort',
'resyncflags',
'resynclabels',
'syncacls',
'nossl2',
'nosyncacls',
'notls1',
'notls2',
'nouidexpunge2',
'nousecache',
'office2',
'testslive',
'debugmemory',
'exitwhenover',
'noid',
'noexpunge1',
'authmd51',
'logfile',
'proxyauth2',
'domain1',
'domain2',
'oauthaccesstoken1',
'oauthaccesstoken2',
'oauthdirect1',
'oauthdirect2',
'folder',
'folderrec',
'folderfirst',
'folderlast',
'nomixfolders',
'authmd52',
'debugfolders',
'nossl2',
'office1',
'office2',
'pidfile',
'pidfilelocking',
'prefix1',
'prefix2',
'proxyauth1',
'proxyauth2',
'resyncflags',
'resynclabels',
'search',
'search1',
'search2',
'sep1',
'sep2',
'showpasswords',
'skipemptyfolders',
'ssl2',
'sslargs1',
'sslargs2',
'subfolder1',
'subscribe',
'subscribed',
'syncacls',
'syncduplicates',
'syncinternaldates',
'synclabels',
'tests',
'testslive',
'testslive6',
'tls2',
'notls2',
'debugssl',
'notls1',
'inet4',
'inet6',
'log',
'showpasswords'
'truncmess',
'usecache',
'useheader',
'useuid'
),
'blacklist' => array(
'skipmess',

View File

@@ -1684,7 +1684,7 @@ function showVersionModal(title, version){
function parseGithubMarkdownLinks(inputText) {
var replacedText, replacePattern1;
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])(?![^<]*>)/gim;
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
if (matched.includes('github.com')){
// return short link if it's github link

View File

@@ -199,6 +199,23 @@ jQuery(function($){
});
}
function add_table_row(table_id, type) {
var row = $('<tr />');
if (type == "mbox_attr") {
cols = '<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="attribute" required></td>';
cols += '<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="value" required></td>';
cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang_admin.remove_row + '</a></td>';
}
row.append(cols);
table_id.append(row);
}
$('#mbox_attr_table').on('click', 'tr a', function (e) {
e.preventDefault();
$(this).parents('tr').remove();
});
$('#add_mbox_attr_row').click(function() {
add_table_row($('#mbox_attr_table'), "mbox_attr");
});
// detect element visibility changes
function onVisible(element, callback) {

View File

@@ -220,7 +220,7 @@ jQuery(function($){
if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive';
else highlightClass = 'neutral';
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? escapeHtml(value.options.join(', ')) : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
});
$('[data-bs-toggle="tooltip"]').tooltip();
}
@@ -295,3 +295,7 @@ jQuery(function($){
$(".table_collapse_option").hide();
}
});

View File

@@ -1591,6 +1591,12 @@ if (isset($_GET['query'])) {
}
}
break;
case "spam-score":
$score = mailbox('get', 'spam_score', $object);
if ($score)
$score = array("score" => preg_replace("/\s+/", "", $score));
process_get_return($score);
break;
break;
// return no route found if no case is matched
default:
@@ -1867,8 +1873,6 @@ if (isset($_GET['query'])) {
case "quota_notification_bcc":
process_edit_return(quota_notification_bcc('edit', $attr));
break;
case "domain-wide-footer":
process_edit_return(mailbox('edit', 'domain_wide_footer', $attr));
break;
case "mailq":
process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
@@ -1881,6 +1885,9 @@ if (isset($_GET['query'])) {
case "template":
process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr)));
break;
case "custom-attribute":
process_edit_return(mailbox('edit', 'mailbox_custom_attribute', array_merge(array('mailboxes' => $items), $attr)));
break;
default:
process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
break;
@@ -1900,6 +1907,9 @@ if (isset($_GET['query'])) {
case "template":
process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr)));
break;
case "footer":
process_edit_return(mailbox('edit', 'domain_wide_footer', array_merge(array('domains' => $items), $attr)));
break;
default:
process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
break;

View File

@@ -147,6 +147,8 @@
"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.",
"change_logo": "Změnit logo",
"logo_normal_label": "Normální",
"logo_dark_label": "Inverzní pro tmavý režim",
"configuration": "Nastavení",
"convert_html_to_text": "Převést HTML do prostého textu",
"credentials_transport_warning": "<b>Upozornění</b>: Přidání položky do transportní mapy aktualizuje také přihlašovací údaje všech záznamů s odpovídajícím skokem.",
@@ -206,6 +208,9 @@
"include_exclude": "Zahrnout/Vyloučit",
"include_exclude_info": "Ve výchozím nastavení (bez výběru), jsou adresovány <b>všechny mailové schránky</b>",
"includes": "Zahrnout tyto přijemce",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je zakázána. Můžete ji povolit v nabídce<br> <strong>Systém > Nastavení > Možnosti > Přizpůsobení</strong>",
"ip_check_opt_in": "Přihlásit se k používání služby třetí strany <strong>ipv4.mailcow.email</strong> a <strong>ipv6.mailcow.email</strong> pro zjištění externích IP adres.",
"is_mx_based": "Na základě MX",
"last_applied": "Naposledy použité",
"license_info": "Licence není povinná, pomůžete však dalšímu vývoji.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registrujte si své GUID</a>, nebo si <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">zaplaťte podporu pro svou instalaci mailcow.</a>",
@@ -232,6 +237,7 @@
"oauth2_renew_secret": "Vytvořit nový tajný klíč",
"oauth2_revoke_tokens": "Odvolat všechny klientské tokeny",
"optional": "volitelné",
"options": "Možnosti",
"password": "Heslo",
"password_length": "Délka hesla",
"password_policy": "Politika hesel",
@@ -337,9 +343,7 @@
"verify": "Ověřit",
"yes": "&#10003;",
"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)",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>"
"f2b_max_ban_time": "Maximální délka banu (s)"
},
"danger": {
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
@@ -443,6 +447,9 @@
"target_domain_invalid": "Cílová doména %s je neplatná",
"targetd_not_found": "Cílová doména %s nenalezena",
"targetd_relay_domain": "Cílová doména %s je předávaná",
"template_exists": "Šablona %s již existuje",
"template_id_invalid": "Šablona ID %s je neplatná",
"template_name_invalid": "Název šablony je neplatný",
"temp_error": "Dočasná chyba",
"text_empty": "Text nesmí být prázdný",
"tfa_token_invalid": "Neplatný TFA token",
@@ -461,27 +468,27 @@
"yotp_verification_failed": "Yubico OTP ověření selhalo: %s"
},
"datatables": {
"emptyTable": "Tabulka neobsahuje žádná data",
"info": "Zobrazuji _START_ až _END_ z celkem _TOTAL_ záznamů",
"infoEmpty": "Zobrazuji 0 až 0 z 0 záznamů",
"infoFiltered": "(filtrováno z celkem _MAX_ záznamů)",
"loadingRecords": "Načítám...",
"zeroRecords": "Žádné záznamy nebyly nalezeny",
"paginate": {
"first": "První",
"last": "Poslední",
"next": "Další",
"previous": "Předchozí"
},
"aria": {
"sortAscending": ": aktivujte pro seřazení vzestupně",
"sortDescending": ": aktivujte pro seřazení sestupně"
},
"lengthMenu": "Zobrazit _MENU_ výsledků",
"processing": "Zpracovávání...",
"search": "Vyhledávání:",
"decimal": ",",
"thousands": " "
"emptyTable": "Tabulka neobsahuje žádná data",
"info": "Zobrazuji _START_ až _END_ z celkem _TOTAL_ záznamů",
"infoEmpty": "Zobrazuji 0 až 0 z 0 záznamů",
"infoFiltered": "(filtrováno z celkem _MAX_ záznamů)",
"loadingRecords": "Načítám...",
"zeroRecords": "Žádné záznamy nebyly nalezeny",
"paginate": {
"first": "První",
"last": "Poslední",
"next": "Další",
"previous": "Předchozí"
},
"aria": {
"sortAscending": ": aktivujte pro seřazení vzestupně",
"sortDescending": ": aktivujte pro seřazení sestupně"
},
"lengthMenu": "Zobrazit _MENU_ výsledků",
"processing": "Zpracovávání...",
"search": "Vyhledávání:",
"decimal": ",",
"thousands": " "
},
"debug": {
"chart_this_server": "Graf (tento server)",
@@ -670,6 +677,7 @@
"apps": "Aplikace",
"debug": "Systémové informace",
"email": "E-Mail",
"mailcow_system": "Systém",
"mailcow_config": "Nastavení",
"quarantine": "Karanténa",
"restart_netfilter": "Restartovat netfilter",
@@ -705,6 +713,7 @@
"add_mailbox": "Přidat mailovou schránku",
"add_recipient_map_entry": "Přidat mapu příjemce",
"add_resource": "Přidat zdroj",
"add_template": "Přidat šablonu",
"add_tls_policy_map": "Přidat mapu TLS pravidel",
"address_rewriting": "Přepisování adres",
"alias": "Alias",
@@ -747,6 +756,7 @@
"domain": "Doména",
"domain_admins": "Správci domén",
"domain_aliases": "Doménové aliasy",
"domain_templates": "Šablony domén",
"domain_quota": "Kvóta",
"domain_quota_total": "Celková kvóta domény",
"domains": "Domény",
@@ -775,6 +785,7 @@
"mailbox_defaults": "Výchozí nastavení",
"mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky",
"mailbox_defquota": "Výchozí velikost schránky",
"mailbox_templates": "Šablony schránek",
"mailbox_quota": "Max. velikost schránky",
"mailboxes": "Mailové schránky",
"max_aliases": "Max. počet aliasů",
@@ -842,6 +853,8 @@
"table_size_show_n": "Zobrazit %s položek",
"target_address": "Cílová adresa",
"target_domain": "Cílová doména",
"templates": "Šablony",
"template": "Šablona",
"tls_enforce_in": "Vynutit TLS pro příchozí",
"tls_enforce_out": "Vynutit TLS pro odchozí",
"tls_map_dest": "Cíl",
@@ -1006,6 +1019,9 @@
"settings_map_added": "Přidána položka mapování nastavení",
"settings_map_removed": "Položka mapování nastavení: %s smazána",
"sogo_profile_reset": "SOGo profil uživatele %s vyresetován",
"template_added": "Přidána šablona %s",
"template_modified": "Změny šablony %s byly uloženy",
"template_removed": "Šablona ID %s byla odstraněna",
"tls_policy_map_entry_deleted": "Položka mapy TLS pravidel ID %s smazána",
"tls_policy_map_entry_saved": "Položka mapy TLS pravidel \"%s\" uložena",
"ui_texts": "Změny UI textů uloženy",

View File

@@ -58,6 +58,7 @@
"domain": "Domain",
"domain_matches_hostname": "Domain %s darf nicht dem Hostnamen entsprechen",
"domain_quota_m": "Domain-Speicherplatz gesamt (MiB)",
"dry": "Synchronisation simulieren",
"enc_method": "Verschlüsselung",
"exclude": "Elemente ausschließen (Regex)",
"full_name": "Vor- und Nachname",
@@ -573,6 +574,7 @@
"client_secret": "Client-Secret",
"comment_info": "Ein privater Kommentar ist für den Benutzer nicht einsehbar. Ein öffentlicher Kommentar wird als Tooltip im Interface des Benutzers angezeigt.",
"created_on": "Erstellt am",
"custom_attributes": "benutzerdefinierte Attribute",
"delete1": "Lösche Nachricht nach Übertragung vom Quell-Server",
"delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind",
"delete2duplicates": "Lösche Duplikate im Ziel",
@@ -613,6 +615,7 @@
"max_quota": "Max. Größe per Mailbox (MiB)",
"maxage": "Maximales Alter in Tagen einer Nachricht, die kopiert werden soll<br><small>(0 = alle Nachrichten kopieren)</small>",
"maxbytespersecond": "Max. Übertragungsrate in Bytes/s (0 für unlimitiert)",
"mbox_exclude": "Mailboxen ausschließen",
"mbox_rl_info": "Dieses Limit wird auf den SASL Loginnamen angewendet und betrifft daher alle Absenderadressen, die der eingeloggte Benutzer verwendet. Bei Mailbox Ratelimit überwiegt ein Domain-weites Ratelimit.",
"mins_interval": "Intervall (min)",
"multiple_bookings": "Mehrfaches Buchen",
@@ -857,7 +860,7 @@
"sieve_preset_5": "Auto-Responder (Vacation, Urlaub)",
"sieve_preset_6": "E-Mails mit Nachricht abweisen",
"sieve_preset_7": "Weiterleiten und behalten oder verwerfen",
"sieve_preset_8": "Nachricht verwerfen, wenn Absender und Alias-Ziel identisch sind.",
"sieve_preset_8": "E-Mail eines bestimmten Absenders umleiten, als gelesen markieren und in Unterordner sortieren",
"sieve_preset_header": "Beispielinhalte zur Einsicht stehen nachstehend bereit. Siehe auch <a href=\"https://de.wikipedia.org/wiki/Sieve\" target=\"_blank\">Wikipedia</a>.",
"sogo_visible": "Alias Sichtbarkeit in SOGo",
"sogo_visible_n": "Alias in SOGo verbergen",
@@ -1124,6 +1127,7 @@
"apple_connection_profile_complete": "Dieses Verbindungsprofil beinhaltet neben IMAP- und SMTP-Konfigurationen auch Pfade für die Konfiguration von CalDAV (Kalender) und CardDAV (Adressbücher) für ein Apple-Gerät.",
"apple_connection_profile_mailonly": "Dieses Verbindungsprofil beinhaltet IMAP- und SMTP-Konfigurationen für ein Apple-Gerät.",
"apple_connection_profile_with_app_password": "Es wird ein neues App-Passwort erzeugt und in das Profil eingefügt, damit bei der Einrichtung kein Passwort eingegeben werden muss. Geben Sie das Profil nicht weiter, da es einen vollständigen Zugriff auf Ihr Postfach ermöglicht.",
"attribute": "Attribut",
"change_password": "Passwort ändern",
"change_password_hint_app_passwords": "Ihre Mailbox hat %d App-Passwörter, die nicht geändert werden. Um diese zu verwalten, gehen Sie bitte zum App-Passwörter-Tab.",
"clear_recent_successful_connections": "Alle erfolgreichen Verbindungen bereinigen",
@@ -1243,6 +1247,7 @@
"tls_policy_warning": "<strong>Vorsicht:</strong> Entscheiden Sie sich unverschlüsselte Verbindungen abzulehnen, kann dies dazu führen, dass Kontakte Sie nicht mehr erreichen.<br>Nachrichten, die die Richtlinie nicht erfüllen, werden durch einen Hard-Fail im Mailsystem abgewiesen.<br>Diese Einstellung ist aktiv für die primäre Mailbox, für alle Alias-Adressen, die dieser Mailbox <b>direkt zugeordnet</b> sind (lediglich eine einzige Ziel-Adresse) und der Adressen, die sich aus Alias-Domains ergeben. Ausgeschlossen sind temporäre Aliasse (\"Spam-Alias-Adressen\"), Catch-All Alias-Adressen sowie Alias-Adressen mit mehreren Zielen.",
"user_settings": "Benutzereinstellungen",
"username": "Benutzername",
"value": "Wert",
"verify": "Verifizieren",
"waiting": "Warte auf Ausführung",
"week": "Woche",

View File

@@ -58,6 +58,7 @@
"domain": "Domain",
"domain_matches_hostname": "Domain %s matches hostname",
"domain_quota_m": "Total domain quota (MiB)",
"dry": "Simulate synchronization",
"enc_method": "Encryption method",
"exclude": "Exclude objects (regex)",
"full_name": "Full name",
@@ -575,6 +576,7 @@
"client_secret": "Client secret",
"comment_info": "A private comment is not visible to the user, while a public comment is shown as tooltip when hovering it in a user's overview",
"created_on": "Created on",
"custom_attributes": "Custom attributes",
"delete1": "Delete from source when completed",
"delete2": "Delete messages on destination that are not on source",
"delete2duplicates": "Delete duplicates on destination",
@@ -591,7 +593,8 @@
"from_user": "{= from_user =} - From user part of envelope, e.g for \"moo@mailcow.tld\" it returns \"moo\"",
"from_name": "{= from_name =} - From name of envelope, e.g for \"Mailcow &lt;moo@mailcow.tld&gt;\" it returns \"Mailcow\"",
"from_addr": "{= from_addr =} - From address part of envelope",
"from_domain": "{= from_domain =} - From domain part of envelope"
"from_domain": "{= from_domain =} - From domain part of envelope",
"custom": "{= foo =} - If mailbox has the custom attribute \"foo\" with value \"bar\" it returns \"bar\""
},
"domain_footer_plain": "PLAIN footer",
"domain_quota": "Domain quota",
@@ -622,6 +625,7 @@
"max_quota": "Max. quota per mailbox (MiB)",
"maxage": "Maximum age of messages in days that will be polled from remote<br><small>(0 = ignore age)</small>",
"maxbytespersecond": "Max. bytes per second <br><small>(0 = unlimited)</small>",
"mbox_exclude": "Exclude mailboxes",
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
"mins_interval": "Interval (min)",
"multiple_bookings": "Multiple bookings",
@@ -873,7 +877,7 @@
"sieve_preset_5": "Auto responder (vacation)",
"sieve_preset_6": "Reject mail with reponse",
"sieve_preset_7": "Redirect and keep/drop",
"sieve_preset_8": "Discard message sent to an alias address the sender is part of",
"sieve_preset_8": "Redirect e-mail from a specific sender, mark as read and sort into subfolder",
"sieve_preset_header": "Please see the example presets below. For more details see <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedia</a>.",
"sogo_visible": "Alias is visible in SOGo",
"sogo_visible_n": "Hide alias in SOGo",
@@ -1140,6 +1144,7 @@
"apple_connection_profile_complete": "This connection profile includes IMAP and SMTP parameters as well as CalDAV (calendars) and CardDAV (contacts) paths for an Apple device.",
"apple_connection_profile_mailonly": "This connection profile includes IMAP and SMTP configuration parameters for an Apple device.",
"apple_connection_profile_with_app_password": "A new app password is generated and added to the profile so that no password needs to be entered when setting up your device. Please do not share the file as it grants full access to your mailbox.",
"attribute": "Attribute",
"change_password": "Change password",
"change_password_hint_app_passwords": "Your account has %d app passwords that will not be changed. To manage these, go to the App passwords tab.",
"clear_recent_successful_connections": "Clear seen successful connections",
@@ -1270,6 +1275,7 @@
"tls_policy_warning": "<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br>Messages to not satisfy the policy will be bounced with a hard fail by the mail system.<br>This option applies to your primary email address (login name), all addresses derived from alias domains as well as alias addresses <b>with only this single mailbox</b> as target.",
"user_settings": "User settings",
"username": "Username",
"value": "Value",
"verify": "Verify",
"waiting": "Waiting",
"week": "week",

View File

@@ -891,5 +891,17 @@
"no_active_admin": "Viimeistä aktiivista järjestelmänvalvojaa ei voi poistaa käytöstä",
"session_token": "Lomakkeen tunnus sanoma ei kelpaa: tunnus sanoman risti riita",
"session_ua": "Lomakkeen tunnus sanoma ei kelpaa: käyttäjä agentin tarkistus virhe"
},
"datatables": {
"emptyTable": "Tietoja ei ole saatavilla taulukossa",
"expand_all": "Laajenna kaikki",
"lengthMenu": "Näytä menu merkinnät",
"loadingRecords": "Ladataan...",
"processing": "Ole hyvä ja odota...",
"search": "Etsi:",
"paginate": {
"first": "Ensimmäinen",
"last": "Edellinen"
}
}
}

View File

@@ -89,7 +89,7 @@
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Vous pouvez définir des cartes de transport vers une destination personnalisée pour ce domaine. sinon, une recherche MX sera effectuée.",
"relay_unknown_only": "Relayer uniquement les boîtes inexistantes. Les boîtes existantes seront livrées localement.",
"relayhost_wrapped_tls_info": "Veuillez <b>ne pas</b> utiliser des ports TLS wrappés (généralement utilisés sur le port 465).<br>\r\nUtilisez n'importe quel port non encapsulé et lancez STARTTLS. Une politique TLS pour appliquer TLS peut être créée dans \"Cartes de politique TLS\".",
"select": "Veuillez sélectionner...",
"select": "Veuillez sélectionner",
"select_domain": "Sélectionner d'abord un domaine",
"sieve_desc": "Description courte",
"sieve_type": "Type de filtre",
@@ -207,7 +207,7 @@
"last_applied": "Dernière application",
"license_info": "Une licence nest pas requise, mais contribue au développement.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Enregistrer votre GUID ici</a> or <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">acheter le support pour votre intallation Mailcow.</a>",
"link": "Lien",
"loading": "Veuillez patienter...",
"loading": "Veuillez patienter",
"logo_info": "Votre image sera redimensionnée à une hauteur de 40 pixels pour la barre de navigation du haut et à un maximum de 250 pixels en largeur pour la page d'accueil. Un graphique extensible est fortement recommandé.",
"lookup_mx": "Faire correspondre la destination à MX (.outlook.com pour acheminer tous les messages ciblés vers un MX * .outlook.com sur ce tronçon)",
"main_name": "\"mailcow UI\" nom",
@@ -326,7 +326,11 @@
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
"password_policy_numbers": "Doit contenir au moins un chiffre",
"ip_check": "Vérification IP",
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>",
"logo_normal_label": "Normal",
"logo_dark_label": "Inversé pour le mode sombre",
"allowed_methods": "Access-Control-Allow-Methods",
"allowed_origins": "Access-Control-Allow-Origin"
},
"danger": {
"access_denied": "Accès refusé ou données de formulaire non valides",
@@ -598,9 +602,9 @@
"delete_these_items": "Veuillez confirmer les modifications apportées à lidentifiant dobjet suivant",
"hibp_nok": "Trouvé ! Il sagit dun mot de passe potentiellement dangereux !",
"hibp_ok": "Aucune correspondance trouvée.",
"loading": "Veuillez patienter...",
"loading": "Veuillez patienter",
"restart_container": "Redémarrer le conteneur",
"restart_container_info": "<b>Important:</b> Un redémarrage en douceur peut prendre un certain temps, veuillez attendre quil soit terminé..",
"restart_container_info": "<b>Important:</b> Un redémarrage en douceur peut prendre un certain temps, veuillez attendre quil soit terminé.",
"restart_now": "Redémarrer maintenant",
"restarting_container": "Redémarrage du conteneur, cela peut prendre un certain temps"
},
@@ -935,7 +939,7 @@
"disable_tfa": "Désactiver TFA jusquà la prochaine ouverture de session réussie",
"enter_qr_code": "Votre code TOTP si votre appareil ne peut pas scanner les codes QR",
"error_code": "Code d'erreur",
"init_webauthn": "Initialisation, veuillez patienter...",
"init_webauthn": "Initialisation, veuillez patienter",
"key_id": "Un identifiant pour votre Périphérique",
"key_id_totp": "Un identifiant pour votre clé",
"none": "Désactiver",
@@ -948,8 +952,8 @@
"tfa_token_invalid": "Token TFA invalide",
"totp": "OTP (One Time Password = Mot de passe à usage unique : Google Authenticator, Authy, etc.)",
"webauthn": "Authentification WebAuthn",
"waiting_usb_auth": "<i>En attente dun périphérique USB...</i><br><br>Sil vous plaît appuyez maintenant sur le bouton de votre périphérique USB WebAuthn.",
"waiting_usb_register": "<i>En attente dun périphérique USB...</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription WebAuthn en appuyant sur le bouton de votre périphérique USB WebAuthn.",
"waiting_usb_auth": "<i>En attente dun périphérique USB</i><br><br>Sil vous plaît appuyez maintenant sur le bouton de votre périphérique USB WebAuthn.",
"waiting_usb_register": "<i>En attente dun périphérique USB</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription WebAuthn en appuyant sur le bouton de votre périphérique USB WebAuthn.",
"yubi_otp": "Authentification OTP Yubico"
},
"fido2": {
@@ -999,9 +1003,9 @@
"eas_reset": "Réinitialiser le cache de lappareil Activesync",
"eas_reset_help": "Dans de nombreux cas, une réinitialisation du cache de lappareil aidera à récupérer un profil Activesync cassé.<br><b>Attention :</b> Tous les éléments seront à nouveau téléchargés !",
"eas_reset_now": "Réinitialiser maintenant",
"edit": "Editer",
"email": "Email",
"email_and_dav": "Email, calendriers et contacts",
"edit": "Éditer",
"email": "E-mail",
"email_and_dav": "E-mail, calendriers et contacts",
"encryption": "Cryptage",
"excludes": "Exclut",
"expire_in": "Expire dans",
@@ -1015,7 +1019,7 @@
"is_catch_all": "Attrape-tout pour le domaine(s)",
"last_mail_login": "Dernière connexion mail",
"last_run": "Dernière exécution",
"loading": "Chargement...",
"loading": "Chargement",
"mailbox_details": "Détails de la boîte",
"messages": "messages",
"never": "jamais",
@@ -1051,7 +1055,7 @@
"shared_aliases": "Adresses alias partagées",
"shared_aliases_desc": "Les alias partagés ne sont pas affectés par les paramètres spécifiques à lutilisateur tels que le filtre anti-spam ou la politique de chiffrement. Les filtres anti-spam correspondants ne peuvent être effectués que par un administrateur en tant que politique de domaine.",
"show_sieve_filters": "Afficher le filtre de tamis actif de lutilisateur",
"sogo_profile_reset": "Remise é zéro du profil SOGo",
"sogo_profile_reset": "Remise à zéro du profil SOGo",
"sogo_profile_reset_help": "Ceci détruira un profil Sogo des utilisateurs et <b>supprimera toutes les données de contact et de calendrier irrécupérables</b>.",
"sogo_profile_reset_now": "Remise à zéro du profil maintenant",
"spam_aliases": "Alias de courriel temporaire",

View File

@@ -394,7 +394,12 @@
"filters": "Szűrők",
"login_as": "Bejelentkezés mint",
"quarantine": "Karantén műveletek",
"bcc_maps": "BCC címek"
"bcc_maps": "BCC címek",
"domain_relayhost": "Relayhost megváltoztatása egy domainhez",
"protocol_access": "Protokoll-hozzáférés módosítása",
"quarantine_attachments": "Karantén mellékletek",
"quarantine_category": "Karantén értesítési kategória módosítása",
"quarantine_notification": "Karantén értesítések módosítása"
},
"diagnostics": {
"dns_records": "DNS bejegyzések"

File diff suppressed because it is too large Load Diff

View File

@@ -107,7 +107,8 @@
"validate": "Проверить",
"validation_success": "Проверка прошла успешно",
"tags": "Теги",
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения"
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения",
"dry": "Имитировать синхронизацию"
},
"admin": {
"access": "Настройки доступа",
@@ -625,11 +626,14 @@
"auth_user": "{= auth_user =} - Аутентифицированное имя пользователя, указанное MTA",
"from_user": "{= from_user =} - Из пользовательской части envelope, например, для \"moo@mailcow.tld\" возвращается \"moo\"",
"from_addr": "{= from_addr =} - Из адресной части envelope",
"from_domain": "{= from_domain =} - из доменной части envelope"
"from_domain": "{= from_domain =} - из доменной части envelope",
"custom": "{= foo =} - Если почтовый ящик имеет пользовательский атрибут \"foo\" со значением \"bar\", он возвращает \"bar\"."
},
"domain_footer": "Нижний колонтитул домена",
"domain_footer_html": "HTML нижний колонтитул",
"domain_footer_plain": "ПРОСТОЙ нижний колонтитул"
"domain_footer_plain": "ПРОСТОЙ нижний колонтитул",
"mbox_exclude": "Исключить почтовые ящики",
"custom_attributes": "Пользовательские атрибуты"
},
"fido2": {
"confirm": "Подтвердить",
@@ -1198,7 +1202,9 @@
"apple_connection_profile_with_app_password": "Новый пароль приложения генерируется и добавляется в профиль, поэтому при настройке устройства не требуется вводить пароль. Не предоставляйте доступ к файлу, поскольку он предоставляет полный доступ к вашему почтовому ящику.",
"direct_protocol_access": "Этот пользователь почтового ящика имеет <b>прямой, внешний доступ</b> к следующим протоколам и приложениям. Эта настройка контролируется вашим администратором. Для предоставления доступа к отдельным протоколам и приложениям могут быть созданы пароли приложений.<br> Кнопка \"Вход в веб-почту\" обеспечивает единый вход в SOGo и всегда доступна.",
"with_app_password": "с паролем приложения",
"change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\"."
"change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".",
"attribute": "Атрибут",
"value": "Значение"
},
"warning": {
"cannot_delete_self": "Вы не можете удалить сами себя",

View File

@@ -342,7 +342,12 @@
"username": "Uporabniško ime",
"validate_license_now": "Potrdi GUID z licenčnim strežnikom",
"verify": "Preveri",
"yes": "&#10003;"
"yes": "&#10003;",
"logo_normal_label": "Navadno",
"logo_dark_label": "Za temni način",
"cors_settings": "Nastavitve CORS",
"allowed_methods": "Dovoljene metode za upravljanje dostopa",
"allowed_origins": "Upravljanje-dostopa-Dovoljeni-Viri"
},
"danger": {
"alias_goto_identical": "Alias in goto naslov morata biti identična",
@@ -391,6 +396,161 @@
"invalid_destination": "Ciljna oblika \"%s\" ni veljavna",
"invalid_filter_type": "Neveljavna vrsta filtra",
"invalid_host": "Naveden je neveljaven gostitelj (host): %s",
"invalid_mime_type": "Neveljaven mime type"
"invalid_mime_type": "Neveljaven mime type",
"max_quota_in_use": "Kvota poštnega predala mora biti večja ali enaka %d MB",
"password_complexity": "Geslo ne ustreza varnostni politiki",
"pushover_credentials_missing": "Manjka Pushover token ali ključ",
"release_send_failed": "Sporočila ni bilo mogoče sprostiti: %s",
"tls_policy_map_dest_invalid": "Cilj politike ni veljaven",
"webauthn_authenticator_failed": "Izbrani avtentikator ni bil najden",
"reset_f2b_regex": "Regex filter ni bilo možno ponastaviti v ustreznem času. Prosim poskusite ponovno ali počakajte nekaj sekund in ponovno naložite stran.",
"target_domain_invalid": "Ciljna domena %s ni veljavna",
"validity_missing": "Prosim nastavite obdobje veljavnosti",
"invalid_recipient_map_old": "Naveden neveljaven izvirni prejemnik: %s",
"ip_list_empty": "Seznam dovoljenih IPjev ne sme biti prazen",
"is_alias": "%s je že znan kot alias naslov",
"is_alias_or_mailbox": "%s je že znan kot alias, poštni naslov, ali alias izveden iz alias domene",
"is_spam_alias": "%s že obstaja kot začasen alias (spam alias naslov)",
"last_key": "Zadnji ključ ne more biti izbrisan, prosim raje deaktivirajte dvofaktorsko avtentikacijo (TFA)",
"login_failed": "Prijava ni uspela",
"mailbox_defquota_exceeds_mailbox_maxquota": "Privzeta kvota presega najvišjo omejitev",
"mailbox_invalid": "Ime poštnega predala ni veljavno",
"mailbox_quota_exceeded": "Kvota presega omejitev domene (maksimalno %d MB)",
"mailbox_quota_exceeds_domain_quota": "Najvišja kvota presega omejitev domene",
"mailbox_quota_left_exceeded": "Ni dovolj prostora (preostali prostor: %d MB)",
"mailboxes_in_use": "Največje število poštnih predalov mora biti večje ali enako %d",
"malformed_username": "Nepravilno oblikovano uporabniško ime",
"map_content_empty": "Preslikava vsebine ne more biti prazna",
"max_alias_exceeded": "Preseženo največje število aliasov",
"max_mailbox_exceeded": "Preseženo največje število poštnih predalov (%d od %d)",
"maxquota_empty": "Največja kvota na poštni predal ne more biti 0",
"mysql_error": "Napaka MySQL: %s",
"network_host_invalid": "Nepravilno omrežje ali gostitel: %s",
"next_hop_interferes": "% moti naslednji skok %s",
"next_hop_interferes_any": "Obstoječi naslednji skok moti %s",
"nginx_reload_failed": "Ponovni zagon Nginx ni uspel: %s",
"no_user_defined": "Uporabnik ni določen",
"object_exists": "Objekt %s že obstaja",
"object_is_not_numeric": "Vrednost %s ni numerična",
"password_empty": "Geslo ne sme biti prazno",
"password_mismatch": "Potrditev gesla se ne ujema z geslom",
"policy_list_from_exists": "Zapis z tem imenom že obstaja",
"policy_list_from_invalid": "Zapis ima nepravilno obliko",
"private_key_error": "Napaka zasebnega ključa: %s",
"pushover_key": "Pushover ključ ni v pravilni obliki",
"pushover_token": "Pushover token ni v pravilni obliki",
"quota_not_0_not_numeric": "Quota mora biti število in večje ali enako 0",
"recipient_map_entry_exists": "Preslikava prejemnika \"%s\" že obstaja",
"redis_error": "Napaka Redis: %s",
"relayhost_invalid": "Vnos preslikave %s ni pravilen",
"resource_invalid": "Ime vira je neveljavno",
"rl_timeframe": "Časovni okvir za rate limit je nepravilen",
"rspamd_ui_pw_length": "Rspamd UI geslo mora biti dolgo vsaj 6 znakov",
"script_empty": "Script ne more biti prazen",
"sender_acl_invalid": "Vrednost ACL pošiljatelja %s ni veljavna",
"set_acl_failed": "Ni uspelo nastaviti ACL",
"settings_map_invalid": "ID preslikave nastavitev %s ni veljaven",
"sieve_error": "Napaka Sieve parserja: %s",
"spam_learn_error": "Napaka pri učenju spama: %s",
"subject_empty": "Predmet ne sme biti prazno",
"targetd_not_found": "Ciljna domena %s ni bila najdena",
"targetd_relay_domain": "Ciljna domena %s je relay domena",
"template_exists": "Predloga %s že obstaja",
"template_id_invalid": "ID predloge %s ni veljaven",
"template_name_invalid": "Ime predloge ni veljavno",
"text_empty": "Besedilo ne sme biti prazno",
"tfa_token_invalid": "Neveljaven token TFA",
"tls_policy_map_entry_exists": "Vpis preslikave TLS \"%s\" že obstaja",
"tls_policy_map_parameter_invalid": "Parameter politike ni pravilen",
"totp_verification_failed": "Neuspešno preverjanje TOTP",
"transport_dest_exists": "Cilj transporta \"%s\" že obstaja",
"webauthn_verification_failed": "Preverjanje WebAuthn ni uspelo: %s",
"webauthn_publickey_failed": "Na izbranem avtentikatorju ni shranjenega javnega ključa",
"webauthn_username_failed": "Izbrani avtentikator pripada drugemu uporabniškemu računu",
"unknown": "Pojavila se je neznana napaka",
"unknown_tfa_method": "Neznana metoda TFA",
"unlimited_quota_acl": "Neomejena kvota je prepovedana z ACL",
"username_invalid": "Uporabniško ime %s ne more biti uporabljeno",
"value_missing": "Prosim vnesite vse vrednosti",
"yotp_verification_failed": "Preverjanje Yubico OTP ni uspelo: %s",
"temp_error": "Začasna napaka",
"cors_invalid_method": "Navedena neveljavna Allow metoda",
"cors_invalid_origin": "Naveden neveljaven Allow-Origin",
"invalid_recipient_map_new": "Naveden neveljaven nov prejemnik: %s"
},
"debug": {
"containers_info": "Informacije o vsebniku (containerju)",
"architecture": "Arhitektura",
"chart_this_server": "Diagram (ta strežnik)",
"container_running": "Aktiven",
"container_disabled": "Ustavljen ali onemogočen",
"container_stopped": "Ustavljen",
"cores": "Jedra",
"current_time": "Sistemski čas",
"disk_usage": "Zasedenost diska",
"docs": "Dokumenti",
"error_show_ip": "Ni mogoče preveriti javnega IP naslova",
"external_logs": "Zunanji dnevniki",
"last_modified": "Nazadnje spremenjeno",
"history_all_servers": "Zgodovina (vsi strežniki)",
"in_memory_logs": "In-memory dnevniki",
"jvm_memory_solr": "JVM zasedenost spomina",
"service": "Servis",
"show_ip": "Prikaži javni IP",
"size": "Velikost",
"solr_dead": "Solr se zaganja, je onemogočen ali se je ustavil.",
"solr_status": "Status Solr",
"started_at": "Zagnano ob",
"started_on": "Zagnano na",
"static_logs": "Statični dnevniki",
"success": "Uspešno",
"system_containers": "Sistem in Containerji",
"timezone": "Časovni pas",
"uptime": "Čas delovanja",
"update_available": "Posodobitev je na voljo",
"no_update_available": "Sistem je na najnovejši verziji",
"update_failed": "Ni mogoče preveriti za posodobitve",
"username": "Uporabniško ime",
"wip": "Trenutno v delu"
},
"datatables": {
"infoFiltered": "(filtrirano od _MAX_ skupaj zapisov)",
"collapse_all": "Strni vse",
"decimal": ",",
"emptyTable": "Ni podatkov",
"expand_all": "Razširi vse",
"info": "Prikazano _START_ do _END_ od _TOTAL_ zapisov",
"infoEmpty": "Prikazano 0 do 0 od 0 zapisov",
"thousands": ".",
"lengthMenu": "Prikaži _MENU_ zapise",
"loadingRecords": "Nalaganje...",
"processing": "Prosim počakajte...",
"search": "Iskanje:",
"zeroRecords": "Ni ujemajočih zapisov",
"paginate": {
"first": "Prva",
"last": "Zadnja",
"previous": "Prejšnja",
"next": "Naslednja"
},
"aria": {
"sortAscending": ": aktivirajte za razvrstitev stolpca naraščajoče",
"sortDescending": ": aktivirajte za razvrstitev stolpca padajoče"
}
},
"diagnostics": {
"cname_from_a": "Vrednost pridobljena iz A/AAAA zapisa. To je podprto, če zapis kaže na pravilen resurs.",
"dns_records": "DNS zapisi",
"dns_records_24hours": "Prosim upoštevajte, da lahko traja do 24 ur da se spremembe v DNS pravilno prikažejo na tej strani. Namen je da lahko enostavno vidite, kako konfigurirati svoje DNS zapise in preverite ali so vaši zapisi pravilno shranjeni v DNS.",
"dns_records_data": "Pravilni podatki",
"dns_records_docs": "Prosim preverite tudi <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentacijo</a>.",
"dns_records_name": "Ime",
"dns_records_status": "Trenutno stanje",
"dns_records_type": "Vrsta",
"optional": "Ta zapis je opcijski."
},
"edit": {
"acl": "ACL (Dovoljenje)",
"active": "Aktivno"
}
}

View File

@@ -41,7 +41,7 @@
"alias_domain": "Alias doména",
"alias_domain_info": "<small>Len platné mená domén (oddelené čiarkou).</small>",
"app_name": "Meno aplikácie",
"app_passwd_protocols": "Povolené protokoly pre heslá aplikácií",
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
"app_password": "Pridať heslo aplikácie",
"automap": "Skúsiť automaticky mapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti preposielania",
@@ -107,7 +107,6 @@
"username": "Používateľské meno",
"validate": "Overiť",
"validation_success": "Úspešne overené",
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
"tags": "Štítky"
},
"admin": {
@@ -148,6 +147,8 @@
"ays": "Naozaj chcete pokračovať?",
"ban_list_info": "Zoznam zakázaných IP je zobrazený nižšie: <b>sieť (zostávajúci čas zákazu) - [akcia]</b>.<br />IP adresy zaradené na unban budú odstránené z aktívneho zoznamu v priebehu niekoľkých sekúnd.<br />Červené položky zobrazujú permanentné blokovanie.",
"change_logo": "Zmeniť logo",
"logo_normal_label": "Normálne",
"logo_dark_label": "Inverzné pre tmavý režim",
"configuration": "Konfigurácia",
"convert_html_to_text": "Konvertovať HTML do obyčajného textu",
"credentials_transport_warning": "<b>Upozornenie</b>: Pridaním ďalšieho záznamu do transportnej mapy bude mať za následok aktualizovanie údajov pre všetky záznamy so zhodným ďalším skokom.",
@@ -207,6 +208,9 @@
"include_exclude": "Zahrnúť/Vylúčiť",
"include_exclude_info": "Ak nič nevyberiete tak bude adresované <b>všetkým schránkam</b>",
"includes": "Zahrnúť týchto príjemcov",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je vypnutá. Môžete ju zapnúť v ponuke<br> <strong>Systém > Konfigurácia > Možnosti > Prispôsobiť</strong>",
"ip_check_opt_in": "Prihlásiť sa k používaniu služby tretej strany <strong>ipv4.mailcow.email</strong> a <strong>ipv6.mailcow.email</strong> za účelom zistenia externých IP adries.",
"is_mx_based": "Na základe MX",
"last_applied": "Naposledy aplikované",
"license_info": "Licencia nie je potrebná, ale napomáha ďalšiemu vývoju.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registrujte váš GUID tu</a> alebo <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Objednávka podpory\">zakúpte si podporu pre vašu mailcow inštaláciu.</a>",
@@ -233,6 +237,7 @@
"oauth2_renew_secret": "Vygenerovať nový tajný kľuč",
"oauth2_revoke_tokens": "Odobrať všetky tokeny klienta",
"optional": "voliteľné",
"options": "Možnosti",
"password": "Heslo",
"password_length": "Dĺžka hesla",
"password_policy": "Politika hesiel",
@@ -440,6 +445,9 @@
"target_domain_invalid": "Cieľová doména %s je neplatná",
"targetd_not_found": "Cieľová doména %s sa nenašla",
"targetd_relay_domain": "Cieľová doména %s je posielaná ďalej (relay)",
"template_exists": "Šablóna %s už existuje",
"template_id_invalid": "Šablóna ID %s je neplatná",
"template_name_invalid": "Názov šablóny je neplatný",
"temp_error": "Dočasná chyba",
"text_empty": "Text nemôže byť prázdny",
"tfa_token_invalid": "Neplatný TFA token",
@@ -529,7 +537,7 @@
"allowed_protocols": "Povolené protokoly",
"app_name": "Meno aplikácie",
"app_passwd": "Heslo aplikácie",
"app_passwd_protocols": "Povolené protokoly pre heslá aplikácií",
"app_passwd_protocols": "Povolené protokoly",
"automap": "Skúsiť automapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti preposielania",
"bcc_dest_format": "Cieľ kópie musí byť jedna platná emailová adresa. Pokiaľ potrebujete posielať kópie na viac adries, vytvorte Alias a použite ho tu.",
@@ -614,8 +622,8 @@
"sieve_desc": "Krátky popis",
"sieve_type": "Typ filtru",
"skipcrossduplicates": "Preskočiť duplikované správy naprieč priečinkami (akceptuje sa prvý nález)",
"sogo_access": "Udeliť priamy prístup k prihláseniu do služby SOGo",
"sogo_access_info": "Jednotné prihlásenie (SSO) z mail UI zostáva funkčné. Toto nastavenie nemá vplyv na prístup k všetkým ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
"sogo_access": "Prideliť priame prihlásenie do SOGo",
"sogo_access_info": "Jednotné prihlásenie z používateľského mail rozhrania zostáva funkčné. Toto nastavenie nemá vplyv na prístup k ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
"sogo_visible": "Alias je viditeľný v SOGo",
"sogo_visible_info": "Táto voľba ovplyvňuje len objekty, ktoré dokážu byť zobrazené v SOGo (zdieľané alebo nezdieľané alias adresy ukazujúc na minimálne jednu lokálnu mailovú schránku). Ak je skrytý, alias nebude prezentovaný ako voliteľný odosielateľ v SOGo.",
"spam_alias": "Vytvoriť alebo zmeniť časovo limitované alias adresy",
@@ -631,10 +639,7 @@
"title": "Upraviť objekt",
"unchanged_if_empty": "Ak nemeníte, nechajte prázdne",
"username": "Používateľské meno",
"validate_save": "Validovať a uložiť",
"sogo_access": "Prideliť priame prihlásenie do SOGo",
"sogo_access_info": "Jednotné prihlásenie z používateľského mail rozhrania zostáva funkčné. Toto nastavenie nemá vplyv na prístup k ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
"app_passwd_protocols": "Povolené protokoly"
"validate_save": "Validovať a uložiť"
},
"fido2": {
"confirm": "Potvrdiť",
@@ -671,6 +676,7 @@
"apps": "Aplikácie",
"debug": "Systémové informácie",
"email": "E-Mail",
"mailcow_system": "Systém",
"mailcow_config": "Konfigurácia",
"quarantine": "Karanténa",
"restart_netfilter": "Reštartovať netfilter",
@@ -706,6 +712,7 @@
"add_mailbox": "Pridať mailovú schránku",
"add_recipient_map_entry": "Pridať mapu príjemcu",
"add_resource": "Pridať zdroj",
"add_template": "Pridať šablónu",
"add_tls_policy_map": "Pridať TLS mapu pravidiel",
"address_rewriting": "Prepisovanie adries",
"alias": "Alias",
@@ -748,6 +755,7 @@
"domain": "Doména",
"domain_admins": "Administrátori domény",
"domain_aliases": "Alias domény",
"domain_templates": "Šablóny domén",
"domain_quota": "Kvóta",
"domain_quota_total": "Celkové kvóta domény",
"domains": "Domény",
@@ -776,6 +784,7 @@
"mailbox_defaults": "Predvolené nastavenia",
"mailbox_defaults_info": "Definuje predvolené nastavenia pre nové schránky.",
"mailbox_defquota": "Predvolená veľkosť schránky",
"mailbox_templates": "Šablóny schránok",
"mailbox_quota": "Max. veľkosť schránky",
"mailboxes": "Mailové schránky",
"max_aliases": "Max. počet aliasov",
@@ -843,6 +852,8 @@
"table_size_show_n": "Zobraziť %s položiek",
"target_address": "Cieľová adresa",
"target_domain": "Cieľová doména",
"templates": "Šablóny",
"template": "Šablóna",
"tls_enforce_in": "Vynútiť TLS pre prichádzajúcu poštu",
"tls_enforce_out": "Vynútiť TLS pre odchádzajúcu poštu",
"tls_map_dest": "Cieľ",
@@ -1007,6 +1018,9 @@
"settings_map_added": "Pridaná mapa nastavení",
"settings_map_removed": "Odstránená mapa nastavení ID %s",
"sogo_profile_reset": "SOGo profil pre používateľa %s resetovaný",
"template_added": "Pridaná šablóna %s",
"template_modified": "Zmeny šablóny %s boli uložené",
"template_removed": "Šablóna ID %s bola odstránená",
"tls_policy_map_entry_deleted": "Položka mapy TLS pravidiel %s vymazaná",
"tls_policy_map_entry_saved": "Položka mapy TLS pravidiel \"%s\" uložená",
"ui_texts": "Zmeny v UI textoch uložené",
@@ -1065,9 +1079,9 @@
"apple_connection_profile": "Apple konfiguračný profil",
"apple_connection_profile_complete": "Tento profil zahŕňa IMAP a SMTP parametre, ako aj CalDAV (kalendáre) a CardDAV (kontakty) pre zariadenia Apple.",
"apple_connection_profile_mailonly": "Tento profil zahŕňa IMAP a SMTP konfiguračné parametre pre zariadenia Apple.",
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej poštovej schránke.",
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej mail schránke.",
"change_password": "Zmeniť heslo",
"change_password_hint_app_passwords": "Váš účet má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
"change_password_hint_app_passwords": "Vaše konto má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
"clear_recent_successful_connections": "Vymazať nedávne úspešné prihlásenia",
"client_configuration": "Zobraziť konfiguračné pokyny pre emailových klientov a smartfóny",
"create_app_passwd": "Vytvoriť heslo aplikácie",
@@ -1078,7 +1092,7 @@
"delete_ays": "Potvrďte zmazanie.",
"direct_aliases": "Priame alias adresy",
"direct_aliases_desc": "Priame aliasy sú ovplyvnené spam filtrom a nastavením TLS pravidiel.",
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie má pod kontrolou Váš správca. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \" Prihláste sa do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii.",
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie kontroluje administrátor. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \"Prihlásenie do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii.",
"eas_reset": "Resetovať medzipamäť u ActiveSync zariadení",
"eas_reset_help": "Vo väčšine prípadov, reset medzipamäte ActiveSync pomôže opravit nefunkčný profil.<br><b>Pozor:</b> Všetky potrebné dáta budú opäť stiahnuté!",
"eas_reset_now": "Reset ActiveSync",
@@ -1202,10 +1216,7 @@
"weeks": "týždne",
"with_app_password": "s heslom aplikácie",
"year": "rok",
"years": "rokov",
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej mail schránke.",
"change_password_hint_app_passwords": "Vaše konto má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie kontroluje administrátor. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \"Prihlásenie do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii."
"years": "rokov"
},
"warning": {
"cannot_delete_self": "Nemožno vymazať prihláseného používateľa",

View File

@@ -1,61 +1,113 @@
{
"acl": {
"alias_domains": "Takma alan adı ekle",
"alias_domains": "Alias domain ekle",
"app_passwds": "Uygulama şifrelerini yönet",
"delimiter_action": "Sınırlama işlemi",
"domain_relayhost": "Bir alan adı için relayhost sunucusunu değiştir",
"eas_reset": "EAS cihazlarını sıfırla",
"mailbox_relayhost": "Bir posta kutusunun relayhost sunucularını değiştir",
"pushover": "Bildirim",
"quarantine": "Karantina işlemleri",
"quarantine_attachments": "Ekleri karantinaya al",
"quarantine_notification": "Karantina bildirimlerini değiştir",
"smtp_ip_access": "SMTP sunucularının değiştirilmesine izin ver",
"sogo_access": "SOGo erişiminin yönetilmesine izin ver",
"bcc_maps": "BCC haritası",
"delimiter_action": "Sınırlandırma işlemi",
"domain_desc": "Alan adııklamasını değiştir",
"extend_sender_acl": "Gönderenin acl'sini harici adreslere göre genişletmeye izin ver",
"spam_policy": "Engellenenler / İzin verilenler",
"filters": "Fitreler"
"domain_relayhost": "Alan adı için relayhost sunucusunu değiştir",
"eas_reset": "EAS cihazlarını sıfırla",
"extend_sender_acl": "Gönderen ACL'sini harici adreslerle genişletmeye izin ver",
"filters": "Filtreler",
"login_as": "E-posta kullanıcısı olarak giriş yapın",
"mailbox_relayhost": "Bir e-posta için relayhost'u değiştirin",
"prohibited": "ACL tarafından yasaklandı",
"protocol_access": "Protokol erişimini değiştirin",
"pushover": "Pushover",
"quarantine": "Karantina eylemleri",
"quarantine_attachments": "Ekleri karantinaya al",
"quarantine_category": "Karantina bildirim kategorisini değiştir",
"quarantine_notification": "Karantina bildirimlerini değiştir",
"ratelimit": "Rate limit",
"recipient_maps": "Alıcı haritaları",
"smtp_ip_access": "SMTP için izin verilen host değerlerini değiştirme",
"sogo_access": "SOGo erişiminin yönetilmesine izin verin",
"sogo_profile_reset": "SOGo profilini sıfırla",
"spam_alias": "Geçici alias değerleri",
"spam_policy": "Kara Liste/Beyaz Liste",
"spam_score": "Spam skoru",
"syncjobs": "Görevleri senkronize et",
"tls_policy": "TLS ilkesi",
"unlimited_quota": "E-postalar için sınırsız kota"
},
"add": {
"activate_filter_warn": "Aktif edilirse diğer tüm filtreler devre dışı bırakılacak.",
"active": "Aktif",
"add": "Ekle",
"add_domain_only": "Sadece alan adı ekle",
"alias_address": "Takma ad adres(leri)",
"add_domain_restart": "Alan adını ekleyin ve SOGo'yu yeniden başlatın",
"alias_address": "Takma adres",
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
"alias_domain": "Takma alan adı",
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayırın).</small>",
"backup_mx_options": "İletme ayarları",
"delete2": "Kaynakta olmayan hedefteki mesajları sil",
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayrılmış).</small>",
"app_name": "Uygulama adı",
"app_password": "Uygulama şifresi ekle",
"app_passwd_protocols": "Uygulama şifresi için izin verilen protokoller",
"automap": "Klasörleri otomatik eşleştirmeyi deneyin (\"Gönderilen postalar\", \"Gönderilen\" => \"Gönderilen\" vb.)",
"backup_mx_options": "Relay ayarları",
"bcc_dest_format": "BCC hedefi tek bir geçerli e-posta adresi olmalıdır.<br>Bir kopyayı birden fazla adrese göndermeniz gerekiyorsa, bir takma ad oluşturun ve bunu burada kullanın.",
"comment_info": "Özel bir yorum kullanıcı tarafından görülemezken, herkese açık bir yorum kullanıcının genel görünümünde üzerine gelindiğinde tooltip olarak gösterilir",
"custom_params": "Özel parametreler",
"custom_params_hint": "Doğru: --param=xy, yanlış: --param xy",
"delete1": "Tamamlandığında kaynaktan sil",
"delete2": "Eğer kaynakta yoksa hedefteki mesajları sil",
"delete2duplicates": "Hedefteki kopyaları sil",
"disable_login": "Giriş yapmaya izin verme ( Gelen mailler yine de kabul edilir)",
"description": "Açıklama",
"destination": "Hedef",
"disable_login": "Giriş yapmaya izin verme (gelen mailler yine de kabul edilir)",
"domain": "Alan adı",
"domain_matches_hostname": "Alan adı %s ana bilgisayar adıyla eşleşiyor",
"add_domain_restart": "Alan adı ekleyin ve SOGo'yu yeniden başlatın",
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
"domain_quota_m": "Toplam alan adı kotası (MiB)",
"enc_method": "Şifreleme yöntemi",
"exclude": "Hariç tutma kuralı (regex)",
"full_name": "Tam isim",
"gal": "Global Adres Listesi",
"gal_info": "GAL bir alan alanının tüm nesnelerini içerir ve herhangi bir kullanıcı tarafından düzenlenemez. Eğer devre dışı bırakırsanız SOGo üzerindeki free/busy bilgileri kaybolur! <b>Değişiklikleri uygulamak için SOGo'yu yeniden başlatın.</b>",
"generate": "oluştur",
"goto_ham": "Ham olarak<span class=\"text-success\"><b>işaretle</b></span>",
"goto_null": "Postaları sessizce çöpe at",
"goto_null": "Postaları çöpe at",
"goto_spam": "Spam olarak<span class=\"text-danger\"><b>işaretle</b></span>",
"hostname": "Ana sunucu",
"inactive": "İnaktif",
"kind": "Tür",
"mailbox_quota_m": "Posta kutusu başına maksimum kota (MiB)",
"max_aliases": "Maksimum olası takma adı",
"max_mailboxes": "Maksimum olası posta kutusu",
"nexthop": "Sonraki atlama",
"mailbox_quota_def": "Varsayılan e-posta kotası",
"mailbox_quota_m": "E-posta başına maksimum kota (MiB)",
"mailbox_username": "Kullanıcı adı (e-posta adresinin sol kısmı)",
"max_aliases": "Maksimum takma adı limiti",
"max_mailboxes": "Maksimum e-posta hesabı",
"mins_interval": "Sorgulama döngüsü (dakika)",
"multiple_bookings": "Birden fazla rezervasyon",
"nexthop": "Next hop",
"password": "Şifre",
"password_repeat": "Şifre (tekrar)",
"port": "Port",
"public_comment": "Genel yorum",
"relay_all": "Tüm alıcılara ilet",
"relay_all_info": "Eğer <b>hiçbir</b> alıcıya iletilmemesini seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
"relay_domain": "Bu alan adını ilet",
"relay_transport_info": "<div class=\"label label-info\">Bilgi</div> Bu etki alanı için özel bir hedef için aktarım eşlemeleri tanımlayabilirsiniz. Ayarlanmazsa, bir MX araması yapılacaktır.",
"relay_unknown_only": "Yalnızca mevcut olmayan posta kutularını ilet. Mevcut posta kutuları yerel olarak teslim edilecektir.",
"relayhost_wrapped_tls_info": "Lütfen TLS ile örtülmüş portları <b> kullanmayın</b> (çoğu 465 portunda çalışır).<br>\nÖrtülmemiş port kullan ve STARTTLS üzerinden yayınla. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
"skipcrossduplicates": "Klasörler arasında yinelenen mesajları atlayın (ilk mesaj seçilir)",
"post_domain_add": "SOGo konteynerinin \"sogo-mailcow\" yeni bir alan adı eklendikten sonra yeniden başlatılması gerekiyor!<br><br>Ayrıca alan adlarının DNS yapılandırması da gözden geçirilmelidir. DNS yapılandırması onaylandıktan sonra, yeni etki alanınız için otomatik olarak sertifika oluşturmak üzere \"acme-mailcow\"u yeniden başlatın (autoconfig.&lt;domain&gt;, autodiscover.&lt;domain&gt;).<br>Bu adım isteğe bağlıdır ve her 24 saatte bir yeniden denenecektir.",
"private_comment": "Özel not",
"public_comment": "Herkese açık not",
"quota_mb": "Kota (MiB)",
"relay_all": "Tüm alıcıları aktar",
"relay_all_info": "↪ Tüm alıcıları <b>aktarmamayı</b> seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
"relay_domain": "Bu etki alanını aktarın",
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Bilgi</div> Bu alan adı nezdinde özel bir hedef için transport haritası tanımlayabilirsiniz. Eğer ayarlanmazsa, MX taraması yapılacaktır.",
"relay_unknown_only": "Yalnızca mevcut olmayan e-postaları aktarın. Mevcut e-postalar local olarak teslim edilecektir.",
"relayhost_wrapped_tls_info": "Lütfen örtülü TLS portları <b>kullanmayın</b> (çoğunlukla 465 portunda kullanılır).<br>\r\nÖrtülmüş olmayan herhangi bir bağlantı noktası kullanın ve STARTTLS üzerinden yayınlayın. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
"select": "Lütfen seçiniz...",
"select_domain": "Lütfen önce alan adı seçin",
"sieve_desc": "Kısa açıklama",
"sieve_type": "Filtre türü",
"skipcrossduplicates": "Klasörler arasında yinelenen(kopya) mesajları es geçin (ilk gelen mail baz alınır)",
"subscribeall": "Tüm klasörlere abone ol",
"syncjob": "Senkronizasyon görevi ekle",
"syncjob_hint": "Parolaların düz metin olarak kaydedilmesi gerektiğini unutmayın!",
"tags": "Etiketler",
"target_address": "Adreslere git",
"target_address_info": "<small>Tam e-posta adres(leri) girin ( virgülle ayırın).</small>",
"target_address_info": "<small>Tam e-posta adresleri (virgülle ayrılmış).</small>",
"target_domain": "Hedef alan adı",
"timeout1": "Uzak ana bilgisayara bağlantısı zaman aşımına uğradı",
"timeout2": "Yerel ana bilgisayara bağlantı zaman aşımına uğradı"
"timeout1": "Uzak ana bilgisayara bağlantı için zaman aşımı",
"timeout2": "Yerel ana bilgisayara bağlantı için zaman aşımı",
"username": "Kullanıcı Adı",
"validate": "Doğrula",
"validation_success": "Doğrulama başarılı"
},
"admin": {
"action": "İşlem",
@@ -82,5 +134,18 @@
"f2b_ban_time": "Yasaklama süresi (saniye)",
"f2b_max_attempts": "Maksimum giriş denemesi",
"f2b_retry_window": "Maksimum girişim için deneme pencere(leri)"
},
"warning": {
"cannot_delete_self": "Cannot delete logged in user",
"domain_added_sogo_failed": "Alan adı eklendi ancak SOGo yeniden başlatılamadı, lütfen sunucu log kayıtlarını kontrol edin.",
"dovecot_restart_failed": "Dovecot yeniden başlatılamadı, lütfen log kayıtlarını kontrol edin",
"fuzzy_learn_error": "Fuzzy hash hatayı öğrendi: %s",
"hash_not_found": "Hash bulunamadı veya zaten silinmiş",
"ip_invalid": "Geçersiz IP atlandı: %s",
"is_not_primary_alias": "Birincil olmayan alias %s atlandı",
"no_active_admin": "Son etkin yönetici devre dışı bırakılamaz",
"quota_exceeded_scope": "Domain kotasııldı: Bu domain kapsamında yalnızca sınırsız e-posta oluşturulabilir!",
"session_token": "Form token geçersiz: Token uyuşmadı",
"session_ua": "Form token geçersiz: User-Agent doğrulama hatası"
}
}

View File

@@ -107,7 +107,8 @@
"kind": "Вид",
"delete1": "Видалити з джерела після завершення",
"delete2duplicates": "Видалити дублікати на місці призначення",
"domain_quota_m": "Загальна квота домену (МіБ)"
"domain_quota_m": "Загальна квота домену (МіБ)",
"dry": "Імітувати синхронізацію"
},
"admin": {
"access": "Налаштування доступу",
@@ -650,10 +651,13 @@
"auth_user": "{= auth_user =} - Аутентифіковане ім'я користувача, вказане MTA",
"from_user": "{= from_user =} - З користувацької частини envelope, наприклад, для \"moo@mailcow.tld\" повертає \"moo\"",
"from_addr": "{= from_addr =} - З адресної частини envelope",
"from_domain": "{= from_domain =} - З доменної частини envelope"
"from_domain": "{= from_domain =} - З доменної частини envelope",
"custom": "{= foo =} - Якщо поштова скринька має кастомний атрибут \"foo\" зі значенням \"bar\", то повертається \"bar\""
},
"domain_footer_html": "Нижній колонтитул HTML",
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул"
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул",
"custom_attributes": "Користувацькі атрибути",
"mbox_exclude": "Виключити поштові скриньки"
},
"fido2": {
"confirm": "Підтвердити",
@@ -1248,7 +1252,9 @@
"tls_policy_warning": "<strong>Попередження:</strong> якщо ви увімкнете примусове шифрування пошти, ви можете зіткнутися з втратою листів.<br>Повідомлення, які не відповідають політиці, будуть відкидатися з повідомленням поштовим сервером про серйозний збій.<br>Цей параметр застосовується до вашої основної адреси електронної пошти (логіну), усім особистим псевдонімам та псевдонімам доменів. Маються на увазі лише псевдоніми <b>з однією поштовою скринькою</b>, як одержувач.",
"year": "рік",
"years": "років",
"pushover_sound": "Звук"
"pushover_sound": "Звук",
"value": "Значення",
"attribute": "Атрибут"
},
"warning": {
"domain_added_sogo_failed": "Домен був доданий, але перезавантажити SOGo не вдалося, будь ласка, перевірте журнали сервера.",

View File

@@ -24,6 +24,7 @@
<script type='text/javascript'>
var lang_user = {{ lang_user|raw }};
var lang_admin = {{ lang_admin|raw }};
var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}';
var pagination_size = Math.trunc('{{ pagination_size }}');

View File

@@ -168,7 +168,7 @@
<label class="control-label col-sm-2">{{ lang.edit.ratelimit }}</label>
<div class="col-sm-10">
<div class="input-group">
<input name="rl_value" type="number" value="{{ rl.value }}" autocomplete="off" class="form-control placeholder="{{ lang.ratelimit.disabled }}">
<input name="rl_value" type="number" value="{{ rl.value }}" autocomplete="off" class="form-control" placeholder="{{ lang.ratelimit.disabled }}">
<select name="rl_frame" class="form-control">
{% include 'mailbox/rl-frame.twig' %}
</select>
@@ -285,23 +285,41 @@
{{ lang.edit.domain_footer_info_vars.from_user }}
{{ lang.edit.domain_footer_info_vars.from_name }}
{{ lang.edit.domain_footer_info_vars.from_addr }}
{{ lang.edit.domain_footer_info_vars.from_domain }}</pre>
{{ lang.edit.domain_footer_info_vars.from_domain }}
{{ lang.edit.domain_footer_info_vars.custom }}</pre>
<form class="form-horizontal mt-4" data-id="domain_footer">
<div class="row mb-4">
<label class="control-label col-sm-2" for="mbox_exclude">{{ lang.edit.mbox_exclude }}</label>
<div class="col-sm-10">
<select data-live-search="true" data-width="100%" style="width:100%" id="editMboxExclude" name="mbox_exclude" size="10" multiple>
{% for mailbox in mailboxes %}
<option value="{{ mailbox }}" {% if mailbox in domain_footer.mbox_exclude %}selected{% endif %}>
{{ mailbox }}
</option>
{% endfor %}
{% for alias in aliases %}
<option data-subtext="Alias" value="{{ alias }}" {% if alias in domain_footer.mbox_exclude %}selected{% endif %}>
{{ alias }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="row mb-2">
<label class="control-label col-sm-2" for="domain_footer_html">{{ lang.edit.domain_footer_html }}:</label>
<div class="col-sm-10">
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_html" name="footer_html">{{ domain_footer.html }}</textarea>
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_html" name="html">{{ domain_footer.html }}</textarea>
</div>
</div>
<div class="row mb-4">
<label class="control-label col-sm-2" for="domain_footer_plain">{{ lang.edit.domain_footer_plain }}:</label>
<div class="col-sm-10">
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_plain" name="footer_plain">{{ domain_footer.plain }}</textarea>
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_plain" name="plain">{{ domain_footer.plain }}</textarea>
</div>
</div>
<div class="row">
<div class="offset-sm-2 col-sm-10">
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="domain_footer" data-item="domain_footer" data-api-url='edit/domain-wide-footer' data-api-attr='{"domain":"{{ domain }}"}' href="#">{{ lang.edit.save }}</button>
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="domain_footer" data-item="{{ domain }}" data-api-url='edit/domain/footer' data-api-attr='{}' href="#">{{ lang.edit.save }}</button>
</div>
</div>
</form>

View File

@@ -5,6 +5,7 @@
<div id="mailbox-content" class="responsive-tabs">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#medit">{{ lang.edit.mailbox }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mattr">{{ lang.edit.custom_attributes }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mpushover">{{ lang.edit.pushover }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#macl">{{ lang.edit.acl }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mrl">{{ lang.edit.ratelimit }}</button></li>
@@ -275,6 +276,37 @@
</div>
</div>
</div>
<div id="mattr" class="tab-pane fade" role="tabpanel" aria-labelledby="mailbox-attr">
<div class="card mb-4">
<div class="card-header d-flex d-md-none fs-5">
<button class="btn flex-grow-1 text-start" data-bs-target="#collapse-tab-mattr" data-bs-toggle="collapse" aria-controls="collapse-tab-mattr">
{{ lang.edit.mailbox }} <span class="badge bg-info table-lines"></span>
</button>
</div>
<div id="collapse-tab-mattr" class="card-body collapse show" data-bs-parent="#mailbox-content">
<form class="form-inline" data-id="mbox_attr" role="form" method="post">
<table class="table table-condensed" style="white-space: nowrap;" id="mbox_attr_table">
<tr>
<th>{{ lang.user.attribute }}</th>
<th>{{ lang.user.value }}</th>
<th style="width:100px;">&nbsp;</th>
</tr>
{% for key, val in result.custom_attributes %}
<tr>
<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="attribute" required value="{{ key }}"></td>
<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="value" required value="{{ val }}"></td>
<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">{{ lang.admin.remove_row }}</a></td>
</tr>
{% endfor %}
</table>
<p><div class="btn-group">
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" data-action="edit_selected" data-item="{{ mailbox }}" data-id="mbox_attr" data-api-url='edit/mailbox/custom-attribute' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" type="button" id="add_mbox_attr_row">{{ lang.admin.add_row }}</button>
</div></p>
</form>
</div>
</div>
</div>
<div id="mpushover" class="tab-pane fade" role="tabpanel" aria-labelledby="mailbox-pushover">
<div class="card mb-4">
<div class="card-header d-flex d-md-none fs-5">

View File

@@ -11,6 +11,7 @@
<input type="hidden" value="0" name="skipcrossduplicates">
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="subscribeall">
<input type="hidden" value="0" name="dry">
<div class="row mb-2">
<label class="control-label col-sm-2" for="host1">{{ lang.edit.hostname }}</label>
<div class="col-sm-10">
@@ -95,7 +96,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2" for="custom_params">{{ lang.add.custom_params }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--dry --some-param=xy --other-param=yx">
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--some-param=xy --other-param=yx">
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
</div>
</div>
@@ -141,6 +142,13 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry"{% if result.dry == '1' %} checked{% endif %}> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@@ -955,7 +955,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end" for="custom_params">{{ lang.add.custom_params }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="custom_params" placeholder="--dry --some-param=xy --other-param=yx">
<input type="text" class="form-control" name="custom_params" placeholder="--some-param=xy --other-param=yx">
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
</div>
</div>
@@ -994,13 +994,20 @@
</div>
</div>
</div>
<div class="row mb-4">
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="subscribeall" checked> {{ lang.add.subscribeall }} (--subscribeall)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry"> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@@ -167,6 +167,13 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry" checked> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@@ -58,12 +58,11 @@ services:
- redis
clamd-mailcow:
image: mailcow/clamd:1.62
image: mailcow/clamd:1.63
restart: always
depends_on:
unbound-mailcow:
condition: service_healthy
restart: true
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
@@ -78,7 +77,7 @@ services:
- clamd
rspamd-mailcow:
image: mailcow/rspamd:1.92
image: mailcow/rspamd:1.94
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -219,7 +218,7 @@ services:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:1.25
image: mailcow/dovecot:1.26
depends_on:
- mysql-mailcow
dns:
@@ -305,7 +304,6 @@ services:
condition: service_started
unbound-mailcow:
condition: service_healthy
restart: true
volumes:
- ./data/hooks/postfix:/hooks:Z
- ./data/conf/postfix:/opt/postfix/conf:z
@@ -528,7 +526,7 @@ services:
- watchdog
dockerapi-mailcow:
image: mailcow/dockerapi:2.05
image: mailcow/dockerapi:2.06
security_opt:
- label=disable
restart: always

View File

@@ -26,10 +26,10 @@ for bin in openssl curl docker git awk sha1sum grep cut; do
done
if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep "^2." > /dev/null 2>&1; then
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
COMPOSE_VERSION=native
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
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"
else
@@ -41,8 +41,8 @@ elif docker-compose > /dev/null 2>&1; then
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
COMPOSE_VERSION=standalone
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
echo -e "\e[33mFound Docker Compose Standalone.\e[0m"
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
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
@@ -553,4 +553,4 @@ else
echo -e "\e[33mCannot determine current git repository version...\e[0m"
fi
detect_bad_asn
detect_bad_asn

View File

@@ -0,0 +1,122 @@
#!/usr/bin/env python3
# Based on github.com/diafygi/acme-tiny, original copyright:
# Copyright Daniel Roesler, under MIT license, see LICENSE at github.com/diafygi/acme-tiny
import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
try:
from urllib.request import urlopen, Request # Python 3
except ImportError: # pragma: no cover
from urllib2 import urlopen, Request # Python 2
DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
LOGGER = logging.getLogger(__name__)
LOGGER.addHandler(logging.StreamHandler())
LOGGER.setLevel(logging.INFO)
def get_id(account_key, log=LOGGER, directory_url=DEFAULT_DIRECTORY_URL, contact=None):
directory, acct_headers, alg, jwk = None, None, None, None # global variables
# helper functions - base64 encode for jose spec
def _b64(b):
return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")
# helper function - run external commands
def _cmd(cmd_list, stdin=None, cmd_input=None, err_msg="Command Line Error"):
proc = subprocess.Popen(cmd_list, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate(cmd_input)
if proc.returncode != 0:
raise IOError("{0}\n{1}".format(err_msg, err))
return out
# helper function - make request and automatically parse json response
def _do_request(url, data=None, err_msg="Error", depth=0):
try:
resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json", "User-Agent": "acme-tiny"}))
resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
except IOError as e:
resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
code, headers = getattr(e, "code", None), {}
try:
resp_data = json.loads(resp_data) # try to parse json results
except ValueError:
pass # ignore json parsing errors
if depth < 100 and code == 400 and resp_data['type'] == "urn:ietf:params:acme:error:badNonce":
raise IndexError(resp_data) # allow 100 retrys for bad nonces
if code not in [200, 201, 204]:
raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
return resp_data, code, headers
# helper function - make signed requests
def _send_signed_request(url, payload, err_msg, depth=0):
payload64 = "" if payload is None else _b64(json.dumps(payload).encode('utf8'))
new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
protected = {"url": url, "alg": alg, "nonce": new_nonce}
protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']})
protected64 = _b64(json.dumps(protected).encode('utf8'))
protected_input = "{0}.{1}".format(protected64, payload64).encode('utf8')
out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key], stdin=subprocess.PIPE, cmd_input=protected_input, err_msg="OpenSSL Error")
data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
try:
return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth)
except IndexError: # retry bad nonces (they raise IndexError)
return _send_signed_request(url, payload, err_msg, depth=(depth + 1))
# helper function - poll until complete
def _poll_until_not(url, pending_statuses, err_msg):
result, t0 = None, time.time()
while result is None or result['status'] in pending_statuses:
assert (time.time() - t0 < 3600), "Polling timeout" # 1 hour timeout
time.sleep(0 if result is None else 2)
result, _, _ = _send_signed_request(url, None, err_msg)
return result
# parse account key to get public key
log.info("Parsing account key...")
out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"], err_msg="OpenSSL Error")
pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'), re.MULTILINE|re.DOTALL).groups()
pub_exp = "{0:x}".format(int(pub_exp))
pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
alg, jwk = "RS256", {
"e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
"kty": "RSA",
"n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
}
accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())
# get the ACME directory of urls
log.info("Getting directory...")
directory, _, _ = _do_request(directory_url, err_msg="Error getting directory")
log.info("Directory found!")
# create account and get the global key identifier
log.info("Registering account...")
reg_payload = {"termsOfServiceAgreed": True} if contact is None else {"termsOfServiceAgreed": True, "contact": contact}
account, code, acct_headers = _send_signed_request(directory['newAccount'], reg_payload, "Error registering")
log.info("Registered!" if code == 201 else "Already registered!")
return acct_headers['Location']
def main(argv=None):
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent("""\
Generate a CAA record for Mailcow.
Example Usage: python mailcow_gencaa.py --account-key data/assets/ssl/acme/account.pem
""")
)
parser.add_argument("--account-key", required=True, help="path to your Let's Encrypt account private key")
parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors")
parser.add_argument("--directory-url", default=DEFAULT_DIRECTORY_URL, help="certificate authority directory url, default is Let's Encrypt")
parser.add_argument("--contact", metavar="CONTACT", default=None, nargs="*", help="Contact details (e.g. mailto:aaa@bbb.com) for your account-key")
args = parser.parse_args(argv)
LOGGER.setLevel(args.quiet or LOGGER.level)
id = get_id(args.account_key, log=LOGGER, directory_url=args.directory_url, contact=args.contact)
print("Use this as your CAA record:")
print('issue 128 "letsencrypt.org;accounturi={}"'.format(id))
if __name__ == "__main__": # pragma: no cover
main(sys.argv[1:])

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
NEXTCLOUD_VERSION=27.1.2
NEXTCLOUD_VERSION=27.1.4
echo -ne "Checking prerequisites..."
sleep 1
@@ -106,6 +106,10 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
exit 1
else
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
NC_SUBD=$(docker exec -i -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:get overwritehost)
mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
fi
elif [[ ${NC_INSTALL} == "y" ]]; then

150
update.sh
View File

@@ -32,51 +32,44 @@ prefetch_images() {
}
docker_garbage() {
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
IMGS_TO_DELETE=()
for container in $(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml"); do
REPOSITORY=${container/:*}
TAG=${container/*:}
V_MAIN=${container/*.}
V_SUB=${container/*.}
EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }')
for existing_tag in ${EXISTING_TAGS[@]}; do
V_MAIN_EXISTING=${existing_tag/*.}
V_SUB_EXISTING=${existing_tag/*.}
# Not an integer
[[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue
[[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue
if [[ $V_MAIN_EXISTING == "latest" ]]; then
echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
declare -A IMAGES_INFO
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)
if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then
continue
else
IMGS_TO_DELETE+=("$ID")
IMAGES_INFO["$ID"]="$REPOSITORY:$TAG"
fi
done
done
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
echo "Run the following command to delete unused image tags:"
echo
echo " docker rmi ${IMGS_TO_DELETE[*]}"
echo
if [ ! $FORCE ]; then
read -r -p "Do you want to delete old image tags right now? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
docker rmi ${IMGS_TO_DELETE[*]}
echo "The following unused mailcow images were found:"
for id in "${IMGS_TO_DELETE[@]}"; do
echo " ${IMAGES_INFO[$id]} ($id)"
done
if [ ! $FORCE ]; then
read -r -p "Do you want to delete them to free up some space? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
docker rmi ${IMGS_TO_DELETE[*]}
else
echo "OK, skipped."
fi
else
echo "OK, skipped."
echo "Running in forced mode! Force removing old mailcow images..."
docker rmi ${IMGS_TO_DELETE[*]}
fi
else
echo "Running image removal without extra confirmation due to force mode."
docker rmi ${IMGS_TO_DELETE[*]}
fi
echo -e "\e[32mFurther cleanup...\e[0m"
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
echo -e "\e[32mFurther cleanup...\e[0m"
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
fi
}
@@ -178,11 +171,11 @@ remove_obsolete_nginx_ports() {
detect_docker_compose_command(){
if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep "2." > /dev/null 2>&1; then
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose"
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
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
sleep 2
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
@@ -196,8 +189,8 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=standalone
COMPOSE_COMMAND="docker-compose"
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
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
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"
@@ -893,45 +886,54 @@ done
# git remote set-url origin https://github.com/mailcow/mailcow-dockerized
DEFAULT_REPO=https://github.com/mailcow/mailcow-dockerized
CURRENT_REPO=$(git remote get-url origin)
CURRENT_REPO=$(git config --get remote.origin.url)
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"
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
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
fi
else
echo "Running in forced mode... setting Repo to default!"
git remote set-url origin $DEFAULT_REPO
fi
fi
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
[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
git add -u
git commit -am "Before update on ${DATE}" > /dev/null
echo -e "\e[32mFetching updated code from remote...\e[0m"
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}"
# Need to use a variable to not pass return codes of if checks
MERGE_RETURN=$?
if [[ ${MERGE_RETURN} == 128 ]]; then
echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
exit 1
elif [[ ${MERGE_RETURN} == 1 ]]; then
echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
git add -A
git commit -m "After update on ${DATE}" > /dev/null
git checkout .
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
elif [[ ${MERGE_RETURN} != 0 ]]; then
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
echo
echo "Run $COMPOSE_COMMAND up -d to restart your stack without updates or try again after fixing the mentioned errors."
exit 1
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
[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
git add -u
git commit -am "Before update on ${DATE}" > /dev/null
echo -e "\e[32mFetching updated code from remote...\e[0m"
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}"
# Need to use a variable to not pass return codes of if checks
MERGE_RETURN=$?
if [[ ${MERGE_RETURN} == 128 ]]; then
echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
exit 1
elif [[ ${MERGE_RETURN} == 1 ]]; then
echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
git add -A
git commit -m "After update on ${DATE}" > /dev/null
git checkout .
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
elif [[ ${MERGE_RETURN} != 0 ]]; then
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
echo
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
echo -e "\e[33mDEVELOPER MODE: Not creating a git diff and commiting it to prevent development stuff within a backup diff...\e[0m"
fi
echo -e "\e[32mFetching new images, if any...\e[0m"