Merge remote-tracking branch 'origin/staging' into feat/f2b-banlist

This commit is contained in:
FreddleSpl0it
2023-12-11 12:27:14 +01:00
161 changed files with 7009 additions and 2185 deletions

View File

@@ -1,7 +1,7 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: ❓ Community-driven support - name: ❓ Community-driven support
url: https://mailcow.github.io/mailcow-dockerized-docs/#get-support url: https://docs.mailcow.email/#get-support
about: Please use the community forum for questions or assistance about: Please use the community forum for questions or assistance
- name: 🚨 Report a security vulnerability - name: 🚨 Report a security vulnerability
url: https://www.servercow.de/anfrage?lang=en url: https://www.servercow.de/anfrage?lang=en

View File

@@ -12,7 +12,7 @@
"baseBranches": ["staging"], "baseBranches": ["staging"],
"enabledManagers": ["github-actions", "regex", "docker-compose"], "enabledManagers": ["github-actions", "regex", "docker-compose"],
"ignorePaths": [ "ignorePaths": [
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**" "data\/web\/inc\/lib\/vendor\/**"
], ],
"regexManagers": [ "regexManagers": [
{ {
@@ -24,7 +24,7 @@
{ {
"fileMatch": ["(^|/)Dockerfile[^/]*$"], "fileMatch": ["(^|/)Dockerfile[^/]*$"],
"matchStrings": [ "matchStrings": [
"#\\srenovate:\\sdatasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s(ENV|ARG) .*?_VERSION=(?<currentValue>.*)\\s" "#\\srenovate:\\sdatasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?( extractVersion=(?<extractVersion>.*?))?\\s(ENV|ARG) .*?_VERSION=(?<currentValue>.*)\\s"
] ]
} }
] ]

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,24 +11,25 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }} username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }}
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }} password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64
file: data/Dockerfiles/backup/Dockerfile file: data/Dockerfiles/backup/Dockerfile
push: true push: true
tags: mailcow/backup:latest tags: mailcow/backup:latest

View File

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

2
.gitignore vendored
View File

@@ -36,6 +36,8 @@ data/conf/postfix/extra.cf
data/conf/postfix/sni.map data/conf/postfix/sni.map
data/conf/postfix/sni.map.db data/conf/postfix/sni.map.db
data/conf/postfix/sql data/conf/postfix/sql
data/conf/postfix/dns_blocklists.cf
data/conf/postfix/dnsbl_reply.map
data/conf/rspamd/custom/* data/conf/rspamd/custom/*
data/conf/rspamd/local.d/* data/conf/rspamd/local.d/*
data/conf/rspamd/override.d/* data/conf/rspamd/override.d/*

View File

@@ -3,7 +3,7 @@ When a problem occurs, then always for a reason! What you want to do in such a c
1. Read your logs; follow them to see what the reason for your problem is. 1. Read your logs; follow them to see what the reason for your problem is.
2. Follow the leads given to you in your logfiles and start investigating. 2. Follow the leads given to you in your logfiles and start investigating.
3. Restarting the troubled service or the whole stack to see if the problem persists. 3. Restarting the troubled service or the whole stack to see if the problem persists.
4. Read the [documentation](https://mailcow.github.io/mailcow-dockerized-docs/) of the troubled service and search its bugtracker for your problem. 4. Read the [documentation](https://docs.mailcow.email/) of the troubled service and search its bugtracker for your problem.
5. Search our [issues](https://github.com/mailcow/mailcow-dockerized/issues) for your problem. 5. Search our [issues](https://github.com/mailcow/mailcow-dockerized/issues) for your problem.
6. [Create an issue](https://github.com/mailcow/mailcow-dockerized/issues/new/choose) over at our GitHub repository if you think your problem might be a bug or a missing feature you badly need. But please make sure, that you include **all the logs** and a full description to your problem. 6. [Create an issue](https://github.com/mailcow/mailcow-dockerized/issues/new/choose) over at our GitHub repository if you think your problem might be a bug or a missing feature you badly need. But please make sure, that you include **all the logs** and a full description to your problem.
7. Ask your questions in our community-driven [support channels](https://mailcow.github.io/mailcow-dockerized-docs/#community-support-and-chat). 7. Ask your questions in our community-driven [support channels](https://docs.mailcow.email/#community-support-and-chat).

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/) [![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) [![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? ## Want to support mailcow?
@@ -13,7 +15,7 @@ Or just spread the word: moo.
## Info, documentation and support ## Info, documentation and support
Please see [the official documentation](https://mailcow.github.io/mailcow-dockerized-docs/) for installation and support instructions. 🐄 Please see [the official documentation](https://docs.mailcow.email/) for installation and support instructions. 🐄
🐛 **If you found a critical security issue, please mail us to [info at servercow.de](mailto:info@servercow.de).** 🐛 **If you found a critical security issue, please mail us to [info at servercow.de](mailto:info@servercow.de).**
@@ -25,7 +27,9 @@ Please see [the official documentation](https://mailcow.github.io/mailcow-docker
[Telegram mailcow Off-Topic channel](https://t.me/mailcowOfftopic) [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. 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. 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.1-1_base 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 \ RUN apk upgrade --no-cache \
&& apk add --update --no-cache \ && apk add --update --no-cache \

View File

@@ -198,8 +198,8 @@ async def handle_pubsub_messages(channel: aioredis.client.PubSub):
while True: while True:
try: try:
async with async_timeout.timeout(1): async with async_timeout.timeout(60):
message = await channel.get_message(ignore_subscribe_messages=True) message = await channel.get_message(ignore_subscribe_messages=True, timeout=30)
if message is not None: if message is not None:
# Parse message # Parse message
data_json = json.loads(message['data'].decode('utf-8')) data_json = json.loads(message['data'].decode('utf-8'))
@@ -244,7 +244,7 @@ async def handle_pubsub_messages(channel: aioredis.client.PubSub):
else: else:
dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json)) dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json))
await asyncio.sleep(0.01) await asyncio.sleep(0.0)
except asyncio.TimeoutError: except asyncio.TimeoutError:
pass pass

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
FROM alpine:3.17 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
WORKDIR /app
ENV XTABLES_LIBDIR /usr/lib/xtables ENV XTABLES_LIBDIR /usr/lib/xtables
ENV PYTHON_IPTABLES_XTABLES_VERSION 12 ENV PYTHON_IPTABLES_XTABLES_VERSION 12
ENV IPTABLES_LIBDIR /usr/lib ENV IPTABLES_LIBDIR /usr/lib
@@ -14,10 +16,13 @@ RUN apk add --virtual .build-deps \
iptables \ iptables \
ip6tables \ ip6tables \
xtables-addons \ xtables-addons \
nftables \
tzdata \ tzdata \
py3-pip \ py3-pip \
py3-nftables \
musl-dev \ musl-dev \
&& pip3 install --ignore-installed --upgrade pip \ && pip3 install --ignore-installed --upgrade pip \
jsonschema \
python-iptables \ python-iptables \
redis \ redis \
ipaddress \ ipaddress \
@@ -26,5 +31,10 @@ RUN apk add --virtual .build-deps \
# && pip3 install --upgrade pip python-iptables==0.13.0 redis ipaddress dnspython \ # && pip3 install --upgrade pip python-iptables==0.13.0 redis ipaddress dnspython \
COPY server.py / COPY modules /app/modules
CMD ["python3", "-u", "/server.py"] COPY main.py /app/
COPY ./docker-entrypoint.sh /app/
RUN chmod +x /app/docker-entrypoint.sh
CMD ["/bin/sh", "-c", "/app/docker-entrypoint.sh"]

View File

@@ -0,0 +1,29 @@
#!/bin/sh
backend=iptables
nft list table ip filter &>/dev/null
nftables_found=$?
iptables -L -n &>/dev/null
iptables_found=$?
if [ $nftables_found -lt $iptables_found ]; then
backend=nftables
fi
if [ $nftables_found -gt $iptables_found ]; then
backend=iptables
fi
if [ $nftables_found -eq 0 ] && [ $nftables_found -eq $iptables_found ]; then
nftables_lines=$(nft list ruleset | wc -l)
iptables_lines=$(iptables-save | wc -l)
if [ $nftables_lines -gt $iptables_lines ]; then
backend=nftables
else
backend=iptables
fi
fi
exec python -u /app/main.py $backend

View File

@@ -13,11 +13,15 @@ from threading import Thread
from threading import Lock from threading import Lock
import redis import redis
import json import json
import iptc
import dns.resolver import dns.resolver
import dns.exception import dns.exception
import uuid import uuid
from modules.Logger import Logger
from modules.IPTables import IPTables
from modules.NFTables import NFTables
# connect to redis
while True: while True:
try: try:
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '') redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
@@ -32,34 +36,33 @@ while True:
time.sleep(3) time.sleep(3)
else: else:
break break
pubsub = r.pubsub() pubsub = r.pubsub()
# rename fail2ban to netfilter
if r.exists('F2B_LOG'):
r.rename('F2B_LOG', 'NETFILTER_LOG')
# globals
WHITELIST = [] WHITELIST = []
BLACKLIST= [] BLACKLIST= []
bans = {} bans = {}
quit_now = False quit_now = False
exit_code = 0 exit_code = 0
lock = Lock() lock = Lock()
def log(priority, message):
tolog = {}
tolog['time'] = int(round(time.time()))
tolog['priority'] = priority
tolog['message'] = message
r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
print(message)
def logWarn(message): # init Logger
log('warn', message) logger = Logger(r)
# init backend
backend = sys.argv[1]
if backend == "nftables":
logger.logInfo('Using NFTables backend')
tables = NFTables("MAILCOW", logger)
else:
logger.logInfo('Using IPTables backend')
tables = IPTables("MAILCOW", logger)
def logCrit(message):
log('crit', message)
def logInfo(message):
log('info', message)
def refreshF2boptions(): def refreshF2boptions():
global f2boptions global f2boptions
@@ -80,7 +83,7 @@ def refreshF2boptions():
try: try:
f2boptions = json.loads(r.get('F2B_OPTIONS')) f2boptions = json.loads(r.get('F2B_OPTIONS'))
except ValueError: except ValueError:
print('Error loading F2B options: F2B_OPTIONS is not json') logger.logCrit('Error loading F2B options: F2B_OPTIONS is not json')
quit_now = True quit_now = True
exit_code = 2 exit_code = 2
@@ -123,44 +126,23 @@ def refreshF2bregex():
f2bregex = {} f2bregex = {}
f2bregex = json.loads(r.get('F2B_REGEX')) f2bregex = json.loads(r.get('F2B_REGEX'))
except ValueError: except ValueError:
print('Error loading F2B options: F2B_REGEX is not json') logger.logCrit('Error loading F2B options: F2B_REGEX is not json')
quit_now = True quit_now = True
exit_code = 2 exit_code = 2
if r.exists('F2B_LOG'): def get_ip(address):
r.rename('F2B_LOG', 'NETFILTER_LOG') ip = ipaddress.ip_address(address)
if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped:
def mailcowChainOrder(): ip = ip.ipv4_mapped
global lock if ip.is_private or ip.is_loopback:
global quit_now return False
global exit_code
while not quit_now: return ip
time.sleep(10)
with lock:
filter4_table = iptc.Table(iptc.Table.FILTER)
filter6_table = iptc.Table6(iptc.Table6.FILTER)
filter4_table.refresh()
filter6_table.refresh()
for f in [filter4_table, filter6_table]:
forward_chain = iptc.Chain(f, 'FORWARD')
input_chain = iptc.Chain(f, 'INPUT')
for chain in [forward_chain, input_chain]:
target_found = False
for position, item in enumerate(chain.rules):
if item.target.name == 'MAILCOW':
target_found = True
if position > 2:
logCrit('Error in %s chain order: MAILCOW on position %d, restarting container' % (chain.name, position))
quit_now = True
exit_code = 2
if not target_found:
logCrit('Error in %s chain: MAILCOW target not found, restarting container' % (chain.name))
quit_now = True
exit_code = 2
def ban(address): def ban(address):
global f2boptions global f2boptions
global lock global lock
refreshF2boptions() refreshF2boptions()
BAN_TIME = int(f2boptions['ban_time']) BAN_TIME = int(f2boptions['ban_time'])
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment']) BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
@@ -169,23 +151,18 @@ def ban(address):
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4']) NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6']) NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6'])
ip = ipaddress.ip_address(address) ip = get_ip(address)
if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: if not ip: return
ip = ip.ipv4_mapped address = str(ip)
address = str(ip)
if ip.is_private or ip.is_loopback:
return
self_network = ipaddress.ip_network(address) self_network = ipaddress.ip_network(address)
with lock: with lock:
temp_whitelist = set(WHITELIST) temp_whitelist = set(WHITELIST)
if temp_whitelist: if temp_whitelist:
for wl_key in temp_whitelist: for wl_key in temp_whitelist:
wl_net = ipaddress.ip_network(wl_key, False) wl_net = ipaddress.ip_network(wl_key, False)
if wl_net.overlaps(self_network): if wl_net.overlaps(self_network):
logInfo('Address %s is whitelisted by rule %s' % (self_network, wl_net)) logger.logInfo('Address %s is whitelisted by rule %s' % (self_network, wl_net))
return return
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False) net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
@@ -194,60 +171,44 @@ def ban(address):
if not net in bans: if not net in bans:
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0} bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
current_attempt = time.time()
if current_attempt - bans[net]['last_attempt'] > RETRY_WINDOW:
bans[net]['attempts'] = 0
bans[net]['attempts'] += 1 bans[net]['attempts'] += 1
bans[net]['last_attempt'] = time.time() bans[net]['last_attempt'] = current_attempt
if bans[net]['attempts'] >= MAX_ATTEMPTS: if bans[net]['attempts'] >= MAX_ATTEMPTS:
cur_time = int(round(time.time())) cur_time = int(round(time.time()))
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter'] NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 )) logger.logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
if type(ip) is ipaddress.IPv4Address and int(f2boptions['manage_external']) != 1: if type(ip) is ipaddress.IPv4Address and int(f2boptions['manage_external']) != 1:
with lock: with lock:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW') tables.banIPv4(net)
rule = iptc.Rule()
rule.src = net
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
elif int(f2boptions['manage_external']) != 1: elif int(f2boptions['manage_external']) != 1:
with lock: with lock:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW') tables.banIPv6(net)
rule = iptc.Rule6()
rule.src = net
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME) r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
else: else:
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)) logger.logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
def unban(net): def unban(net):
global lock global lock
if not net in bans: if not net in bans:
logInfo('%s is not banned, skipping unban and deleting from queue (if any)' % net) logger.logInfo('%s is not banned, skipping unban and deleting from queue (if any)' % net)
r.hdel('F2B_QUEUE_UNBAN', '%s' % net) r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
return return
logInfo('Unbanning %s' % net)
logger.logInfo('Unbanning %s' % net)
if type(ipaddress.ip_network(net)) is ipaddress.IPv4Network: if type(ipaddress.ip_network(net)) is ipaddress.IPv4Network:
with lock: with lock:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW') tables.unbanIPv4(net)
rule = iptc.Rule()
rule.src = net
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule in chain.rules:
chain.delete_rule(rule)
else: else:
with lock: with lock:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW') tables.unbanIPv6(net)
rule = iptc.Rule6()
rule.src = net
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule in chain.rules:
chain.delete_rule(rule)
r.hdel('F2B_ACTIVE_BANS', '%s' % net) r.hdel('F2B_ACTIVE_BANS', '%s' % net)
r.hdel('F2B_QUEUE_UNBAN', '%s' % net) r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
if net in bans: if net in bans:
@@ -257,73 +218,44 @@ def unban(net):
def permBan(net, unban=False): def permBan(net, unban=False):
global f2boptions global f2boptions
global lock global lock
is_unbanned = False
is_banned = False
if type(ipaddress.ip_network(net, strict=False)) is ipaddress.IPv4Network: if type(ipaddress.ip_network(net, strict=False)) is ipaddress.IPv4Network:
with lock: with lock:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW') if unban:
rule = iptc.Rule() is_unbanned = tables.unbanIPv4(net)
rule.src = net elif int(f2boptions['manage_external']) != 1:
target = iptc.Target(rule, "REJECT") is_banned = tables.banIPv4(net)
rule.target = target
if rule not in chain.rules and not unban and int(f2boptions['manage_external']) != 1:
logCrit('Add host/network %s to blacklist' % net)
chain.insert_rule(rule)
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
elif rule in chain.rules and unban:
logCrit('Remove host/network %s from blacklist' % net)
chain.delete_rule(rule)
r.hdel('F2B_PERM_BANS', '%s' % net)
else: else:
with lock: with lock:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW') if unban:
rule = iptc.Rule6() is_unbanned = tables.unbanIPv6(net)
rule.src = net elif int(f2boptions['manage_external']) != 1:
target = iptc.Target(rule, "REJECT") is_banned = tables.banIPv6(net)
rule.target = target
if rule not in chain.rules and not unban and int(f2boptions['manage_external']) != 1:
logCrit('Add host/network %s to blacklist' % net)
chain.insert_rule(rule)
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
elif rule in chain.rules and unban:
logCrit('Remove host/network %s from blacklist' % net)
chain.delete_rule(rule)
r.hdel('F2B_PERM_BANS', '%s' % net)
def quit(signum, frame):
global quit_now if is_unbanned:
quit_now = True r.hdel('F2B_PERM_BANS', '%s' % net)
logger.logCrit('Removed host/network %s from blacklist' % net)
elif is_banned:
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
logger.logCrit('Added host/network %s to blacklist' % net)
def clear(): def clear():
global lock global lock
logInfo('Clearing all bans') logger.logInfo('Clearing all bans')
for net in bans.copy(): for net in bans.copy():
unban(net) unban(net)
with lock: with lock:
filter4_table = iptc.Table(iptc.Table.FILTER) tables.clearIPv4Table()
filter6_table = iptc.Table6(iptc.Table6.FILTER) tables.clearIPv6Table()
for filter_table in [filter4_table, filter6_table]:
filter_table.autocommit = False
forward_chain = iptc.Chain(filter_table, "FORWARD")
input_chain = iptc.Chain(filter_table, "INPUT")
mailcow_chain = iptc.Chain(filter_table, "MAILCOW")
if mailcow_chain in filter_table.chains:
for rule in mailcow_chain.rules:
mailcow_chain.delete_rule(rule)
for rule in forward_chain.rules:
if rule.target.name == 'MAILCOW':
forward_chain.delete_rule(rule)
for rule in input_chain.rules:
if rule.target.name == 'MAILCOW':
input_chain.delete_rule(rule)
filter_table.delete_chain("MAILCOW")
filter_table.commit()
filter_table.refresh()
filter_table.autocommit = True
r.delete('F2B_ACTIVE_BANS') r.delete('F2B_ACTIVE_BANS')
r.delete('F2B_PERM_BANS') r.delete('F2B_PERM_BANS')
pubsub.unsubscribe() pubsub.unsubscribe()
def watch(): def watch():
logInfo('Watching Redis channel F2B_CHANNEL') logger.logInfo('Watching Redis channel F2B_CHANNEL')
pubsub.subscribe('F2B_CHANNEL') pubsub.subscribe('F2B_CHANNEL')
global quit_now global quit_now
@@ -344,10 +276,10 @@ def watch():
ip = ipaddress.ip_address(addr) ip = ipaddress.ip_address(addr)
if ip.is_private or ip.is_loopback: if ip.is_private or ip.is_loopback:
continue continue
logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data'])) logger.logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
ban(addr) ban(addr)
except Exception as ex: except Exception as ex:
logWarn('Error reading log line from pubsub: %s' % ex) logger.logWarn('Error reading log line from pubsub: %s' % ex)
quit_now = True quit_now = True
exit_code = 2 exit_code = 2
@@ -355,87 +287,19 @@ def snat4(snat_target):
global lock global lock
global quit_now global quit_now
def get_snat4_rule():
rule = iptc.Rule()
rule.src = os.getenv('IPV4_NETWORK', '172.22.1') + '.0/24'
rule.dst = '!' + rule.src
target = rule.create_target("SNAT")
target.to_source = snat_target
match = rule.create_match("comment")
match.comment = f'{int(round(time.time()))}'
return rule
while not quit_now: while not quit_now:
time.sleep(10) time.sleep(10)
with lock: with lock:
try: tables.snat4(snat_target, os.getenv('IPV4_NETWORK', '172.22.1') + '.0/24')
table = iptc.Table('nat')
table.refresh()
chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False
new_rule = get_snat4_rule()
if not chain.rules:
# if there are no rules in the chain, insert the new rule directly
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
for position, rule in enumerate(chain.rules):
if not hasattr(rule.target, 'parameter'):
continue
match = all((
new_rule.get_src() == rule.get_src(),
new_rule.get_dst() == rule.get_dst(),
new_rule.target.parameters == rule.target.parameters,
new_rule.target.name == rule.target.name
))
if position == 0:
if not match:
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
if match:
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
chain.delete_rule(rule)
table.commit()
table.autocommit = True
except:
print('Error running SNAT4, retrying...')
def snat6(snat_target): def snat6(snat_target):
global lock global lock
global quit_now global quit_now
def get_snat6_rule():
rule = iptc.Rule6()
rule.src = os.getenv('IPV6_NETWORK', 'fd4d:6169:6c63:6f77::/64')
rule.dst = '!' + rule.src
target = rule.create_target("SNAT")
target.to_source = snat_target
return rule
while not quit_now: while not quit_now:
time.sleep(10) time.sleep(10)
with lock: with lock:
try: tables.snat6(snat_target, os.getenv('IPV6_NETWORK', 'fd4d:6169:6c63:6f77::/64'))
table = iptc.Table6('nat')
table.refresh()
chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False
if get_snat6_rule() not in chain.rules:
logInfo('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat6_rule().src, snat_target))
chain.insert_rule(get_snat6_rule())
table.commit()
else:
for position, item in enumerate(chain.rules):
if item == get_snat6_rule():
if position != 0:
chain.delete_rule(get_snat6_rule())
table.commit()
table.autocommit = True
except:
print('Error running SNAT6, retrying...')
def autopurge(): def autopurge():
while not quit_now: while not quit_now:
@@ -456,6 +320,17 @@ def autopurge():
if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME: if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME:
unban(net) unban(net)
def mailcowChainOrder():
global lock
global quit_now
global exit_code
while not quit_now:
time.sleep(10)
with lock:
quit_now, exit_code = tables.checkIPv4ChainOrder()
if quit_now: return
quit_now, exit_code = tables.checkIPv6ChainOrder()
def isIpNetwork(address): def isIpNetwork(address):
try: try:
ipaddress.ip_network(address, False) ipaddress.ip_network(address, False)
@@ -463,7 +338,6 @@ def isIpNetwork(address):
return False return False
return True return True
def genNetworkList(list): def genNetworkList(list):
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
hostnames = [] hostnames = []
@@ -479,12 +353,12 @@ def genNetworkList(list):
try: try:
answer = resolver.resolve(qname=hostname, rdtype=rdtype, lifetime=3) answer = resolver.resolve(qname=hostname, rdtype=rdtype, lifetime=3)
except dns.exception.Timeout: except dns.exception.Timeout:
logInfo('Hostname %s timedout on resolve' % hostname) logger.logInfo('Hostname %s timedout on resolve' % hostname)
break break
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
continue continue
except dns.exception.DNSException as dnsexception: except dns.exception.DNSException as dnsexception:
logInfo('%s' % dnsexception) logger.logInfo('%s' % dnsexception)
continue continue
for rdata in answer: for rdata in answer:
hostname_ips.append(rdata.to_text()) hostname_ips.append(rdata.to_text())
@@ -504,7 +378,7 @@ def whitelistUpdate():
with lock: with lock:
if Counter(new_whitelist) != Counter(WHITELIST): if Counter(new_whitelist) != Counter(WHITELIST):
WHITELIST = new_whitelist WHITELIST = new_whitelist
logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST)) logger.logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
time.sleep(60.0 - ((time.time() - start_time) % 60.0)) time.sleep(60.0 - ((time.time() - start_time) % 60.0))
def blacklistUpdate(): def blacklistUpdate():
@@ -520,7 +394,7 @@ def blacklistUpdate():
addban = set(new_blacklist).difference(BLACKLIST) addban = set(new_blacklist).difference(BLACKLIST)
delban = set(BLACKLIST).difference(new_blacklist) delban = set(BLACKLIST).difference(new_blacklist)
BLACKLIST = new_blacklist BLACKLIST = new_blacklist
logInfo('Blacklist was changed, it has %s entries' % len(BLACKLIST)) logger.logInfo('Blacklist was changed, it has %s entries' % len(BLACKLIST))
if addban: if addban:
for net in addban: for net in addban:
permBan(net=net) permBan(net=net)
@@ -529,40 +403,20 @@ def blacklistUpdate():
permBan(net=net, unban=True) permBan(net=net, unban=True)
time.sleep(60.0 - ((time.time() - start_time) % 60.0)) time.sleep(60.0 - ((time.time() - start_time) % 60.0))
def initChain(): def quit(signum, frame):
# Is called before threads start, no locking global quit_now
print("Initializing mailcow netfilter chain") quit_now = True
# IPv4
if not iptc.Chain(iptc.Table(iptc.Table.FILTER), "MAILCOW") in iptc.Table(iptc.Table.FILTER).chains:
iptc.Table(iptc.Table.FILTER).create_chain("MAILCOW")
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
rule = iptc.Rule()
rule.src = '0.0.0.0/0'
rule.dst = '0.0.0.0/0'
target = iptc.Target(rule, "MAILCOW")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
# IPv6
if not iptc.Chain(iptc.Table6(iptc.Table6.FILTER), "MAILCOW") in iptc.Table6(iptc.Table6.FILTER).chains:
iptc.Table6(iptc.Table6.FILTER).create_chain("MAILCOW")
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
rule = iptc.Rule6()
rule.src = '::/0'
rule.dst = '::/0'
target = iptc.Target(rule, "MAILCOW")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
if __name__ == '__main__': if __name__ == '__main__':
refreshF2boptions() refreshF2boptions()
# In case a previous session was killed without cleanup # In case a previous session was killed without cleanup
clear() clear()
# Reinit MAILCOW chain # Reinit MAILCOW chain
initChain() # Is called before threads start, no locking
logger.logInfo("Initializing mailcow netfilter chain")
tables.initChainIPv4()
tables.initChainIPv6()
watch_thread = Thread(target=watch) watch_thread = Thread(target=watch)
watch_thread.daemon = True watch_thread.daemon = True

View File

@@ -0,0 +1,213 @@
import iptc
import time
class IPTables:
def __init__(self, chain_name, logger):
self.chain_name = chain_name
self.logger = logger
def initChainIPv4(self):
if not iptc.Chain(iptc.Table(iptc.Table.FILTER), self.chain_name) in iptc.Table(iptc.Table.FILTER).chains:
iptc.Table(iptc.Table.FILTER).create_chain(self.chain_name)
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
rule = iptc.Rule()
rule.src = '0.0.0.0/0'
rule.dst = '0.0.0.0/0'
target = iptc.Target(rule, self.chain_name)
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
def initChainIPv6(self):
if not iptc.Chain(iptc.Table6(iptc.Table6.FILTER), self.chain_name) in iptc.Table6(iptc.Table6.FILTER).chains:
iptc.Table6(iptc.Table6.FILTER).create_chain(self.chain_name)
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
rule = iptc.Rule6()
rule.src = '::/0'
rule.dst = '::/0'
target = iptc.Target(rule, self.chain_name)
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
def checkIPv4ChainOrder(self):
filter_table = iptc.Table(iptc.Table.FILTER)
filter_table.refresh()
return self.checkChainOrder(filter_table)
def checkIPv6ChainOrder(self):
filter_table = iptc.Table6(iptc.Table6.FILTER)
filter_table.refresh()
return self.checkChainOrder(filter_table)
def checkChainOrder(self, filter_table):
err = False
exit_code = None
forward_chain = iptc.Chain(filter_table, 'FORWARD')
input_chain = iptc.Chain(filter_table, 'INPUT')
for chain in [forward_chain, input_chain]:
target_found = False
for position, item in enumerate(chain.rules):
if item.target.name == self.chain_name:
target_found = True
if position > 2:
self.logger.logCrit('Error in %s chain: %s target not found, restarting container' % (chain.name, self.chain_name))
err = True
exit_code = 2
if not target_found:
self.logger.logCrit('Error in %s chain: %s target not found, restarting container' % (chain.name, self.chain_name))
err = True
exit_code = 2
return err, exit_code
def clearIPv4Table(self):
self.clearTable(iptc.Table(iptc.Table.FILTER))
def clearIPv6Table(self):
self.clearTable(iptc.Table6(iptc.Table6.FILTER))
def clearTable(self, filter_table):
filter_table.autocommit = False
forward_chain = iptc.Chain(filter_table, "FORWARD")
input_chain = iptc.Chain(filter_table, "INPUT")
mailcow_chain = iptc.Chain(filter_table, self.chain_name)
if mailcow_chain in filter_table.chains:
for rule in mailcow_chain.rules:
mailcow_chain.delete_rule(rule)
for rule in forward_chain.rules:
if rule.target.name == self.chain_name:
forward_chain.delete_rule(rule)
for rule in input_chain.rules:
if rule.target.name == self.chain_name:
input_chain.delete_rule(rule)
filter_table.delete_chain(self.chain_name)
filter_table.commit()
filter_table.refresh()
filter_table.autocommit = True
def banIPv4(self, source):
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), self.chain_name)
rule = iptc.Rule()
rule.src = source
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule in chain.rules:
return False
chain.insert_rule(rule)
return True
def banIPv6(self, source):
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), self.chain_name)
rule = iptc.Rule6()
rule.src = source
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule in chain.rules:
return False
chain.insert_rule(rule)
return True
def unbanIPv4(self, source):
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), self.chain_name)
rule = iptc.Rule()
rule.src = source
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
return False
chain.delete_rule(rule)
return True
def unbanIPv6(self, source):
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), self.chain_name)
rule = iptc.Rule6()
rule.src = source
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
return False
chain.delete_rule(rule)
return True
def snat4(self, snat_target, source):
try:
table = iptc.Table('nat')
table.refresh()
chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False
new_rule = self.getSnat4Rule(snat_target, source)
if not chain.rules:
# if there are no rules in the chain, insert the new rule directly
self.logger.logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
for position, rule in enumerate(chain.rules):
if not hasattr(rule.target, 'parameter'):
continue
match = all((
new_rule.get_src() == rule.get_src(),
new_rule.get_dst() == rule.get_dst(),
new_rule.target.parameters == rule.target.parameters,
new_rule.target.name == rule.target.name
))
if position == 0:
if not match:
self.logger.logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
if match:
self.logger.logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
chain.delete_rule(rule)
table.commit()
table.autocommit = True
return True
except:
self.logger.logCrit('Error running SNAT4, retrying...')
return False
def snat6(self, snat_target, source):
try:
table = iptc.Table6('nat')
table.refresh()
chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False
new_rule = self.getSnat6Rule(snat_target, source)
if new_rule not in chain.rules:
self.logger.logInfo('Added POSTROUTING rule for source network %s to SNAT target %s' % (new_rule.src, snat_target))
chain.insert_rule(new_rule)
else:
for position, item in enumerate(chain.rules):
if item == new_rule:
if position != 0:
chain.delete_rule(new_rule)
table.commit()
table.autocommit = True
except:
self.logger.logCrit('Error running SNAT6, retrying...')
def getSnat4Rule(self, snat_target, source):
rule = iptc.Rule()
rule.src = source
rule.dst = '!' + rule.src
target = rule.create_target("SNAT")
target.to_source = snat_target
match = rule.create_match("comment")
match.comment = f'{int(round(time.time()))}'
return rule
def getSnat6Rule(self, snat_target, source):
rule = iptc.Rule6()
rule.src = source
rule.dst = '!' + rule.src
target = rule.create_target("SNAT")
target.to_source = snat_target
return rule

View File

@@ -0,0 +1,23 @@
import time
import json
class Logger:
def __init__(self, redis):
self.r = redis
def log(self, priority, message):
tolog = {}
tolog['time'] = int(round(time.time()))
tolog['priority'] = priority
tolog['message'] = message
self.r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
print(message)
def logWarn(self, message):
self.log('warn', message)
def logCrit(self, message):
self.log('crit', message)
def logInfo(self, message):
self.log('info', message)

View File

@@ -0,0 +1,495 @@
import nftables
import ipaddress
class NFTables:
def __init__(self, chain_name, logger):
self.chain_name = chain_name
self.logger = logger
self.nft = nftables.Nftables()
self.nft.set_json_output(True)
self.nft.set_handle_output(True)
self.nft_chain_names = {'ip': {'filter': {'input': '', 'forward': ''}, 'nat': {'postrouting': ''} },
'ip6': {'filter': {'input': '', 'forward': ''}, 'nat': {'postrouting': ''} } }
self.search_current_chains()
def initChainIPv4(self):
self.insert_mailcow_chains("ip")
def initChainIPv6(self):
self.insert_mailcow_chains("ip6")
def checkIPv4ChainOrder(self):
return self.checkChainOrder("ip")
def checkIPv6ChainOrder(self):
return self.checkChainOrder("ip6")
def checkChainOrder(self, filter_table):
err = False
exit_code = None
for chain in ['input', 'forward']:
chain_position = self.check_mailcow_chains(filter_table, chain)
if chain_position is None: continue
if chain_position is False:
self.logger.logCrit(f'MAILCOW target not found in {filter_table} {chain} table, restarting container to fix it...')
err = True
exit_code = 2
if chain_position > 0:
self.logger.logCrit(f'MAILCOW target is in position {chain_position} in the {filter_table} {chain} table, restarting container to fix it...')
err = True
exit_code = 2
return err, exit_code
def clearIPv4Table(self):
self.clearTable("ip")
def clearIPv6Table(self):
self.clearTable("ip6")
def clearTable(self, _family):
is_empty_dict = True
json_command = self.get_base_dict()
chain_handle = self.get_chain_handle(_family, "filter", self.chain_name)
# if no handle, the chain doesn't exists
if chain_handle is not None:
is_empty_dict = False
# flush chain
mailcow_chain = {'family': _family, 'table': 'filter', 'name': self.chain_name}
flush_chain = {'flush': {'chain': mailcow_chain}}
json_command["nftables"].append(flush_chain)
# remove rule in forward chain
# remove rule in input chain
chains_family = [self.nft_chain_names[_family]['filter']['input'],
self.nft_chain_names[_family]['filter']['forward'] ]
for chain_base in chains_family:
if not chain_base: continue
rules_handle = self.get_rules_handle(_family, "filter", chain_base)
if rules_handle is not None:
for r_handle in rules_handle:
is_empty_dict = False
mailcow_rule = {'family':_family,
'table': 'filter',
'chain': chain_base,
'handle': r_handle }
delete_rules = {'delete': {'rule': mailcow_rule} }
json_command["nftables"].append(delete_rules)
# remove chain
# after delete all rules referencing this chain
if chain_handle is not None:
mc_chain_handle = {'family':_family,
'table': 'filter',
'name': self.chain_name,
'handle': chain_handle }
delete_chain = {'delete': {'chain': mc_chain_handle} }
json_command["nftables"].append(delete_chain)
if is_empty_dict == False:
if self.nft_exec_dict(json_command):
self.logger.logInfo(f"Clear completed: {_family}")
def banIPv4(self, source):
ban_dict = self.get_ban_ip_dict(source, "ip")
return self.nft_exec_dict(ban_dict)
def banIPv6(self, source):
ban_dict = self.get_ban_ip_dict(source, "ip6")
return self.nft_exec_dict(ban_dict)
def unbanIPv4(self, source):
unban_dict = self.get_unban_ip_dict(source, "ip")
if not unban_dict:
return False
return self.nft_exec_dict(unban_dict)
def unbanIPv6(self, source):
unban_dict = self.get_unban_ip_dict(source, "ip6")
if not unban_dict:
return False
return self.nft_exec_dict(unban_dict)
def snat4(self, snat_target, source):
self.snat_rule("ip", snat_target, source)
def snat6(self, snat_target, source):
self.snat_rule("ip6", snat_target, source)
def nft_exec_dict(self, query: dict):
if not query: return False
rc, output, error = self.nft.json_cmd(query)
if rc != 0:
#self.logger.logCrit(f"Nftables Error: {error}")
return False
# Prevent returning False or empty string on commands that do not produce output
if rc == 0 and len(output) == 0:
return True
return output
def get_base_dict(self):
return {'nftables': [{ 'metainfo': { 'json_schema_version': 1} } ] }
def search_current_chains(self):
nft_chain_priority = {'ip': {'filter': {'input': None, 'forward': None}, 'nat': {'postrouting': None} },
'ip6': {'filter': {'input': None, 'forward': None}, 'nat': {'postrouting': None} } }
# Command: 'nft list chains'
_list = {'list' : {'chains': 'null'} }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if kernel_ruleset:
for _object in kernel_ruleset['nftables']:
chain = _object.get("chain")
if not chain: continue
_family = chain['family']
_table = chain['table']
_hook = chain.get("hook")
_priority = chain.get("prio")
_name = chain['name']
if _family not in self.nft_chain_names: continue
if _table not in self.nft_chain_names[_family]: continue
if _hook not in self.nft_chain_names[_family][_table]: continue
if _priority is None: continue
_saved_priority = nft_chain_priority[_family][_table][_hook]
if _saved_priority is None or _priority < _saved_priority:
# at this point, we know the chain has:
# hook and priority set
# and it has the lowest priority
nft_chain_priority[_family][_table][_hook] = _priority
self.nft_chain_names[_family][_table][_hook] = _name
def search_for_chain(self, kernel_ruleset: dict, chain_name: str):
found = False
for _object in kernel_ruleset["nftables"]:
chain = _object.get("chain")
if not chain:
continue
ch_name = chain.get("name")
if ch_name == chain_name:
found = True
break
return found
def get_chain_dict(self, _family: str, _name: str):
# nft (add | create) chain [<family>] <table> <name>
_chain_opts = {'family': _family, 'table': 'filter', 'name': _name }
_add = {'add': {'chain': _chain_opts} }
final_chain = self.get_base_dict()
final_chain["nftables"].append(_add)
return final_chain
def get_mailcow_jump_rule_dict(self, _family: str, _chain: str):
_jump_rule = self.get_base_dict()
_expr_opt=[]
_expr_counter = {'family': _family, 'table': 'filter', 'packets': 0, 'bytes': 0}
_counter_dict = {'counter': _expr_counter}
_expr_opt.append(_counter_dict)
_jump_opts = {'jump': {'target': self.chain_name} }
_expr_opt.append(_jump_opts)
_rule_params = {'family': _family,
'table': 'filter',
'chain': _chain,
'expr': _expr_opt,
'comment': "mailcow" }
_add_rule = {'insert': {'rule': _rule_params} }
_jump_rule["nftables"].append(_add_rule)
return _jump_rule
def insert_mailcow_chains(self, _family: str):
nft_input_chain = self.nft_chain_names[_family]['filter']['input']
nft_forward_chain = self.nft_chain_names[_family]['filter']['forward']
# Command: 'nft list table <family> filter'
_table_opts = {'family': _family, 'name': 'filter'}
_list = {'list': {'table': _table_opts} }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if kernel_ruleset:
# chain
if not self.search_for_chain(kernel_ruleset, self.chain_name):
cadena = self.get_chain_dict(_family, self.chain_name)
if self.nft_exec_dict(cadena):
self.logger.logInfo(f"MAILCOW {_family} chain created successfully.")
input_jump_found, forward_jump_found = False, False
for _object in kernel_ruleset["nftables"]:
if not _object.get("rule"):
continue
rule = _object["rule"]
if nft_input_chain and rule["chain"] == nft_input_chain:
if rule.get("comment") and rule["comment"] == "mailcow":
input_jump_found = True
if nft_forward_chain and rule["chain"] == nft_forward_chain:
if rule.get("comment") and rule["comment"] == "mailcow":
forward_jump_found = True
if not input_jump_found:
command = self.get_mailcow_jump_rule_dict(_family, nft_input_chain)
self.nft_exec_dict(command)
if not forward_jump_found:
command = self.get_mailcow_jump_rule_dict(_family, nft_forward_chain)
self.nft_exec_dict(command)
def delete_nat_rule(self, _family:str, _chain: str, _handle:str):
delete_command = self.get_base_dict()
_rule_opts = {'family': _family,
'table': 'nat',
'chain': _chain,
'handle': _handle }
_delete = {'delete': {'rule': _rule_opts} }
delete_command["nftables"].append(_delete)
return self.nft_exec_dict(delete_command)
def snat_rule(self, _family: str, snat_target: str, source_address: str):
chain_name = self.nft_chain_names[_family]['nat']['postrouting']
# no postrouting chain, may occur if docker has ipv6 disabled.
if not chain_name: return
# Command: nft list chain <family> nat <chain_name>
_chain_opts = {'family': _family, 'table': 'nat', 'name': chain_name}
_list = {'list':{'chain': _chain_opts} }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if not kernel_ruleset:
return
rule_position = 0
rule_handle = None
rule_found = False
for _object in kernel_ruleset["nftables"]:
if not _object.get("rule"):
continue
rule = _object["rule"]
if not rule.get("comment") or not rule["comment"] == "mailcow":
rule_position +=1
continue
rule_found = True
rule_handle = rule["handle"]
break
dest_net = ipaddress.ip_network(source_address)
target_net = ipaddress.ip_network(snat_target)
if rule_found:
saddr_ip = rule["expr"][0]["match"]["right"]["prefix"]["addr"]
saddr_len = int(rule["expr"][0]["match"]["right"]["prefix"]["len"])
daddr_ip = rule["expr"][1]["match"]["right"]["prefix"]["addr"]
daddr_len = int(rule["expr"][1]["match"]["right"]["prefix"]["len"])
target_ip = rule["expr"][3]["snat"]["addr"]
saddr_net = ipaddress.ip_network(saddr_ip + '/' + str(saddr_len))
daddr_net = ipaddress.ip_network(daddr_ip + '/' + str(daddr_len))
current_target_net = ipaddress.ip_network(target_ip)
match = all((
dest_net == saddr_net,
dest_net == daddr_net,
target_net == current_target_net
))
try:
if rule_position == 0:
if not match:
# Position 0 , it is a mailcow rule , but it does not have the same parameters
if self.delete_nat_rule(_family, chain_name, rule_handle):
self.logger.logInfo(f'Remove rule for source network {saddr_net} to SNAT target {target_net} from {_family} nat {chain_name} chain, rule does not match configured parameters')
else:
# Position > 0 and is mailcow rule
if self.delete_nat_rule(_family, chain_name, rule_handle):
self.logger.logInfo(f'Remove rule for source network {saddr_net} to SNAT target {target_net} from {_family} nat {chain_name} chain, rule is at position {rule_position}')
except:
self.logger.logCrit(f"Error running SNAT on {_family}, retrying..." )
else:
# rule not found
json_command = self.get_base_dict()
try:
snat_dict = {'snat': {'addr': str(target_net.network_address)} }
expr_counter = {'family': _family, 'table': 'nat', 'packets': 0, 'bytes': 0}
counter_dict = {'counter': expr_counter}
prefix_dict = {'prefix': {'addr': str(dest_net.network_address), 'len': int(dest_net.prefixlen)} }
payload_dict = {'payload': {'protocol': _family, 'field': "saddr"} }
match_dict1 = {'match': {'op': '==', 'left': payload_dict, 'right': prefix_dict} }
payload_dict2 = {'payload': {'protocol': _family, 'field': "daddr"} }
match_dict2 = {'match': {'op': '!=', 'left': payload_dict2, 'right': prefix_dict } }
expr_list = [
match_dict1,
match_dict2,
counter_dict,
snat_dict
]
rule_fields = {'family': _family,
'table': 'nat',
'chain': chain_name,
'comment': "mailcow",
'expr': expr_list }
insert_dict = {'insert': {'rule': rule_fields} }
json_command["nftables"].append(insert_dict)
if self.nft_exec_dict(json_command):
self.logger.logInfo(f'Added {_family} nat {chain_name} rule for source network {dest_net} to {target_net}')
except:
self.logger.logCrit(f"Error running SNAT on {_family}, retrying...")
def get_chain_handle(self, _family: str, _table: str, chain_name: str):
chain_handle = None
# Command: 'nft list chains {family}'
_list = {'list': {'chains': {'family': _family} } }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if kernel_ruleset:
for _object in kernel_ruleset["nftables"]:
if not _object.get("chain"):
continue
chain = _object["chain"]
if chain["family"] == _family and chain["table"] == _table and chain["name"] == chain_name:
chain_handle = chain["handle"]
break
return chain_handle
def get_rules_handle(self, _family: str, _table: str, chain_name: str):
rule_handle = []
# Command: 'nft list chain {family} {table} {chain_name}'
_chain_opts = {'family': _family, 'table': _table, 'name': chain_name}
_list = {'list': {'chain': _chain_opts} }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if kernel_ruleset:
for _object in kernel_ruleset["nftables"]:
if not _object.get("rule"):
continue
rule = _object["rule"]
if rule["family"] == _family and rule["table"] == _table and rule["chain"] == chain_name:
if rule.get("comment") and rule["comment"] == "mailcow":
rule_handle.append(rule["handle"])
return rule_handle
def get_ban_ip_dict(self, ipaddr: str, _family: str):
json_command = self.get_base_dict()
expr_opt = []
ipaddr_net = ipaddress.ip_network(ipaddr)
right_dict = {'prefix': {'addr': str(ipaddr_net.network_address), 'len': int(ipaddr_net.prefixlen) } }
left_dict = {'payload': {'protocol': _family, 'field': 'saddr'} }
match_dict = {'op': '==', 'left': left_dict, 'right': right_dict }
expr_opt.append({'match': match_dict})
counter_dict = {'counter': {'family': _family, 'table': "filter", 'packets': 0, 'bytes': 0} }
expr_opt.append(counter_dict)
expr_opt.append({'drop': "null"})
rule_dict = {'family': _family, 'table': "filter", 'chain': self.chain_name, 'expr': expr_opt}
base_dict = {'insert': {'rule': rule_dict} }
json_command["nftables"].append(base_dict)
return json_command
def get_unban_ip_dict(self, ipaddr:str, _family: str):
json_command = self.get_base_dict()
# Command: 'nft list chain {s_family} filter MAILCOW'
_chain_opts = {'family': _family, 'table': 'filter', 'name': self.chain_name}
_list = {'list': {'chain': _chain_opts} }
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
rule_handle = None
if kernel_ruleset:
for _object in kernel_ruleset["nftables"]:
if not _object.get("rule"):
continue
rule = _object["rule"]["expr"][0]["match"]
left_opt = rule["left"]["payload"]
if not left_opt["protocol"] == _family:
continue
if not left_opt["field"] =="saddr":
continue
# ip currently banned
rule_right = rule["right"]
if isinstance(rule_right, dict):
current_rule_ip = rule_right["prefix"]["addr"] + '/' + str(rule_right["prefix"]["len"])
else:
current_rule_ip = rule_right
current_rule_net = ipaddress.ip_network(current_rule_ip)
# ip to ban
candidate_net = ipaddress.ip_network(ipaddr)
if current_rule_net == candidate_net:
rule_handle = _object["rule"]["handle"]
break
if rule_handle is not None:
mailcow_rule = {'family': _family, 'table': 'filter', 'chain': self.chain_name, 'handle': rule_handle}
delete_rule = {'delete': {'rule': mailcow_rule} }
json_command["nftables"].append(delete_rule)
else:
return False
return json_command
def check_mailcow_chains(self, family: str, chain: str):
position = 0
rule_found = False
chain_name = self.nft_chain_names[family]['filter'][chain]
if not chain_name: return None
_chain_opts = {'family': family, 'table': 'filter', 'name': chain_name}
_list = {'list': {'chain': _chain_opts}}
command = self.get_base_dict()
command['nftables'].append(_list)
kernel_ruleset = self.nft_exec_dict(command)
if kernel_ruleset:
for _object in kernel_ruleset["nftables"]:
if not _object.get("rule"):
continue
rule = _object["rule"]
if rule.get("comment") and rule["comment"] == "mailcow":
rule_found = True
break
position+=1
return position if rule_found else False

View File

@@ -1,18 +1,18 @@
FROM php:8.2-fpm-alpine3.17 FROM php:8.2-fpm-alpine3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced # renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG APCU_PECL_VERSION=5.1.22 ARG APCU_PECL_VERSION=5.1.22
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced # renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.7.0 ARG IMAGICK_PECL_VERSION=3.7.0
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced # renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.1.4 ARG MAILPARSE_PECL_VERSION=3.1.6
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced # renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MEMCACHED_PECL_VERSION=3.2.0 ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced # renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=5.3.7 ARG REDIS_PECL_VERSION=6.0.1
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced # renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.5.5 ARG COMPOSER_VERSION=2.6.5
RUN apk add -U --no-cache autoconf \ RUN apk add -U --no-cache autoconf \
aspell-dev \ aspell-dev \
@@ -110,4 +110,4 @@ COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["php-fpm"] CMD ["php-fpm"]

View File

@@ -17,10 +17,10 @@ RUN groupadd -g 102 postfix \
ca-certificates \ ca-certificates \
curl \ curl \
dirmngr \ dirmngr \
dnsutils \ dnsutils \
gnupg \ gnupg \
libsasl2-modules \ libsasl2-modules \
mariadb-client \ mariadb-client \
perl \ perl \
postfix \ postfix \
postfix-mysql \ postfix-mysql \
@@ -32,7 +32,7 @@ RUN groupadd -g 102 postfix \
syslog-ng \ syslog-ng \
syslog-ng-core \ syslog-ng-core \
syslog-ng-mod-redis \ syslog-ng-mod-redis \
tzdata \ tzdata \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& touch /etc/default/locale \ && touch /etc/default/locale \
&& printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \ && printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \

View File

@@ -393,12 +393,101 @@ query = SELECT goto FROM spamalias
AND validity >= UNIX_TIMESTAMP() AND validity >= UNIX_TIMESTAMP()
EOF EOF
sed -i '/User overrides/q' /opt/postfix/conf/main.cf if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
echo >> /opt/postfix/conf/main.cf cat <<EOF > /opt/postfix/conf/dns_blocklists.cf
touch /opt/postfix/conf/extra.cf # This file can be edited.
sed -i '/myhostname/d' /opt/postfix/conf/extra.cf # Delete this file and restart postfix container to revert any changes.
echo -e "myhostname = ${MAILCOW_HOSTNAME}\n$(cat /opt/postfix/conf/extra.cf)" > /opt/postfix/conf/extra.cf postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
hostkarma.junkemailfilter.com=127.0.0.1*-2
list.dnswl.org=127.0.[0..255].0*-2
list.dnswl.org=127.0.[0..255].1*-4
list.dnswl.org=127.0.[0..255].2*-6
list.dnswl.org=127.0.[0..255].3*-8
ix.dnsbl.manitu.net*2
bl.spamcop.net*2
bl.suomispam.net*2
hostkarma.junkemailfilter.com=127.0.0.2*3
hostkarma.junkemailfilter.com=127.0.0.4*2
hostkarma.junkemailfilter.com=127.0.1.2*1
backscatter.spameatingmonkey.net*2
bl.ipv6.spameatingmonkey.net*2
bl.spameatingmonkey.net*2
b.barracudacentral.org=127.0.0.2*7
bl.mailspike.net=127.0.0.2*5
bl.mailspike.net=127.0.0.[10;11;12]*4
dnsbl.sorbs.net=127.0.0.10*8
dnsbl.sorbs.net=127.0.0.5*6
dnsbl.sorbs.net=127.0.0.7*3
dnsbl.sorbs.net=127.0.0.8*2
dnsbl.sorbs.net=127.0.0.6*2
dnsbl.sorbs.net=127.0.0.9*2
EOF
fi
DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S')
if [ ! -z "$DNSBL_CONFIG" ]; then
echo -e "\e[33mChecking if ASN for your IP is listed for Spamhaus Bad ASN List...\e[0m"
if [ -n "$SPAMHAUS_DQS_KEY" ]; then
echo -e "\e[32mDetected SPAMHAUS_DQS_KEY variable from mailcow.conf...\e[0m"
echo -e "\e[33mUsing DQS Blocklists from Spamhaus!\e[0m"
SPAMHAUS_DNSBL_CONFIG=$(cat <<EOF
${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net=127.0.0.[4..7]*6
${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net=127.0.0.[10;11]*8
${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net=127.0.0.3*4
${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net=127.0.0.2*3
postscreen_dnsbl_reply_map = texthash:/opt/postfix/conf/dnsbl_reply.map
EOF
cat <<EOF > /opt/postfix/conf/dnsbl_reply.map
# Autogenerated by mailcow, using Spamhaus DQS reply domains
${SPAMHAUS_DQS_KEY}.sbl.dq.spamhaus.net sbl.spamhaus.org
${SPAMHAUS_DQS_KEY}.xbl.dq.spamhaus.net xbl.spamhaus.org
${SPAMHAUS_DQS_KEY}.pbl.dq.spamhaus.net pbl.spamhaus.org
${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net zen.spamhaus.org
${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net dbl.spamhaus.org
${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net zrd.spamhaus.org
EOF
)
else
if [ -f "/opt/postfix/conf/dnsbl_reply.map" ]; then
rm /opt/postfix/conf/dnsbl_reply.map
fi
response=$(curl --connect-timeout 15 --max-time 30 -s -o /dev/null -w "%{http_code}" "https://asn-check.mailcow.email")
if [ "$response" -eq 503 ]; then
echo -e "\e[31mThe AS of your IP is listed as a banned AS from Spamhaus!\e[0m"
echo -e "\e[33mNo SPAMHAUS_DQS_KEY found... Skipping Spamhaus blocklists entirely!\e[0m"
SPAMHAUS_DNSBL_CONFIG=""
elif [ "$response" -eq 200 ]; then
echo -e "\e[32mThe AS of your IP is NOT listed as a banned AS from Spamhaus!\e[0m"
echo -e "\e[33mUsing the open Spamhaus blocklists.\e[0m"
SPAMHAUS_DNSBL_CONFIG=$(cat <<EOF
zen.spamhaus.org=127.0.0.[10;11]*8
zen.spamhaus.org=127.0.0.[4..7]*6
zen.spamhaus.org=127.0.0.3*4
zen.spamhaus.org=127.0.0.2*3
EOF
)
else
echo -e "\e[31mWe couldn't determine your AS... (maybe DNS/Network issue?) Response Code: $response\e[0m"
echo -e "\e[33mDeactivating Spamhaus DNS Blocklists to be on the safe site!\e[0m"
SPAMHAUS_DNSBL_CONFIG=""
fi
fi
fi
# Reset main.cf
sed -i '/Overrides/q' /opt/postfix/conf/main.cf
echo >> /opt/postfix/conf/main.cf
# Append postscreen dnsbl sites to main.cf
if [ ! -z "$DNSBL_CONFIG" ]; then
echo -e "${DNSBL_CONFIG}\n${SPAMHAUS_DNSBL_CONFIG}" >> /opt/postfix/conf/main.cf
fi
# Append user overrides
echo -e "\n# User Overrides" >> /opt/postfix/conf/main.cf
touch /opt/postfix/conf/extra.cf
sed -i '/\$myhostname/! { /myhostname/d }' /opt/postfix/conf/extra.cf
echo -e "myhostname = ${MAILCOW_HOSTNAME}\n$(cat /opt/postfix/conf/extra.cf)" > /opt/postfix/conf/extra.cf
cat /opt/postfix/conf/extra.cf >> /opt/postfix/conf/main.cf cat /opt/postfix/conf/extra.cf >> /opt/postfix/conf/main.cf
if [ ! -f /opt/postfix/conf/custom_transport.pcre ]; then if [ ! -f /opt/postfix/conf/custom_transport.pcre ]; then

View File

@@ -79,6 +79,9 @@ EOF
redis-cli -h redis-mailcow SLAVEOF NO ONE redis-cli -h redis-mailcow SLAVEOF NO ONE
fi 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 \ chown -R _rspamd:_rspamd /var/lib/rspamd \
/etc/rspamd/local.d \ /etc/rspamd/local.d \
/etc/rspamd/override.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 DEBIAN_FRONTEND=noninteractive
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/ ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.16 ARG GOSU_VERSION=1.16
ENV LC_ALL C ENV LC_ALL C

View File

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

View File

@@ -18,6 +18,11 @@ EXPOSE 53/udp 53/tcp
COPY docker-entrypoint.sh /docker-entrypoint.sh COPY docker-entrypoint.sh /docker-entrypoint.sh
# healthcheck (nslookup)
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /healthcheck.sh
HEALTHCHECK --interval=30s --timeout=10s CMD [ "/healthcheck.sh" ]
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/usr/sbin/unbound"] CMD ["/usr/sbin/unbound"]

View File

@@ -0,0 +1,12 @@
#!/bin/bash
nslookup mailcow.email 127.0.0.1 1> /dev/null
if [ $? == 0 ]; then
echo "DNS resolution is working!"
exit 0
else
echo "DNS resolution is not working correctly..."
echo "Maybe check your outbound firewall, as it needs to resolve DNS over TCP AND UDP!"
exit 1
fi

View File

@@ -86,7 +86,7 @@ server {
deny all; 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)(\/.*|)$; fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info; set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404; try_files $fastcgi_script_name =404;
@@ -105,7 +105,7 @@ server {
fastcgi_read_timeout 1200; fastcgi_read_timeout 1200;
} }
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) { location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
try_files $uri/ =404; try_files $uri/ =404;
index index.php; index index.php;
} }

View File

@@ -1,5 +1,6 @@
proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g; proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
server_names_hash_bucket_size 64; server_names_hash_max_size 512;
server_names_hash_bucket_size 128;
map $http_x_forwarded_proto $client_req_scheme { map $http_x_forwarded_proto $client_req_scheme {
default $scheme; default $scheme;

View File

@@ -1,2 +1,3 @@
listen ${HTTPS_PORT} ssl http2; listen ${HTTPS_PORT} ssl;
listen [::]:${HTTPS_PORT} ssl http2; listen [::]:${HTTPS_PORT} ssl;
http2 on;

View File

@@ -40,34 +40,6 @@ postscreen_blacklist_action = drop
postscreen_cache_cleanup_interval = 24h postscreen_cache_cleanup_interval = 24h
postscreen_cache_map = proxy:btree:$data_directory/postscreen_cache postscreen_cache_map = proxy:btree:$data_directory/postscreen_cache
postscreen_dnsbl_action = enforce postscreen_dnsbl_action = enforce
postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
hostkarma.junkemailfilter.com=127.0.0.1*-2
list.dnswl.org=127.0.[0..255].0*-2
list.dnswl.org=127.0.[0..255].1*-4
list.dnswl.org=127.0.[0..255].2*-6
list.dnswl.org=127.0.[0..255].3*-8
ix.dnsbl.manitu.net*2
bl.spamcop.net*2
bl.suomispam.net*2
hostkarma.junkemailfilter.com=127.0.0.2*3
hostkarma.junkemailfilter.com=127.0.0.4*2
hostkarma.junkemailfilter.com=127.0.1.2*1
backscatter.spameatingmonkey.net*2
bl.ipv6.spameatingmonkey.net*2
bl.spameatingmonkey.net*2
b.barracudacentral.org=127.0.0.2*7
bl.mailspike.net=127.0.0.2*5
bl.mailspike.net=127.0.0.[10;11;12]*4
dnsbl.sorbs.net=127.0.0.10*8
dnsbl.sorbs.net=127.0.0.5*6
dnsbl.sorbs.net=127.0.0.7*3
dnsbl.sorbs.net=127.0.0.8*2
dnsbl.sorbs.net=127.0.0.6*2
dnsbl.sorbs.net=127.0.0.9*2
zen.spamhaus.org=127.0.0.[10;11]*8
zen.spamhaus.org=127.0.0.[4..7]*6
zen.spamhaus.org=127.0.0.3*4
zen.spamhaus.org=127.0.0.2*3
postscreen_dnsbl_threshold = 6 postscreen_dnsbl_threshold = 6
postscreen_dnsbl_ttl = 5m postscreen_dnsbl_ttl = 5m
postscreen_greet_action = enforce postscreen_greet_action = enforce
@@ -197,4 +169,4 @@ smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
parent_domain_matches_subdomains = debug_peer_list,fast_flush_domains,mynetworks,qmqpd_authorized_clients parent_domain_matches_subdomains = debug_peer_list,fast_flush_domains,mynetworks,qmqpd_authorized_clients
# DO NOT EDIT ANYTHING BELOW # # DO NOT EDIT ANYTHING BELOW #
# User overrides # # Overrides #

View File

@@ -1,15 +1,20 @@
# Whitelist generated by Postwhite v3.4 on Mon 21 Mar 2022 06:50:26 PM CET # Whitelist generated by Postwhite v3.4 on Fri Dec 1 00:15:18 UTC 2023
# https://github.com/stevejenkins/postwhite/ # https://github.com/stevejenkins/postwhite/
# 1898 total rules # 2038 total rules
2a00:1450:4000::/36 permit 2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit 2a01:111:f400::/48 permit
2a01:111:f403::/48 permit 2a01:111:f403:8000::/50 permit
2a01:4180:4050:0400::/64 permit 2a01:111:f403::/49 permit
2a01:4180:4050:0800::/64 permit 2a01:111:f403:c000::/51 permit
2a01:4180:4051:0400::/64 permit 2a01:111:f403:f000::/52 permit
2a01:4180:4051:0800::/64 permit
2a02:a60:0:5::/64 permit 2a02:a60:0:5::/64 permit
2c0f:fb50:4000::/36 permit 2c0f:fb50:4000::/36 permit
2.207.151.53 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.20.114.31 permit
8.25.194.0/23 permit 8.25.194.0/23 permit
8.25.196.0/23 permit 8.25.196.0/23 permit
@@ -19,41 +24,53 @@
13.70.32.43 permit 13.70.32.43 permit
13.72.50.45 permit 13.72.50.45 permit
13.74.143.28 permit 13.74.143.28 permit
13.77.161.179 permit
13.78.233.182 permit 13.78.233.182 permit
13.92.31.129 permit 13.92.31.129 permit
13.110.208.0/21 permit 13.110.208.0/21 permit
13.110.209.0/24 permit
13.110.216.0/22 permit 13.110.216.0/22 permit
13.110.224.0/20 permit 13.110.224.0/20 permit
13.111.0.0/16 permit 13.111.0.0/16 permit
17.41.0.0/16 permit 15.200.21.50 permit
15.200.44.248 permit
15.200.201.185 permit
17.57.155.0/24 permit 17.57.155.0/24 permit
17.57.156.0/24 permit 17.57.156.0/24 permit
17.58.0.0/16 permit 17.58.0.0/16 permit
17.110.0.0/15 permit 18.156.89.250 permit
17.142.0.0/15 permit 18.157.243.190 permit
17.162.0.0/15 permit
17.164.0.0/16 permit
17.171.37.0/24 permit
17.172.0.0/16 permit
17.179.168.0/23 permit
18.194.95.56 permit 18.194.95.56 permit
18.198.96.88 permit 18.198.96.88 permit
20.47.149.138 permit 18.208.124.128/25 permit
20.48.0.0/12 permit 18.216.232.154 permit
18.234.1.244 permit
18.236.40.242 permit
20.51.6.32/30 permit
20.52.52.2 permit 20.52.52.2 permit
20.52.128.133 permit 20.52.128.133 permit
20.59.80.4/30 permit
20.63.210.192/28 permit 20.63.210.192/28 permit
20.64.0.0/10 permit 20.69.8.108/30 permit
20.70.246.20 permit
20.76.201.171 permit
20.83.222.104/30 permit
20.88.157.184/30 permit
20.94.180.64/28 permit 20.94.180.64/28 permit
20.97.34.220/30 permit
20.98.148.156/30 permit
20.98.194.68/30 permit
20.105.209.76/30 permit
20.107.239.64/30 permit
20.112.250.133 permit
20.118.139.208/30 permit
20.185.213.160/27 permit 20.185.213.160/27 permit
20.185.213.224/27 permit 20.185.213.224/27 permit
20.185.214.0/27 permit 20.185.214.0/27 permit
20.185.214.2 permit 20.185.214.2 permit
20.185.214.32/27 permit 20.185.214.32/27 permit
20.185.214.64/27 permit 20.185.214.64/27 permit
20.192.0.0/10 permit 20.231.239.246 permit
23.100.85.1 permit 20.236.44.162 permit
23.103.224.0/19 permit 23.103.224.0/19 permit
23.249.208.0/20 permit 23.249.208.0/20 permit
23.251.224.0/19 permit 23.251.224.0/19 permit
@@ -78,46 +95,34 @@
27.123.206.56/29 permit 27.123.206.56/29 permit
27.123.206.76/30 permit 27.123.206.76/30 permit
27.123.206.80/28 permit 27.123.206.80/28 permit
34.194.25.167 permit 31.25.48.222 permit
34.194.144.120 permit 34.195.217.107 permit
34.202.239.6 permit
34.212.163.75 permit 34.212.163.75 permit
34.215.104.144 permit
34.225.212.172 permit 34.225.212.172 permit
34.247.168.44 permit 34.247.168.44 permit
35.161.32.253 permit
35.167.93.243 permit
35.176.132.251 permit 35.176.132.251 permit
35.190.247.0/24 permit 35.190.247.0/24 permit
35.191.0.0/16 permit 35.191.0.0/16 permit
37.188.97.188 permit
37.218.248.47 permit 37.218.248.47 permit
37.218.249.47 permit 37.218.249.47 permit
37.218.251.62 permit 37.218.251.62 permit
39.156.163.64/29 permit 39.156.163.64/29 permit
40.71.187.0/24 permit 40.71.187.0/24 permit
40.76.4.15 permit
40.77.102.222 permit
40.92.0.0/15 permit 40.92.0.0/15 permit
40.97.116.82 permit 40.92.0.0/16 permit
40.97.128.194 permit
40.97.148.226 permit
40.97.153.146 permit
40.97.156.114 permit
40.97.160.2 permit
40.97.161.50 permit
40.97.164.146 permit
40.107.0.0/16 permit 40.107.0.0/16 permit
40.112.65.63 permit 40.112.65.63 permit
40.112.72.205 permit
40.113.200.201 permit
40.117.80.0/24 permit 40.117.80.0/24 permit
40.121.71.46 permit
41.74.192.0/22 permit
41.74.196.0/22 permit
41.74.200.0/23 permit
41.74.204.0/23 permit
41.74.206.0/24 permit
42.159.163.81 permit
42.159.163.82 permit
42.159.163.83 permit
43.228.184.0/22 permit 43.228.184.0/22 permit
44.206.138.57 permit
44.209.42.157 permit
44.236.56.93 permit
44.238.220.251 permit
46.19.168.0/23 permit
46.226.48.0/21 permit 46.226.48.0/21 permit
46.228.36.37 permit 46.228.36.37 permit
46.228.36.38/31 permit 46.228.36.38/31 permit
@@ -167,6 +172,8 @@
46.243.88.175 permit 46.243.88.175 permit
46.243.88.176 permit 46.243.88.176 permit
46.243.88.177 permit 46.243.88.177 permit
46.243.95.179 permit
46.243.95.180 permit
50.18.45.249 permit 50.18.45.249 permit
50.18.121.236 permit 50.18.121.236 permit
50.18.121.248 permit 50.18.121.248 permit
@@ -176,33 +183,36 @@
50.18.125.237 permit 50.18.125.237 permit
50.18.126.162 permit 50.18.126.162 permit
50.31.32.0/19 permit 50.31.32.0/19 permit
50.31.156.96/27 permit
50.31.205.0/24 permit
51.4.71.62 permit
51.4.72.0/24 permit
51.4.80.0/27 permit
51.5.72.0/24 permit
51.5.80.0/27 permit
51.137.58.21 permit 51.137.58.21 permit
51.140.75.55 permit 51.140.75.55 permit
51.144.100.179 permit 51.144.100.179 permit
51.163.158.0/24 permit
51.163.159.21 permit
52.5.230.59 permit 52.5.230.59 permit
52.27.5.72 permit 52.27.5.72 permit
52.27.28.47 permit 52.27.28.47 permit
52.33.191.91 permit 52.28.63.81 permit
52.36.138.31 permit 52.36.138.31 permit
52.37.142.146 permit 52.37.142.146 permit
52.38.191.253 permit 52.58.216.183 permit
52.41.64.145 permit 52.59.143.3 permit
52.60.41.5 permit 52.60.41.5 permit
52.60.115.116 permit 52.60.115.116 permit
52.61.91.9 permit
52.71.0.205 permit
52.82.172.0/22 permit 52.82.172.0/22 permit
52.94.124.0/28 permit 52.94.124.0/28 permit
52.95.48.152/29 permit 52.95.48.152/29 permit
52.95.49.88/29 permit 52.95.49.88/29 permit
52.96.91.34 permit
52.96.111.82 permit
52.96.172.98 permit
52.96.214.50 permit
52.96.222.194 permit
52.96.222.226 permit
52.96.223.2 permit
52.96.228.130 permit
52.96.229.242 permit
52.100.0.0/14 permit 52.100.0.0/14 permit
52.103.0.0/17 permit
52.119.213.144/28 permit 52.119.213.144/28 permit
52.160.39.140 permit 52.160.39.140 permit
52.165.175.144 permit 52.165.175.144 permit
@@ -214,23 +224,29 @@
52.222.73.83 permit 52.222.73.83 permit
52.222.73.120 permit 52.222.73.120 permit
52.222.75.85 permit 52.222.75.85 permit
52.222.89.228 permit
52.234.172.96/28 permit 52.234.172.96/28 permit
52.236.28.240/28 permit 52.236.28.240/28 permit
52.237.141.173 permit
52.244.206.214 permit 52.244.206.214 permit
52.247.53.144 permit 52.247.53.144 permit
52.250.107.196 permit 52.250.107.196 permit
52.250.126.174 permit 52.250.126.174 permit
52.251.55.143 permit
54.90.148.255 permit 54.90.148.255 permit
54.156.255.69 permit
54.172.97.247 permit 54.172.97.247 permit
54.174.52.0/24 permit
54.174.53.128/30 permit
54.174.57.0/24 permit
54.174.59.0/24 permit
54.174.60.0/23 permit
54.174.63.0/24 permit
54.186.193.102 permit 54.186.193.102 permit
54.191.223.5 permit 54.191.223.56 permit
54.194.61.95 permit 54.194.61.95 permit
54.195.113.45 permit 54.195.113.45 permit
54.213.20.246 permit
54.214.39.184 permit 54.214.39.184 permit
54.216.77.168 permit 54.216.77.168 permit
54.221.227.204 permit
54.240.0.0/18 permit 54.240.0.0/18 permit
54.240.64.0/19 permit 54.240.64.0/19 permit
54.240.96.0/19 permit 54.240.96.0/19 permit
@@ -238,7 +254,9 @@
54.244.54.130 permit 54.244.54.130 permit
54.244.242.0/24 permit 54.244.242.0/24 permit
54.246.232.180 permit 54.246.232.180 permit
54.255.61.23 permit
62.13.128.0/24 permit 62.13.128.0/24 permit
62.13.128.150 permit
62.13.129.128/25 permit 62.13.129.128/25 permit
62.13.136.0/22 permit 62.13.136.0/22 permit
62.13.140.0/22 permit 62.13.140.0/22 permit
@@ -247,29 +265,32 @@
62.13.150.0/23 permit 62.13.150.0/23 permit
62.13.152.0/23 permit 62.13.152.0/23 permit
62.17.146.128/26 permit 62.17.146.128/26 permit
62.140.7.0/24 permit 62.179.121.0/24 permit
62.140.10.21 permit 62.201.172.0/27 permit
62.201.172.32/27 permit
62.253.227.114 permit
63.32.13.159 permit 63.32.13.159 permit
63.80.14.0/23 permit 63.80.14.0/23 permit
63.111.28.137 permit
63.128.21.0/24 permit 63.128.21.0/24 permit
63.143.57.128/25 permit 63.143.57.128/25 permit
63.143.59.128/25 permit 63.143.59.128/25 permit
64.18.0.0/20 permit 64.18.0.0/20 permit
64.20.241.45 permit 64.20.241.45 permit
64.34.47.128/27 permit 64.69.212.0/24 permit
64.34.57.192/26 permit
64.71.149.160/28 permit 64.71.149.160/28 permit
64.79.155.0/24 permit 64.79.155.0/24 permit
64.79.155.192 permit
64.79.155.193 permit
64.79.155.205 permit
64.79.155.206 permit
64.89.44.85 permit 64.89.44.85 permit
64.89.45.80 permit 64.89.45.80 permit
64.89.45.194 permit 64.89.45.194 permit
64.89.45.196 permit 64.89.45.196 permit
64.95.144.196 permit
64.127.115.252 permit 64.127.115.252 permit
64.132.88.0/23 permit 64.132.88.0/23 permit
64.132.92.0/24 permit 64.132.92.0/24 permit
64.135.77.0/24 permit
64.135.83.0/24 permit
64.147.123.17 permit 64.147.123.17 permit
64.147.123.18 permit 64.147.123.18 permit
64.147.123.19 permit 64.147.123.19 permit
@@ -281,28 +302,35 @@
64.147.123.27 permit 64.147.123.27 permit
64.147.123.28 permit 64.147.123.28 permit
64.147.123.29 permit 64.147.123.29 permit
64.147.123.128/27 permit
64.207.219.7 permit 64.207.219.7 permit
64.207.219.8 permit 64.207.219.8 permit
64.207.219.9 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.13 permit
64.207.219.14 permit 64.207.219.14 permit
64.207.219.15 permit 64.207.219.15 permit
64.207.219.71 permit 64.207.219.71 permit
64.207.219.72 permit 64.207.219.72 permit
64.207.219.73 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.77 permit
64.207.219.78 permit 64.207.219.78 permit
64.207.219.79 permit 64.207.219.79 permit
64.207.219.135 permit 64.207.219.135 permit
64.207.219.136 permit 64.207.219.136 permit
64.207.219.137 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.141 permit
64.207.219.142 permit 64.207.219.142 permit
64.207.219.143 permit 64.207.219.143 permit
64.233.160.0/19 permit 64.233.160.0/19 permit
65.38.115.76 permit
65.38.115.84 permit
65.39.215.0/24 permit
65.52.80.137 permit 65.52.80.137 permit
65.54.51.64/26 permit 65.54.51.64/26 permit
65.54.61.64/26 permit 65.54.61.64/26 permit
@@ -342,6 +370,10 @@
66.111.4.225 permit 66.111.4.225 permit
66.111.4.229 permit 66.111.4.229 permit
66.111.4.230 permit 66.111.4.230 permit
66.119.150.192/26 permit
66.135.202.0/27 permit
66.135.215.0/24 permit
66.135.222.1 permit
66.162.193.226/31 permit 66.162.193.226/31 permit
66.163.184.0/21 permit 66.163.184.0/21 permit
66.163.184.0/24 permit 66.163.184.0/24 permit
@@ -372,8 +404,8 @@
66.196.81.232/31 permit 66.196.81.232/31 permit
66.196.81.234 permit 66.196.81.234 permit
66.211.168.230/31 permit 66.211.168.230/31 permit
66.211.170.86/31 permit 66.211.170.88/29 permit
66.211.170.88/30 permit 66.211.184.0/23 permit
66.218.74.64/30 permit 66.218.74.64/30 permit
66.218.74.68/31 permit 66.218.74.68/31 permit
66.218.75.112/30 permit 66.218.75.112/30 permit
@@ -445,6 +477,8 @@
68.142.230.72/30 permit 68.142.230.72/30 permit
68.142.230.76/31 permit 68.142.230.76/31 permit
68.142.230.78 permit 68.142.230.78 permit
68.232.140.138 permit
68.232.157.143 permit
68.232.192.0/20 permit 68.232.192.0/20 permit
69.63.178.128/25 permit 69.63.178.128/25 permit
69.63.181.0/24 permit 69.63.181.0/24 permit
@@ -452,6 +486,10 @@
69.65.42.195 permit 69.65.42.195 permit
69.65.49.192/29 permit 69.65.49.192/29 permit
69.72.32.0/20 permit 69.72.32.0/20 permit
69.72.40.93 permit
69.72.40.94/31 permit
69.72.40.96/30 permit
69.72.47.205 permit
69.147.84.227 permit 69.147.84.227 permit
69.162.98.0/24 permit 69.162.98.0/24 permit
69.169.224.0/20 permit 69.169.224.0/20 permit
@@ -460,7 +498,7 @@
70.37.151.128/25 permit 70.37.151.128/25 permit
70.42.149.0/24 permit 70.42.149.0/24 permit
70.42.149.35 permit 70.42.149.35 permit
72.3.185.0/24 permit 72.3.237.64/28 permit
72.14.192.0/18 permit 72.14.192.0/18 permit
72.21.192.0/19 permit 72.21.192.0/19 permit
72.21.217.142 permit 72.21.217.142 permit
@@ -522,15 +560,11 @@
72.30.239.228/31 permit 72.30.239.228/31 permit
72.30.239.244/30 permit 72.30.239.244/30 permit
72.30.239.248/31 permit 72.30.239.248/31 permit
72.32.154.0/24 permit
72.32.217.0/24 permit
72.32.243.0/24 permit
72.34.168.76 permit 72.34.168.76 permit
72.34.168.80 permit 72.34.168.80 permit
72.34.168.85 permit 72.34.168.85 permit
72.34.168.86 permit 72.34.168.86 permit
72.52.72.32/28 permit 72.52.72.32/28 permit
72.52.72.36 permit
74.6.128.0/21 permit 74.6.128.0/21 permit
74.6.128.0/24 permit 74.6.128.0/24 permit
74.6.129.0/24 permit 74.6.129.0/24 permit
@@ -558,8 +592,11 @@
74.112.67.243 permit 74.112.67.243 permit
74.125.0.0/16 permit 74.125.0.0/16 permit
74.202.227.40 permit 74.202.227.40 permit
74.208.4.192/26 permit
74.208.5.64/26 permit
74.208.122.0/26 permit
74.209.250.0/24 permit 74.209.250.0/24 permit
74.209.250.12 permit 76.223.128.0/19 permit
76.223.176.0/20 permit 76.223.176.0/20 permit
77.238.176.0/22 permit 77.238.176.0/22 permit
77.238.176.0/24 permit 77.238.176.0/24 permit
@@ -582,8 +619,17 @@
77.238.189.142 permit 77.238.189.142 permit
77.238.189.146/31 permit 77.238.189.146/31 permit
77.238.189.148/30 permit 77.238.189.148/30 permit
81.7.169.128/25 permit
81.223.46.0/27 permit 81.223.46.0/27 permit
84.16.77.1 permit 82.165.159.0/24 permit
82.165.159.0/26 permit
82.165.229.31 permit
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 85.158.136.0/21 permit
86.61.88.25 permit 86.61.88.25 permit
87.198.219.130 permit 87.198.219.130 permit
@@ -624,11 +670,9 @@
87.248.117.201 permit 87.248.117.201 permit
87.248.117.202 permit 87.248.117.202 permit
87.248.117.205 permit 87.248.117.205 permit
87.252.219.254 permit
87.253.232.0/21 permit 87.253.232.0/21 permit
89.22.108.0/24 permit 89.22.108.0/24 permit
91.220.42.0/24 permit 91.211.240.0/22 permit
94.236.119.0/26 permit
94.245.112.0/27 permit 94.245.112.0/27 permit
94.245.112.10/31 permit 94.245.112.10/31 permit
95.131.104.0/21 permit 95.131.104.0/21 permit
@@ -638,6 +682,7 @@
96.43.148.64/28 permit 96.43.148.64/28 permit
96.43.148.64/31 permit 96.43.148.64/31 permit
96.43.151.64/28 permit 96.43.151.64/28 permit
98.97.248.0/21 permit
98.136.44.181 permit 98.136.44.181 permit
98.136.44.182/31 permit 98.136.44.182/31 permit
98.136.44.184 permit 98.136.44.184 permit
@@ -1142,24 +1187,20 @@
98.139.245.212/31 permit 98.139.245.212/31 permit
99.78.197.208/28 permit 99.78.197.208/28 permit
103.2.140.0/22 permit 103.2.140.0/22 permit
103.9.8.121 permit
103.9.8.122 permit
103.9.8.123 permit
103.9.96.0/22 permit 103.9.96.0/22 permit
103.13.69.0/24 permit 103.28.42.0/24 permit
103.47.204.0/22 permit 103.47.204.0/22 permit
103.96.21.0/24 permit
103.96.23.0/24 permit
103.151.192.0/23 permit 103.151.192.0/23 permit
103.237.104.0/22 permit 103.168.172.128/27 permit
104.43.243.237 permit 104.43.243.237 permit
104.44.112.128/25 permit
104.47.0.0/17 permit 104.47.0.0/17 permit
104.47.20.0/23 permit
104.47.75.0/24 permit
104.47.108.0/23 permit
104.130.96.0/28 permit 104.130.96.0/28 permit
104.130.122.0/23 permit 104.130.122.0/23 permit
104.214.25.77 permit 104.214.25.77 permit
104.215.148.63 permit
104.215.186.3 permit
104.245.209.192/26 permit
106.10.144.64/27 permit 106.10.144.64/27 permit
106.10.144.100/31 permit 106.10.144.100/31 permit
106.10.144.103 permit 106.10.144.103 permit
@@ -1320,9 +1361,9 @@
117.120.16.0/21 permit 117.120.16.0/21 permit
119.42.242.52/31 permit 119.42.242.52/31 permit
119.42.242.156 permit 119.42.242.156 permit
121.244.91.48 permit
122.15.156.182 permit
123.126.78.64/29 permit 123.126.78.64/29 permit
124.47.150.0/24 permit
124.47.189.0/24 permit
124.108.96.0/24 permit 124.108.96.0/24 permit
124.108.96.24/31 permit 124.108.96.24/31 permit
124.108.96.28/31 permit 124.108.96.28/31 permit
@@ -1335,20 +1376,40 @@
128.127.70.0/26 permit 128.127.70.0/26 permit
128.245.0.0/20 permit 128.245.0.0/20 permit
128.245.64.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
128.245.242.18 permit
128.245.243.0/24 permit
128.245.244.0/24 permit
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.77.70 permit
129.41.169.249 permit 129.41.169.249 permit
129.80.5.164 permit
129.80.67.121 permit
129.145.74.12 permit
129.146.88.28 permit
129.146.147.105 permit
129.146.236.58 permit 129.146.236.58 permit
129.151.67.221 permit
129.153.62.216 permit
129.153.104.71 permit
129.153.168.146 permit
129.153.190.200 permit
129.153.194.228 permit 129.153.194.228 permit
129.159.87.137 permit 129.159.87.137 permit
129.213.195.191 permit
130.61.9.72 permit 130.61.9.72 permit
130.162.39.83 permit
130.211.0.0/22 permit 130.211.0.0/22 permit
130.248.172.0/24 permit
130.248.173.0/24 permit
131.107.0.0/16 permit
131.253.30.0/24 permit 131.253.30.0/24 permit
131.253.121.0/26 permit 131.253.121.0/26 permit
131.253.121.20 permit
131.253.121.52 permit
132.145.13.209 permit 132.145.13.209 permit
132.226.26.225 permit 132.226.26.225 permit
132.226.49.32 permit 132.226.49.32 permit
@@ -1358,45 +1419,67 @@
134.170.141.64/26 permit 134.170.141.64/26 permit
134.170.143.0/24 permit 134.170.143.0/24 permit
134.170.174.0/24 permit 134.170.174.0/24 permit
135.84.80.192/26 permit 135.84.80.0/24 permit
135.84.81.0/24 permit
135.84.82.0/24 permit 135.84.82.0/24 permit
135.84.83.0/24 permit
135.84.216.0/22 permit 135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.182.0/23 permit 136.143.182.0/23 permit
136.143.184.0/24 permit 136.143.184.0/24 permit
136.143.188.0/24 permit 136.143.188.0/24 permit
136.143.190.0/23 permit
136.147.128.0/20 permit 136.147.128.0/20 permit
136.147.135.0/24 permit 136.147.135.0/24 permit
136.147.176.0/20 permit 136.147.176.0/20 permit
136.147.176.0/24 permit 136.147.176.0/24 permit
136.147.182.0/24 permit 136.147.182.0/24 permit
136.179.50.206 permit
138.91.172.26 permit 138.91.172.26 permit
139.60.152.0/22 permit 139.60.152.0/22 permit
139.178.64.159 permit 139.138.35.44 permit
139.178.64.195 permit 139.138.46.121 permit
139.138.46.176 permit
139.138.46.219 permit
139.138.57.55 permit
139.138.58.119 permit
139.180.17.0/24 permit
141.148.159.229 permit
141.193.32.0/23 permit 141.193.32.0/23 permit
143.55.224.0/21 permit 143.55.224.0/21 permit
143.55.232.0/22 permit 143.55.232.0/22 permit
143.55.236.0/22 permit 143.55.236.0/22 permit
143.244.80.0/20 permit
144.24.6.140 permit
144.34.8.247 permit
144.34.9.247 permit
144.34.32.247 permit
144.34.33.247 permit
144.178.36.0/24 permit 144.178.36.0/24 permit
144.178.38.0/24 permit 144.178.38.0/24 permit
145.253.228.160/29 permit
145.253.239.128/29 permit
146.20.112.0/26 permit 146.20.112.0/26 permit
146.20.113.0/24 permit 146.20.113.0/24 permit
146.20.191.0/24 permit 146.20.191.0/24 permit
146.20.215.0/24 permit 146.20.215.0/24 permit
146.101.78.0/24 permit 146.20.215.182 permit
147.75.65.173 permit 146.88.28.0/24 permit
147.75.65.174 permit
147.75.98.190 permit
147.160.158.0/24 permit
147.243.1.47 permit 147.243.1.47 permit
147.243.1.48 permit 147.243.1.48 permit
147.243.1.153 permit 147.243.1.153 permit
147.243.128.24 permit 147.243.128.24 permit
147.243.128.26 permit 147.243.128.26 permit
148.105.0.14 permit 148.105.0.0/16 permit
148.105.8.0/21 permit 148.105.8.0/21 permit
149.72.0.0/16 permit 149.72.0.0/16 permit
149.72.248.236 permit
149.97.173.180 permit
150.230.98.160 permit
152.67.105.195 permit 152.67.105.195 permit
152.69.200.236 permit
155.248.208.51 permit
157.55.0.192/26 permit 157.55.0.192/26 permit
157.55.1.128/26 permit 157.55.1.128/26 permit
157.55.2.0/25 permit 157.55.2.0/25 permit
@@ -1412,37 +1495,54 @@
157.56.232.0/21 permit 157.56.232.0/21 permit
157.56.240.0/20 permit 157.56.240.0/20 permit
157.56.248.0/21 permit 157.56.248.0/21 permit
157.58.30.128/25 permit
157.58.196.96/29 permit 157.58.196.96/29 permit
157.58.249.3 permit 157.58.249.3 permit
157.151.208.65 permit 157.151.208.65 permit
157.255.1.64/29 permit 157.255.1.64/29 permit
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.0/24 permit
159.92.157.16 permit
159.92.157.17 permit
159.92.157.18 permit
159.92.158.0/24 permit 159.92.158.0/24 permit
159.92.159.0/24 permit 159.92.159.0/24 permit
159.92.160.0/24 permit 159.92.160.0/24 permit
159.92.161.0/24 permit 159.92.161.0/24 permit
159.92.162.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 159.135.132.128/25 permit
159.135.140.80/29 permit 159.135.140.80/29 permit
159.135.224.0/20 permit 159.135.224.0/20 permit
159.135.228.10 permit
159.183.0.0/16 permit 159.183.0.0/16 permit
160.1.62.192 permit
161.38.192.0/20 permit 161.38.192.0/20 permit
161.38.204.0/22 permit 161.38.204.0/22 permit
161.71.32.0/19 permit 161.71.32.0/19 permit
161.71.64.0/20 permit 161.71.64.0/20 permit
162.208.119.181 permit
162.247.216.0/22 permit 162.247.216.0/22 permit
163.47.180.0/22 permit
163.47.180.0/23 permit 163.47.180.0/23 permit
163.114.130.16 permit 163.114.130.16 permit
163.114.132.120 permit 163.114.132.120 permit
165.173.128.0/24 permit
166.78.68.0/22 permit 166.78.68.0/22 permit
166.78.68.221 permit 166.78.68.221 permit
166.78.69.146 permit
166.78.69.169 permit 166.78.69.169 permit
166.78.69.170 permit 166.78.69.170 permit
166.78.71.131 permit 166.78.71.131 permit
167.89.0.0/17 permit 167.89.0.0/17 permit
167.89.46.159 permit 167.89.46.159 permit
167.89.54.103 permit
167.89.64.9 permit 167.89.64.9 permit
167.89.65.0 permit 167.89.65.0 permit
167.89.65.53 permit 167.89.65.53 permit
@@ -1457,10 +1557,18 @@
167.216.129.210 permit 167.216.129.210 permit
167.216.131.180 permit 167.216.131.180 permit
167.220.67.232/29 permit 167.220.67.232/29 permit
167.220.67.238 permit
168.138.5.36 permit 168.138.5.36 permit
168.138.73.51 permit
168.245.0.0/17 permit 168.245.0.0/17 permit
168.245.12.252 permit
168.245.46.9 permit
168.245.127.231 permit
169.148.129.0/24 permit
169.148.131.0/24 permit
169.148.142.10 permit
169.148.144.0/25 permit
170.10.68.0/22 permit 170.10.68.0/22 permit
170.10.128.0/24 permit
170.10.129.0/24 permit 170.10.129.0/24 permit
170.10.133.0/24 permit 170.10.133.0/24 permit
172.217.0.0/19 permit 172.217.0.0/19 permit
@@ -1475,10 +1583,8 @@
173.194.0.0/16 permit 173.194.0.0/16 permit
173.203.79.182 permit 173.203.79.182 permit
173.203.81.39 permit 173.203.81.39 permit
173.224.160.128/25 permit
173.224.160.188 permit
173.224.161.128/25 permit 173.224.161.128/25 permit
173.228.155.0/24 permit 173.224.165.0/26 permit
174.36.84.8/29 permit 174.36.84.8/29 permit
174.36.84.16/29 permit 174.36.84.16/29 permit
174.36.84.32/29 permit 174.36.84.32/29 permit
@@ -1491,27 +1597,27 @@
174.36.114.152/29 permit 174.36.114.152/29 permit
174.37.67.28/30 permit 174.37.67.28/30 permit
174.129.203.189 permit 174.129.203.189 permit
175.41.215.51 permit
176.32.105.0/24 permit 176.32.105.0/24 permit
176.32.127.0/24 permit 176.32.127.0/24 permit
178.236.10.128/26 permit 178.236.10.128/26 permit
180.189.28.0/24 permit
182.50.76.0/22 permit 182.50.76.0/22 permit
182.50.78.64/28 permit 182.50.78.64/28 permit
183.240.219.64/29 permit 183.240.219.64/29 permit
185.4.120.0/23 permit
185.4.122.0/24 permit
185.12.80.0/22 permit 185.12.80.0/22 permit
185.28.196.0/22 permit
185.58.84.93 permit 185.58.84.93 permit
185.58.85.0/24 permit
185.58.86.0/24 permit
185.72.128.75 permit
185.72.128.76 permit
185.72.128.80 permit
185.80.93.204 permit 185.80.93.204 permit
185.80.93.227 permit 185.80.93.227 permit
185.80.95.31 permit 185.80.95.31 permit
185.90.20.0/22 permit
185.189.236.0/22 permit 185.189.236.0/22 permit
185.211.120.0/22 permit 185.211.120.0/22 permit
185.250.236.0/22 permit 185.250.236.0/22 permit
185.250.239.148 permit
185.250.239.168 permit
185.250.239.190 permit
188.125.68.132 permit 188.125.68.132 permit
188.125.68.152/31 permit 188.125.68.152/31 permit
188.125.68.156 permit 188.125.68.156 permit
@@ -1563,7 +1669,7 @@
188.125.85.238 permit 188.125.85.238 permit
188.172.128.0/20 permit 188.172.128.0/20 permit
192.0.64.0/18 permit 192.0.64.0/18 permit
192.28.128.0/18 permit 192.18.139.154 permit
192.30.252.0/22 permit 192.30.252.0/22 permit
192.64.236.0/24 permit 192.64.236.0/24 permit
192.64.237.0/24 permit 192.64.237.0/24 permit
@@ -1579,17 +1685,17 @@
192.254.113.10 permit 192.254.113.10 permit
192.254.113.101 permit 192.254.113.101 permit
192.254.114.176 permit 192.254.114.176 permit
192.254.118.63 permit
193.7.206.0/25 permit
193.7.207.0/25 permit
193.109.254.0/23 permit 193.109.254.0/23 permit
193.122.128.100 permit 193.122.128.100 permit
193.123.56.63 permit
194.19.134.0/25 permit
194.64.234.128/27 permit
194.64.234.129 permit 194.64.234.129 permit
194.104.109.0/24 permit
194.104.111.0/24 permit
194.106.220.0/23 permit 194.106.220.0/23 permit
194.113.24.0/22 permit
194.154.193.192/27 permit 194.154.193.192/27 permit
195.130.217.0/24 permit 195.4.92.0/23 permit
195.54.172.0/23 permit
195.234.109.226 permit 195.234.109.226 permit
195.245.230.0/23 permit 195.245.230.0/23 permit
198.2.128.0/18 permit 198.2.128.0/18 permit
@@ -1605,19 +1711,24 @@
198.37.144.0/20 permit 198.37.144.0/20 permit
198.37.152.186 permit 198.37.152.186 permit
198.61.254.0/23 permit 198.61.254.0/23 permit
198.61.254.21 permit
198.61.254.231 permit 198.61.254.231 permit
198.74.56.28 permit
198.178.234.57 permit 198.178.234.57 permit
198.244.48.0/20 permit
198.244.60.0/22 permit
198.245.80.0/20 permit 198.245.80.0/20 permit
198.245.81.0/24 permit 198.245.81.0/24 permit
199.15.176.173 permit
199.15.212.0/22 permit
199.15.213.187 permit 199.15.213.187 permit
199.15.226.37 permit 199.15.226.37 permit
199.16.156.0/22 permit 199.16.156.0/22 permit
199.33.145.1 permit 199.33.145.1 permit
199.33.145.32 permit 199.33.145.32 permit
199.34.22.36 permit
199.59.148.0/22 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
199.101.161.130 permit 199.101.161.130 permit
199.101.162.0/25 permit 199.101.162.0/25 permit
199.122.120.0/21 permit 199.122.120.0/21 permit
@@ -1630,8 +1741,10 @@
202.177.148.110 permit 202.177.148.110 permit
203.31.36.0/22 permit 203.31.36.0/22 permit
203.32.4.25 permit 203.32.4.25 permit
203.55.21.0/24 permit
203.81.17.0/24 permit 203.81.17.0/24 permit
203.122.32.250 permit 203.122.32.250 permit
203.145.57.160/27 permit
203.188.194.32 permit 203.188.194.32 permit
203.188.194.151 permit 203.188.194.151 permit
203.188.194.203 permit 203.188.194.203 permit
@@ -1666,28 +1779,32 @@
203.209.230.76/31 permit 203.209.230.76/31 permit
204.11.168.0/21 permit 204.11.168.0/21 permit
204.13.11.48/29 permit 204.13.11.48/29 permit
204.13.11.48/30 permit
204.14.232.0/21 permit 204.14.232.0/21 permit
204.14.232.64/28 permit 204.14.232.64/28 permit
204.14.234.64/28 permit 204.14.234.64/28 permit
204.29.186.0/23 permit 204.29.186.0/23 permit
204.75.142.0/24 permit
204.79.197.212 permit 204.79.197.212 permit
204.92.114.187 permit 204.92.114.187 permit
204.92.114.203 permit 204.92.114.203 permit
204.92.114.204/31 permit 204.92.114.204/31 permit
204.141.32.0/23 permit 204.141.32.0/23 permit
204.141.42.0/23 permit 204.141.42.0/23 permit
204.153.121.0/24 permit 204.220.160.0/20 permit
204.232.168.0/24 permit 204.232.168.0/24 permit
205.139.110.0/24 permit 205.139.110.0/24 permit
205.201.128.0/20 permit 205.201.128.0/20 permit
205.201.131.128/25 permit 205.201.131.128/25 permit
205.201.134.128/25 permit 205.201.134.128/25 permit
205.201.136.0/23 permit 205.201.136.0/23 permit
205.201.137.229 permit
205.201.139.0/24 permit 205.201.139.0/24 permit
205.207.104.0/22 permit 205.207.104.0/22 permit
205.207.104.108 permit
205.220.167.17 permit 205.220.167.17 permit
205.220.167.98 permit
205.220.179.17 permit 205.220.179.17 permit
205.220.179.98 permit
205.251.233.32 permit 205.251.233.32 permit
205.251.233.36 permit 205.251.233.36 permit
206.25.247.143 permit 206.25.247.143 permit
@@ -1715,7 +1832,6 @@
207.67.98.192/27 permit 207.67.98.192/27 permit
207.68.176.0/26 permit 207.68.176.0/26 permit
207.68.176.96/27 permit 207.68.176.96/27 permit
207.82.80.0/24 permit
207.126.144.0/20 permit 207.126.144.0/20 permit
207.171.160.0/19 permit 207.171.160.0/19 permit
207.211.30.64/26 permit 207.211.30.64/26 permit
@@ -1723,6 +1839,7 @@
207.211.31.0/25 permit 207.211.31.0/25 permit
207.211.41.113 permit 207.211.41.113 permit
207.218.90.0/24 permit 207.218.90.0/24 permit
207.218.90.122 permit
207.250.68.0/24 permit 207.250.68.0/24 permit
208.40.232.70 permit 208.40.232.70 permit
208.43.21.28/30 permit 208.43.21.28/30 permit
@@ -1758,8 +1875,10 @@
208.71.42.212/31 permit 208.71.42.212/31 permit
208.71.42.214 permit 208.71.42.214 permit
208.72.249.240/29 permit 208.72.249.240/29 permit
208.74.204.0/22 permit
208.74.204.9 permit 208.74.204.9 permit
208.75.120.0/22 permit 208.75.120.0/22 permit
208.75.121.246 permit
208.75.122.246 permit 208.75.122.246 permit
208.82.237.96/29 permit 208.82.237.96/29 permit
208.82.237.104/31 permit 208.82.237.104/31 permit
@@ -1773,14 +1892,12 @@
209.46.117.168 permit 209.46.117.168 permit
209.46.117.179 permit 209.46.117.179 permit
209.61.151.0/24 permit 209.61.151.0/24 permit
209.61.151.236 permit
209.61.151.249 permit
209.61.151.251 permit
209.67.98.46 permit 209.67.98.46 permit
209.67.98.59 permit 209.67.98.59 permit
209.85.128.0/17 permit 209.85.128.0/17 permit
212.4.136.0/26 permit
212.25.240.80 permit
212.25.240.83 permit
212.25.240.84/31 permit
212.25.240.88 permit
212.82.96.0/24 permit 212.82.96.0/24 permit
212.82.96.32/27 permit 212.82.96.32/27 permit
212.82.96.64/29 permit 212.82.96.64/29 permit
@@ -1821,8 +1938,12 @@
212.82.111.228/31 permit 212.82.111.228/31 permit
212.82.111.230 permit 212.82.111.230 permit
212.123.28.40 permit 212.123.28.40 permit
213.167.75.0/25 permit 212.227.15.0/24 permit
213.167.81.0/25 permit 212.227.15.0/25 permit
212.227.17.0/27 permit
212.227.126.128/25 permit
213.46.255.0/24 permit
213.165.64.0/23 permit
213.199.128.139 permit 213.199.128.139 permit
213.199.128.145 permit 213.199.128.145 permit
213.199.138.181 permit 213.199.138.181 permit
@@ -1861,6 +1982,10 @@
216.46.168.0/24 permit 216.46.168.0/24 permit
216.58.192.0/19 permit 216.58.192.0/19 permit
216.66.217.240/29 permit 216.66.217.240/29 permit
216.71.138.33 permit
216.71.152.207 permit
216.71.154.29 permit
216.71.155.89 permit
216.74.162.13 permit 216.74.162.13 permit
216.74.162.14 permit 216.74.162.14 permit
216.82.240.0/20 permit 216.82.240.0/20 permit
@@ -1870,33 +1995,48 @@
216.109.114.0/24 permit 216.109.114.0/24 permit
216.109.114.32/27 permit 216.109.114.32/27 permit
216.109.114.64/29 permit 216.109.114.64/29 permit
216.113.160.0/24 permit
216.113.172.0/25 permit
216.113.175.0/24 permit
216.128.126.97 permit 216.128.126.97 permit
216.136.162.65 permit 216.136.162.65 permit
216.136.162.120/29 permit 216.136.162.120/29 permit
216.136.168.80/28 permit 216.136.168.80/28 permit
216.145.221.0/24 permit
216.198.0.0/18 permit 216.198.0.0/18 permit
216.203.30.55 permit 216.203.30.55 permit
216.203.33.178/31 permit 216.203.33.178/31 permit
216.205.24.0/24 permit 216.205.24.0/24 permit
216.239.32.0/19 permit 216.239.32.0/19 permit
217.72.192.64/26 permit
217.72.192.248/29 permit
217.72.207.0/27 permit
217.77.141.52 permit 217.77.141.52 permit
217.77.141.59 permit 217.77.141.59 permit
217.175.194.0/24 permit
222.73.195.64/29 permit 222.73.195.64/29 permit
223.165.113.0/24 permit 223.165.113.0/24 permit
223.165.115.0/24 permit 223.165.115.0/24 permit
223.165.118.0/23 permit 223.165.118.0/23 permit
223.165.120.0/23 permit 223.165.120.0/23 permit
2001:0868:0100:0600::/64 permit
2001:4860:4000::/36 permit 2001:4860:4000::/36 permit
2001:748:100:40::2:0/112 permit
2404:6800:4000::/36 permit 2404:6800:4000::/36 permit
2603:1010:3:3::5b permit
2603:1020:201:10::10f permit
2603:1030:20e:3::23c permit
2603:1030:b:3::152 permit
2603:1030:c02:8::14 permit
2607:f8b0:4000::/36 permit 2607:f8b0:4000::/36 permit
2620:109:c003:104::215 permit
2620:109:c003:104::/64 permit 2620:109:c003:104::/64 permit
2620:109:c006:104::215 permit 2620:109:c003:104::215 permit
2620:109:c006:104::/64 permit 2620:109:c006:104::/64 permit
2620:109:c006:104::215 permit
2620:109:c00d:104::/64 permit 2620:109:c00d:104::/64 permit
2620:10d:c090:450::120 permit 2620:10d:c090:400::8:1 permit
2620:10d:c091:450::16 permit 2620:10d:c091:400::8:1 permit
2620:119:50c0:207::215 permit
2620:119:50c0:207::/64 permit 2620:119:50c0:207::/64 permit
2620:119:50c0:207::215 permit
2800:3f0:4000::/36 permit 2800:3f0:4000::/36 permit
194.25.134.0/24 permit # t-online.de 194.25.134.0/24 permit # t-online.de

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

@@ -68,3 +68,39 @@ WL_FWD_HOST {
ENCRYPTED_CHAT { ENCRYPTED_CHAT {
expression = "CHAT_VERSION_HEADER & ENCRYPTED_PGP"; expression = "CHAT_VERSION_HEADER & ENCRYPTED_PGP";
} }
CLAMD_SPAM_FOUND {
expression = "CLAM_SECI_SPAM & !MAILCOW_WHITE";
description = "Probably Spam, Securite Spam Flag set through ClamAV";
score = 5;
}
CLAMD_BAD_PDF {
expression = "CLAM_SECI_PDF & !MAILCOW_WHITE";
description = "Bad PDF Found, Securite bad PDF Flag set through ClamAV";
score = 8;
}
CLAMD_BAD_JPG {
expression = "CLAM_SECI_JPG & !MAILCOW_WHITE";
description = "Bad JPG Found, Securite bad JPG Flag set through ClamAV";
score = 8;
}
CLAMD_ASCII_MALWARE {
expression = "CLAM_SECI_ASCII & !MAILCOW_WHITE";
description = "ASCII malware found, Securite ASCII malware Flag set through ClamAV";
score = 8;
}
CLAMD_HTML_MALWARE {
expression = "CLAM_SECI_HTML & !MAILCOW_WHITE";
description = "HTML malware found, Securite HTML malware Flag set through ClamAV";
score = 8;
}
CLAMD_JS_MALWARE {
expression = "CLAM_SECI_JS & !MAILCOW_WHITE";
description = "JS malware found, Securite JS malware Flag set through ClamAV";
score = 8;
}

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

@@ -221,6 +221,16 @@ rspamd_config:register_symbol({
local tagged_rcpt = task:get_symbol("TAGGED_RCPT") local tagged_rcpt = task:get_symbol("TAGGED_RCPT")
local mailcow_domain = task:get_symbol("RCPT_MAILCOW_DOMAIN") local mailcow_domain = task:get_symbol("RCPT_MAILCOW_DOMAIN")
local function remove_moo_tag()
local moo_tag_header = task:get_header('X-Moo-Tag', false)
if moo_tag_header then
task:set_milter_reply({
remove_headers = {['X-Moo-Tag'] = 0},
})
end
return true
end
if tagged_rcpt and tagged_rcpt[1].options and mailcow_domain then if tagged_rcpt and tagged_rcpt[1].options and mailcow_domain then
local tag = tagged_rcpt[1].options[1] local tag = tagged_rcpt[1].options[1]
rspamd_logger.infox("found tag: %s", tag) rspamd_logger.infox("found tag: %s", tag)
@@ -229,6 +239,7 @@ rspamd_config:register_symbol({
if action ~= 'no action' and action ~= 'greylist' then if action ~= 'no action' and action ~= 'greylist' then
rspamd_logger.infox("skipping tag handler for action: %s", action) rspamd_logger.infox("skipping tag handler for action: %s", action)
remove_moo_tag()
return true return true
end end
@@ -243,6 +254,7 @@ rspamd_config:register_symbol({
local function tag_callback_subfolder(err, data) local function tag_callback_subfolder(err, data)
if err or type(data) ~= 'string' then if err or type(data) ~= 'string' then
rspamd_logger.infox(rspamd_config, "subfolder tag handler for rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\")", body, data, err) rspamd_logger.infox(rspamd_config, "subfolder tag handler for rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\")", body, data, err)
remove_moo_tag()
else else
rspamd_logger.infox("Add X-Moo-Tag header") rspamd_logger.infox("Add X-Moo-Tag header")
task:set_milter_reply({ task:set_milter_reply({
@@ -261,6 +273,7 @@ rspamd_config:register_symbol({
) )
if not redis_ret_subfolder then if not redis_ret_subfolder then
rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt") rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt")
remove_moo_tag()
end end
else else
@@ -268,7 +281,10 @@ rspamd_config:register_symbol({
local sbj = task:get_header('Subject') local sbj = task:get_header('Subject')
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?=' new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
task:set_milter_reply({ task:set_milter_reply({
remove_headers = {['Subject'] = 1}, remove_headers = {
['Subject'] = 1,
['X-Moo-Tag'] = 0
},
add_headers = {['Subject'] = new_sbj} add_headers = {['Subject'] = new_sbj}
}) })
end end
@@ -284,6 +300,7 @@ rspamd_config:register_symbol({
) )
if not redis_ret_subject then if not redis_ret_subject then
rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt") rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt")
remove_moo_tag()
end end
end end
@@ -295,6 +312,7 @@ rspamd_config:register_symbol({
if #rcpt_split == 2 then if #rcpt_split == 2 then
if rcpt_split[1] == 'postmaster' then if rcpt_split[1] == 'postmaster' then
rspamd_logger.infox(rspamd_config, "not expanding postmaster alias") rspamd_logger.infox(rspamd_config, "not expanding postmaster alias")
remove_moo_tag()
else else
rspamd_http.request({ rspamd_http.request({
task=task, task=task,
@@ -307,7 +325,8 @@ rspamd_config:register_symbol({
end end
end end
end end
else
remove_moo_tag()
end end
end, end,
priority = 19 priority = 19
@@ -503,3 +522,158 @@ rspamd_config:register_symbol({
end end
end end
}) })
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_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()
local env_from_addr = envfrom[1].addr:lower()
-- determine newline type
local function newline(task)
local t = task:get_newlines_type()
if t == 'cr' then
return '\r'
elseif t == 'lf' then
return '\n'
end
return '\r\n'
end
-- 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 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
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 = ""
if envfrom_mime and envfrom_mime[1].name then
from_name = envfrom_mime[1].name
elseif envfrom and envfrom[1].name then
from_name = envfrom[1].name
end
-- default replacements
local replacements = {
auth_user = uname,
from_user = envfrom[1].user,
from_name = from_name,
from_addr = envfrom[1].addr,
from_domain = envfrom[1].domain:lower()
}
-- 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 and footer.plain ~= "" then
footer.plain = lua_util.jinja_template(footer.plain, replacements, true)
end
-- add footer
local out = {}
local rewrite = lua_mime.add_text_footer(task, footer.html, footer.plain) or {}
local seen_cte
local newline_s = newline(task)
local function rewrite_ct_cb(name, hdr)
if rewrite.need_rewrite_ct then
if name:lower() == 'content-type' then
local nct = string.format('%s: %s/%s; charset=utf-8',
'Content-Type', rewrite.new_ct.type, rewrite.new_ct.subtype)
out[#out + 1] = nct
return
elseif name:lower() == 'content-transfer-encoding' then
out[#out + 1] = string.format('%s: %s',
'Content-Transfer-Encoding', 'quoted-printable')
seen_cte = true
return
end
end
out[#out + 1] = hdr.raw:gsub('\r?\n?$', '')
end
task:headers_foreach(rewrite_ct_cb, {full = true})
if not seen_cte and rewrite.need_rewrite_ct then
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', 'quoted-printable')
end
-- End of headers
out[#out + 1] = newline_s
if rewrite.out then
for _,o in ipairs(rewrite.out) do
out[#out + 1] = o
end
else
out[#out + 1] = task:get_rawbody()
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
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
rspamd_logger.infox(rspamd_config, "domain wide footer request for user %s returned invalid or empty data (\"%s\")", uname, data)
end
end
end
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,
priority = 1
})

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"; whitelisted_rcpts = "postmaster,mailer-daemon";
max_rcpt = 25; max_rcpt = 25;
custom_keywords = "/etc/rspamd/lua/ratelimit.lua"; custom_keywords = "/etc/rspamd/lua/ratelimit.lua";

View File

@@ -83,6 +83,7 @@
//SoDebugBaseURL = YES; //SoDebugBaseURL = YES;
//ImapDebugEnabled = YES; //ImapDebugEnabled = YES;
//SOGoEASDebugEnabled = YES; //SOGoEASDebugEnabled = YES;
SOGoEASSearchInBody = YES; // Experimental. Enabled since 2023-10
//LDAPDebugEnabled = YES; //LDAPDebugEnabled = YES;
//PGDebugEnabled = YES; //PGDebugEnabled = YES;
//MySQL4DebugEnabled = YES; //MySQL4DebugEnabled = YES;

View File

@@ -20,6 +20,6 @@
<pre>BACKUP_LOCATION=/tmp/ ./helper-scripts/backup_and_restore.sh backup all</pre> <pre>BACKUP_LOCATION=/tmp/ ./helper-scripts/backup_and_restore.sh backup all</pre>
<pre>docker compose down --volumes ; docker compose up -d</pre> <pre>docker compose down --volumes ; docker compose up -d</pre>
<p>Make sure your timezone is correct. Use "America/New_York" for example, do not use spaces. Check <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">here</a> for a list.</p> <p>Make sure your timezone is correct. Use "America/New_York" for example, do not use spaces. Check <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">here</a> for a list.</p>
<br>Click to learn more about <a style="color:red;text-decoration:none;" href="https://mailcow.github.io/mailcow-dockerized-docs/#get-support" target="_blank">getting support.</a> <br>Click to learn more about <a style="color:red;text-decoration:none;" href="https://docs.mailcow.email/#get-support" target="_blank">getting support.</a>
</body> </body>
</html> </html>

View File

@@ -111,6 +111,7 @@ $template_data = [
'rsettings' => $rsettings, 'rsettings' => $rsettings,
'rspamd_regex_maps' => $rspamd_regex_maps, 'rspamd_regex_maps' => $rspamd_regex_maps,
'logo_specs' => customize('get', 'main_logo_specs'), 'logo_specs' => customize('get', 'main_logo_specs'),
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
'ip_check' => customize('get', 'ip_check'), 'ip_check' => customize('get', 'ip_check'),
'password_complexity' => password_complexity('get'), 'password_complexity' => password_complexity('get'),
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'], 'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],

View File

@@ -3137,6 +3137,86 @@ paths:
type: string type: string
type: object type: object
summary: Update domain 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: /api/v1/edit/fail2ban:
post: post:
responses: responses:
@@ -3336,6 +3416,86 @@ paths:
type: object type: object
type: object type: object
summary: Update mailbox 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: /api/v1/edit/mailq:
post: post:
responses: responses:
@@ -5581,6 +5741,7 @@ paths:
sogo_access: "1" sogo_access: "1"
tls_enforce_in: "0" tls_enforce_in: "0"
tls_enforce_out: "0" tls_enforce_out: "0"
custom_attributes: {}
domain: domain3.tld domain: domain3.tld
is_relayed: 0 is_relayed: 0
local_part: info local_part: info
@@ -5602,6 +5763,84 @@ paths:
description: You can list all mailboxes existing in system for a specific domain. description: You can list all mailboxes existing in system for a specific domain.
operationId: Get mailboxes of a domain operationId: Get mailboxes of a domain
summary: Get mailboxes of a domain summary: Get mailboxes of a domain
/api/v1/edit/cors:
post:
responses:
"401":
$ref: "#/components/responses/Unauthorized"
"200":
content:
application/json:
examples:
response:
value:
- type: "success"
log: ["cors", "edit", {"allowed_origins": ["*", "mail.mailcow.tld"], "allowed_methods": ["POST", "GET", "DELETE", "PUT"]}]
msg: "cors_headers_edited"
description: OK
headers: { }
tags:
- Cross-Origin Resource Sharing (CORS)
description: >-
This endpoint allows you to manage Cross-Origin Resource Sharing (CORS) settings for the API.
CORS is a security feature implemented by web browsers to prevent unauthorized cross-origin requests.
By editing the CORS settings, you can specify which domains and which methods are permitted to access the API resources from outside the mailcow domain.
operationId: Edit Cross-Origin Resource Sharing (CORS) settings
requestBody:
content:
application/json:
schema:
example:
attr:
allowed_origins: ["*", "mail.mailcow.tld"]
allowed_methods: ["POST", "GET", "DELETE", "PUT"]
properties:
attr:
type: object
properties:
allowed_origins:
type: array
items:
type: string
allowed_methods:
type: array
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: tags:
- name: Domains - name: Domains
@@ -5646,3 +5885,5 @@ tags:
description: Get the status of your cow description: Get the status of your cow
- name: Ratelimits - name: Ratelimits
description: Edit domain ratelimits description: Edit domain ratelimits
- name: Cross-Origin Resource Sharing (CORS)
description: Manage Cross-Origin Resource Sharing (CORS) settings

View File

@@ -42,11 +42,6 @@ table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
table.dataTable td.dt-control:before { table.dataTable td.dt-control:before {
background-color: #979797 !important; background-color: #979797 !important;
} }
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
background-color: #fbfbfb;
}
table.dataTable.table-striped>tbody>tr>td { table.dataTable.table-striped>tbody>tr>td {
vertical-align: middle; vertical-align: middle;
} }

View File

@@ -357,6 +357,7 @@ button[aria-expanded='true'] > .caret {
} }
.progress { .progress {
height: 16px;
background-color: #d5d5d5; background-color: #d5d5d5;
} }
@@ -370,3 +371,22 @@ button[aria-expanded='true'] > .caret {
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #f0f0f0 !important; background-color: #f0f0f0 !important;
} }
.btn-check:checked+.btn-light, .btn-check:active+.btn-light, .btn-light:active, .btn-light.active, .show>.btn-light.dropdown-toggle {
color: #fff;
background-color: #555;
background-image: none;
border-color: #4d4d4d;
}
.btn-check:checked+.btn-light:focus, .btn-check:active+.btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show>.btn-light.dropdown-toggle:focus,
.btn-check:focus+.btn-light, .btn-light:focus {
box-shadow: none;
}
.btn-group>.btn:not(:last-of-type) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.badge.bg-info > a,
.badge.bg-danger > a {
color: #fff !important;
text-decoration: none;
}

View File

@@ -38,7 +38,7 @@
@media (max-width: 767px) { @media (max-width: 767px) {
.responsive-tabs .tab-pane { .responsive-tabs .tab-pane:not(.rsettings) {
display: block !important; display: block !important;
opacity: 1; opacity: 1;
} }
@@ -206,6 +206,19 @@
.senders-mw220 { .senders-mw220 {
max-width: 100% !important; max-width: 100% !important;
} }
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before,
table.dataTable td.dt-control:before {
height: 2rem;
width: 2rem;
line-height: 2rem;
margin-top: -15px;
}
li .dtr-data {
padding: 0;
}
} }
@media (max-width: 350px) { @media (max-width: 350px) {

View File

@@ -1,90 +1,128 @@
body { body {
background-color: #414141; background-color: #1c1c1e;
color: #e0e0e0; color: #f2f2f7;
} }
.card { .card {
border: 1px solid #1c1c1c; border: 1px solid #2c2c2e;
background-color: #3a3a3a; background-color: #2c2c2e;
} }
legend { legend {
color: #f5f5f5; color: #f2f2f7;
} }
.card-header { .card-header {
color: #bbb; color: #8e8e93;
background-color: #2c2c2c; background-color: #1c1c1e;
border-color: transparent; border-color: transparent;
} }
.card-body {
--bs-card-color: #bbb;
}
.btn-secondary, .paginate_button, .page-link, .btn-light { .btn-secondary, .paginate_button, .page-link, .btn-light {
color: #fff !important; color: #f2f2f7 !important;
background-color: #7a7a7a !important; background-color: #5e5e5e !important;
border-color: #5c5c5c !important; border-color: #4c4c4e !important;
} }
.btn-dark { .btn-dark {
color: #000 !important;; color: #f2f2f7 !important;
background-color: #f6f6f6 !important;; background-color: #242424 !important;
border-color: #ddd !important;; border-color: #1c1c1e !important;
}
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
border-color: #7a7a7a !important;
}
.alert-secondary {
color: #fff !important;
background-color: #7a7a7a !important;
border-color: #5c5c5c !important;
}
.bg-secondary {
color: #fff !important;
background-color: #7a7a7a !important;
}
.alert-secondary, .alert-secondary a, .alert-secondary .alert-link {
color: #fff;
}
.page-item.active .page-link {
background-color: #158cba !important;
border-color: #127ba3 !important;
} }
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary { .btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
background-color: #7a7a7a; background-color: #444444;
border-color: #5c5c5c !important; border-color: #4c4c4e !important;
color: #fff; color: #f2f2f7;
} }
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
border-color: #5e5e5e !important;
}
.alert-secondary {
color: #f2f2f7 !important;
background-color: #5e5e5e !important;
border-color: #4c4c4e !important;
}
.bg-secondary {
color: #f2f2f7 !important;
background-color: #5e5e5e !important;
}
.alert-secondary, .alert-secondary a, .alert-secondary .alert-link {
color: #f2f2f7;
}
.page-item.active .page-link {
background-color: #3e3e3e !important;
border-color: #3e3e3e !important;
}
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
background-color: #5e5e5e;
border-color: #4c4c4e !important;
color: #f2f2f7;
}
.btn-secondary:disabled, .btn-secondary.disabled { .btn-secondary:disabled, .btn-secondary.disabled {
border-color: #7a7a7a !important; border-color: #5e5e5e !important;
} }
.modal-content { .modal-content {
background-color: #414141; --bs-modal-color: #bbb;
background-color: #2c2c2e;
} }
.modal-header { .modal-header {
border-bottom: 1px solid #161616; border-bottom: 1px solid #999;
} }
.modal-title { .modal-title {
color: white; color: #bbb;
} }
.modal .btn-close { .modal .btn-close {
filter: invert(1) grayscale(100%) brightness(200%); filter: invert(1) grayscale(100%) brightness(200%);
} }
.navbar.bg-light { .navbar.bg-light {
background-color: #222222 !important; background-color: #1c1c1e !important;
border-color: #181818; border-color: #2c2c2e;
} }
.nav-link { .nav-link {
color: #ccc !important; color: #8e8e93 !important;
} }
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
background: none; background: none;
} }
.nav-tabs, .nav-tabs .nav-link {
border-color: #444444 !important;
}
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus, .nav-tabs .nav-link.active { .nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus, .nav-tabs .nav-link.active {
border-bottom-color: #414141; border-bottom-color: #1c1c1e !important;
}
.card .nav-tabs .nav-link:not(.disabled):hover, .card .nav-tabs .nav-link:not(.disabled):focus, .card .nav-tabs .nav-link.active {
border-bottom-color: #2c2c2e !important;
} }
.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr { .table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
color: #ccc !important; color: #f2f2f7 !important;
} }
.dropdown-menu { .dropdown-menu {
background-color: #585858; background-color: #424242;
border: 1px solid #333; border: 1px solid #282828;
} }
.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover { .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {
color: #fafafa; color: #fafafa;
@@ -97,7 +135,7 @@ legend {
color: #d4d4d4 !important; color: #d4d4d4 !important;
} }
tbody tr { tbody tr {
color: #555; color: #ccc;
} }
.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover { .navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover {
color: #ccc; color: #ccc;
@@ -106,18 +144,15 @@ tbody tr {
color: #ccc; color: #ccc;
} }
.list-group-item { .list-group-item {
background-color: #333; background-color: #282828;
border: 1px solid #555; border: 1px solid #555;
} }
.table-striped>tbody>tr:nth-of-type(odd) { .table-striped>tbody>tr:nth-of-type(odd) {
background-color: #333; background-color: #424242;
} }
table.dataTable>tbody>tr.child ul.dtr-details>li { table.dataTable>tbody>tr.child ul.dtr-details>li {
border-bottom: 1px solid rgba(255, 255, 255, 0.13); border-bottom: 1px solid rgba(255, 255, 255, 0.13);
} }
tbody tr {
color: #ccc;
}
.label.label-last-login { .label.label-last-login {
color: #ccc !important; color: #ccc !important;
background-color: #555 !important; background-color: #555 !important;
@@ -133,20 +168,20 @@ div.numberedtextarea-number {
} }
.well { .well {
border: 1px solid #555; border: 1px solid #555;
background-color: #333; background-color: #282828;
} }
pre { pre {
color: #ccc; color: #ccc;
background-color: #333; background-color: #282828;
border: 1px solid #555; border: 1px solid #555;
} }
input.form-control, textarea.form-control { input.form-control, textarea.form-control {
color: #e2e2e2 !important; color: #e2e2e2 !important;
background-color: #555 !important; background-color: #424242 !important;
border: 1px solid #999; border: 1px solid #999;
} }
input.form-control:focus, textarea.form-control { input.form-control:focus, textarea.form-control {
background-color: #555 !important; background-color: #424242 !important;
} }
input.form-control:disabled, textarea.form-disabled { input.form-control:disabled, textarea.form-disabled {
color: #a8a8a8 !important; color: #a8a8a8 !important;
@@ -154,16 +189,14 @@ input.form-control:disabled, textarea.form-disabled {
} }
.input-group-addon { .input-group-addon {
color: #ccc; color: #ccc;
background-color: #555 !important; background-color: #424242 !important;
border: 1px solid #999; border: 1px solid #999;
} }
.input-group-text { .input-group-text {
color: #ccc; color: #ccc;
background-color: #242424; background-color: #1c1c1c;
} }
.list-group-item { .list-group-item {
color: #ccc; color: #ccc;
} }
@@ -175,11 +208,11 @@ input.form-control:disabled, textarea.form-disabled {
} }
.dropdown-item.active:hover { .dropdown-item.active:hover {
color: #fff !important; color: #fff !important;
background-color: #31b1e4; background-color: #007aff;
} }
.form-select { .form-select {
color: #e2e2e2!important; color: #e2e2e2!important;
background-color: #555!important; background-color: #424242!important;
border: 1px solid #999; border: 1px solid #999;
} }
@@ -191,31 +224,6 @@ input.form-control:disabled, textarea.form-disabled {
color: #fff !important; color: #fff !important;
} }
.table-secondary {
--bs-table-bg: #7a7a7a;
--bs-table-striped-bg: #e4e4e4;
--bs-table-striped-color: #000;
--bs-table-active-bg: #d8d8d8;
--bs-table-active-color: #000;
--bs-table-hover-bg: #dedede;
--bs-table-hover-color: #000;
color: #000;
border-color: #d8d8d8;
}
.table-light {
--bs-table-bg: #f6f6f6;
--bs-table-striped-bg: #eaeaea;
--bs-table-striped-color: #000;
--bs-table-active-bg: #dddddd;
--bs-table-active-color: #000;
--bs-table-hover-bg: #e4e4e4;
--bs-table-hover-color: #000;
color: #000;
border-color: #dddddd;
}
.form-control-plaintext { .form-control-plaintext {
color: #e0e0e0; color: #e0e0e0;
} }
@@ -289,12 +297,12 @@ a:hover {
} }
.tag-box { .tag-box {
background-color: #555; background-color: #282828;
border: 1px solid #999; border: 1px solid #555;
} }
.tag-input { .tag-input {
color: #fff; color: #fff;
background-color: #555; background-color: #282828;
} }
.tag-add { .tag-add {
color: #ccc; color: #ccc;
@@ -303,43 +311,24 @@ a:hover {
color: #d1d1d1; color: #d1d1d1;
} }
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
background-color: #7a7a7a !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before {
background-color: #7a7a7a !important;
border: 1.5px solid #5c5c5c !important;
color: #fff !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before {
background-color: #949494;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
background-color: #444444;
}
.btn-check-label { .btn-check-label {
color: #fff; color: #fff;
} }
.btn-outline-secondary:hover { .btn-outline-secondary:hover {
background-color: #c3c3c3; background-color: #5c5c5c;
} }
.btn.btn-outline-secondary { .btn.btn-outline-secondary {
color: #fff !important; color: #e0e0e0 !important;
border-color: #7a7a7a !important; border-color: #7a7a7a !important;
} }
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #9b9b9b !important; background-color: #7a7a7a !important;
}
.btn-check:checked+.btn-light, .btn-check:active+.btn-light, .btn-light:active, .btn-light.active, .show>.btn-light.dropdown-toggle {
color: #f2f2f7 !important;
background-color: #242424 !important;
border-color: #1c1c1e !important;
} }
.btn-input-missing, .btn-input-missing,
.btn-input-missing:hover, .btn-input-missing:hover,
.btn-input-missing:active, .btn-input-missing:active,
@@ -347,27 +336,119 @@ table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
.btn-input-missing:active:hover, .btn-input-missing:active:hover,
.btn-input-missing:active:focus { .btn-input-missing:active:focus {
color: #fff !important; color: #fff !important;
background-color: #ff2f24 !important; background-color: #ff3b30 !important;
border-color: #e21207 !important; border-color: #ff3b30 !important;
} }
.inputMissingAttr { .inputMissingAttr {
border-color: #FF4136 !important; border-color: #ff4136 !important;
} }
.list-group-details { .list-group-details {
background: #444444; background: #555;
} }
.list-group-header { .list-group-header {
background: #333; background: #444;
} }
span.mail-address-item { span.mail-address-item {
background-color: #333; background-color: #444;
border-radius: 4px; border-radius: 4px;
border: 1px solid #555; border: 1px solid #555;
padding: 2px 7px; padding: 2px 7px;
display: inline-block; display: inline-block;
margin: 2px 6px 2px 0; margin: 2px 6px 2px 0;
} }
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
background-color: #7a7a7a !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before {
background-color: #7a7a7a !important;
border: 1.5px solid #5c5c5c !important;
color: #e0e0e0 !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before {
background-color: #949494;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
background-color: #414141;
}
table.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
color: #ccc !important;
}
.table-secondary {
--bs-table-bg: #282828;
--bs-table-striped-bg: #343434;
--bs-table-striped-color: #f2f2f7;
--bs-table-active-bg: #4c4c4c;
--bs-table-active-color: #f2f2f7;
--bs-table-hover-bg: #3a3a3a;
--bs-table-hover-color: #f2f2f7;
color: #ccc;
border-color: #3a3a3a;
}
.table-light {
--bs-table-bg: #3a3a3a;
--bs-table-striped-bg: #444444;
--bs-table-striped-color: #f2f2f7;
--bs-table-active-bg: #5c5c5c;
--bs-table-active-color: #f2f2f7;
--bs-table-hover-bg: #4c4c4c;
--bs-table-hover-color: #f2f2f7;
color: #ccc;
border-color: #4c4c4c;
}
.table-bordered {
border-color: #3a3a3a;
}
.table-bordered th,
.table-bordered td {
border-color: #3a3a3a !important;
}
.table-bordered thead th,
.table-bordered thead td {
border-bottom-width: 2px;
}
.table-striped>tbody>tr:nth-of-type(odd)>td,
.table-striped>tbody>tr:nth-of-type(odd)>th {
background-color: #282828;
}
.table-hover>tbody>tr:hover {
background-color: #343434;
}
.table>:not(caption)>*>* {
border-color: #5c5c5c;
--bs-table-color-state:#bbb;
--bs-table-bg: #3a3a3a;
}
.text-muted {
--bs-secondary-color: #8e8e93;
}
input::placeholder {
color: #8e8e93 !important;
}
.form-select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%238e8e93' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
.btn-light, .btn-light:hover {
background-image: none;
}

View File

@@ -47,6 +47,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
$quota_notification_bcc = quota_notification_bcc('get', $domain); $quota_notification_bcc = quota_notification_bcc('get', $domain);
$rl = ratelimit('get', 'domain', $domain); $rl = ratelimit('get', 'domain', $domain);
$rlyhosts = relayhost('get'); $rlyhosts = relayhost('get');
$domain_footer = mailbox('get', 'domain_wide_footer', $domain);
$template = 'edit/domain.twig'; $template = 'edit/domain.twig';
$template_data = [ $template_data = [
'acl' => $_SESSION['acl'], 'acl' => $_SESSION['acl'],
@@ -56,23 +57,28 @@ if (isset($_SESSION['mailcow_cc_role'])) {
'rlyhosts' => $rlyhosts, 'rlyhosts' => $rlyhosts,
'dkim' => dkim('details', $domain), 'dkim' => dkim('details', $domain),
'domain_details' => $result, 'domain_details' => $result,
'domain_footer' => $domain_footer,
'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address')
]; ];
} }
} }
elseif (isset($_GET["template"])){ elseif (isset($_GET['template'])){
$domain_template = mailbox('get', 'domain_templates', $_GET["template"]); $domain_template = mailbox('get', 'domain_templates', $_GET['template']);
if ($domain_template){ if ($domain_template){
$template_data = [ $template_data = [
'template' => $domain_template 'template' => $domain_template,
'rl' => ['frame' => $domain_template['attributes']['rl_frame']],
]; ];
$template = 'edit/domain-templates.twig'; $template = 'edit/domain-templates.twig';
$result = true; $result = true;
} }
else { else {
$mailbox_template = mailbox('get', 'mailbox_templates', $_GET["template"]); $mailbox_template = mailbox('get', 'mailbox_templates', $_GET['template']);
if ($mailbox_template){ if ($mailbox_template){
$template_data = [ $template_data = [
'template' => $mailbox_template 'template' => $mailbox_template,
'rl' => ['frame' => $mailbox_template['attributes']['rl_frame']],
]; ];
$template = 'edit/mailbox-templates.twig'; $template = 'edit/mailbox-templates.twig';
$result = true; $result = true;
@@ -214,6 +220,7 @@ $js_minifier->add('/web/js/site/pwgen.js');
$template_data['result'] = $result; $template_data['result'] = $result;
$template_data['return_to'] = $_SESSION['return_to']; $template_data['return_to'] = $_SESSION['return_to'];
$template_data['lang_user'] = json_encode($lang['user']); $template_data['lang_user'] = json_encode($lang['user']);
$template_data['lang_admin'] = json_encode($lang['admin']);
$template_data['lang_datatables'] = json_encode($lang['datatables']); $template_data['lang_datatables'] = json_encode($lang['datatables']);
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';

View File

@@ -24,9 +24,10 @@ function customize($_action, $_item, $_data = null) {
} }
switch ($_item) { switch ($_item) {
case 'main_logo': case 'main_logo':
if (in_array($_data['main_logo']['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) { case 'main_logo_dark':
if (in_array($_data[$_item]['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) {
try { try {
if (file_exists($_data['main_logo']['tmp_name']) !== true) { if (file_exists($_data[$_item]['tmp_name']) !== true) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data), 'log' => array(__FUNCTION__, $_action, $_item, $_data),
@@ -34,7 +35,7 @@ function customize($_action, $_item, $_data = null) {
); );
return false; return false;
} }
$image = new Imagick($_data['main_logo']['tmp_name']); $image = new Imagick($_data[$_item]['tmp_name']);
if ($image->valid() !== true) { if ($image->valid() !== true) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@@ -63,7 +64,7 @@ function customize($_action, $_item, $_data = null) {
return false; return false;
} }
try { try {
$redis->Set('MAIN_LOGO', 'data:' . $_data['main_logo']['type'] . ';base64,' . base64_encode(file_get_contents($_data['main_logo']['tmp_name']))); $redis->Set(strtoupper($_item), 'data:' . $_data[$_item]['type'] . ';base64,' . base64_encode(file_get_contents($_data[$_item]['tmp_name'])));
} }
catch (RedisException $e) { catch (RedisException $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@@ -201,8 +202,9 @@ function customize($_action, $_item, $_data = null) {
} }
switch ($_item) { switch ($_item) {
case 'main_logo': case 'main_logo':
case 'main_logo_dark':
try { try {
if ($redis->del('MAIN_LOGO')) { if ($redis->del(strtoupper($_item))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data), 'log' => array(__FUNCTION__, $_action, $_item, $_data),
@@ -239,8 +241,9 @@ function customize($_action, $_item, $_data = null) {
return ($app_links) ? $app_links : false; return ($app_links) ? $app_links : false;
break; break;
case 'main_logo': case 'main_logo':
case 'main_logo_dark':
try { try {
return $redis->get('MAIN_LOGO'); return $redis->get(strtoupper($_item));
} }
catch (RedisException $e) { catch (RedisException $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@@ -277,9 +280,14 @@ function customize($_action, $_item, $_data = null) {
} }
break; break;
case 'main_logo_specs': case 'main_logo_specs':
case 'main_logo_dark_specs':
try { try {
$image = new Imagick(); $image = new Imagick();
$img_data = explode('base64,', customize('get', 'main_logo')); if($_item == 'main_logo_specs') {
$img_data = explode('base64,', customize('get', 'main_logo'));
} else {
$img_data = explode('base64,', customize('get', 'main_logo_dark'));
}
if ($img_data[1]) { if ($img_data[1]) {
$image->readImageBlob(base64_decode($img_data[1])); $image->readImageBlob(base64_decode($img_data[1]));
return $image->identifyImage(); return $image->identifyImage();

View File

@@ -325,6 +325,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$timeout2 = intval($_data['timeout2']); $timeout2 = intval($_data['timeout2']);
$skipcrossduplicates = intval($_data['skipcrossduplicates']); $skipcrossduplicates = intval($_data['skipcrossduplicates']);
$automap = intval($_data['automap']); $automap = intval($_data['automap']);
$dry = intval($_data['dry']);
$port1 = $_data['port1']; $port1 = $_data['port1'];
$host1 = strtolower($_data['host1']); $host1 = strtolower($_data['host1']);
$password1 = $_data['password1']; $password1 = $_data['password1'];
@@ -435,8 +436,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; 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`) $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, :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( $stmt->execute(array(
':user2' => $username, ':user2' => $username,
':custom_params' => $custom_params, ':custom_params' => $custom_params,
@@ -450,6 +451,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':skipcrossduplicates' => $skipcrossduplicates, ':skipcrossduplicates' => $skipcrossduplicates,
':maxbytespersecond' => $maxbytespersecond, ':maxbytespersecond' => $maxbytespersecond,
':subscribeall' => $subscribeall, ':subscribeall' => $subscribeall,
':dry' => $dry,
':subfolder2' => $subfolder2, ':subfolder2' => $subfolder2,
':host1' => $host1, ':host1' => $host1,
':authmech1' => 'PLAIN', ':authmech1' => 'PLAIN',
@@ -1250,9 +1252,27 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
)); ));
} }
else { else {
$stmt = $pdo->prepare("INSERT INTO `user_acl` (`username`) VALUES (:username)"); $stmt = $pdo->prepare("INSERT INTO `user_acl`
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`)
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
$stmt->execute(array( $stmt->execute(array(
':username' => $username ':username' => $username,
':spam_alias' => 0,
':tls_policy' => 0,
':spam_score' => 0,
':spam_policy' => 0,
':delimiter_action' => 0,
':syncjobs' => 0,
':eas_reset' => 0,
':sogo_profile_reset' => 0,
':pushover' => 0,
':quarantine' => 0,
':quarantine_attachments' => 0,
':quarantine_notification' => 0,
':quarantine_category' => 0,
':app_passwds' => 0
)); ));
} }
@@ -1533,20 +1553,20 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0; $attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
} else { } else {
$_data['acl'] = (array)$_data['acl']; $_data['acl'] = (array)$_data['acl'];
$attr['acl_spam_alias'] = 1; $attr['acl_spam_alias'] = 0;
$attr['acl_tls_policy'] = 1; $attr['acl_tls_policy'] = 0;
$attr['acl_spam_score'] = 1; $attr['acl_spam_score'] = 0;
$attr['acl_spam_policy'] = 1; $attr['acl_spam_policy'] = 0;
$attr['acl_delimiter_action'] = 1; $attr['acl_delimiter_action'] = 0;
$attr['acl_syncjobs'] = 0; $attr['acl_syncjobs'] = 0;
$attr['acl_eas_reset'] = 1; $attr['acl_eas_reset'] = 0;
$attr['acl_sogo_profile_reset'] = 0; $attr['acl_sogo_profile_reset'] = 0;
$attr['acl_pushover'] = 1; $attr['acl_pushover'] = 0;
$attr['acl_quarantine'] = 1; $attr['acl_quarantine'] = 0;
$attr['acl_quarantine_attachments'] = 1; $attr['acl_quarantine_attachments'] = 0;
$attr['acl_quarantine_notification'] = 1; $attr['acl_quarantine_notification'] = 0;
$attr['acl_quarantine_category'] = 1; $attr['acl_quarantine_category'] = 0;
$attr['acl_app_passwds'] = 1; $attr['acl_app_passwds'] = 0;
} }
@@ -2013,6 +2033,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$success = (isset($_data['success'])) ? NULL : $is_now['success']; $success = (isset($_data['success'])) ? NULL : $is_now['success'];
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates']; $delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
$subscribeall = (isset($_data['subscribeall'])) ? intval($_data['subscribeall']) : $is_now['subscribeall']; $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']; $delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2']; $delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
$automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap']; $automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap'];
@@ -2146,6 +2167,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`timeout1` = :timeout1, `timeout1` = :timeout1,
`timeout2` = :timeout2, `timeout2` = :timeout2,
`subscribeall` = :subscribeall, `subscribeall` = :subscribeall,
`dry` = :dry,
`active` = :active `active` = :active
WHERE `id` = :id"); WHERE `id` = :id");
$stmt->execute(array( $stmt->execute(array(
@@ -2171,6 +2193,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':timeout1' => $timeout1, ':timeout1' => $timeout1,
':timeout2' => $timeout2, ':timeout2' => $timeout2,
':subscribeall' => $subscribeall, ':subscribeall' => $subscribeall,
':dry' => $dry,
':active' => $active, ':active' => $active,
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@@ -3241,6 +3264,62 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return true; return true;
break; 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': case 'resource':
if (!is_array($_data['name'])) { if (!is_array($_data['name'])) {
$names = array(); $names = array();
@@ -3320,6 +3399,90 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
} }
break; break;
case 'domain_wide_footer':
if (!is_array($_data['domains'])) {
$domains = array();
$domains[] = $_data['domains'];
}
else {
$domains = $_data['domains'];
}
$footers = array();
$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);
}
}
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' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('domain_footer_modified', htmlspecialchars($domain))
);
}
break;
} }
break; break;
case 'get': case 'get':
@@ -3872,13 +4035,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false; 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( $stmt->execute(array(
':domain' => $_data, ':domain' => $_data,
)); ));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) { while($row = array_shift($rows)) {
$aliases[] = $row['id']; if ($_extra == "address"){
$aliases[] = $row['address'];
} else {
$aliases[] = $row['id'];
}
} }
return $aliases; return $aliases;
break; break;
@@ -4230,6 +4397,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`modified`, `mailbox`.`modified`,
`quota2`.`bytes`, `quota2`.`bytes`,
`attributes`, `attributes`,
`custom_attributes`,
`quota2`.`messages` `quota2`.`messages`
FROM `mailbox`, `quota2`, `domain` FROM `mailbox`, `quota2`, `domain`
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL) WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
@@ -4250,6 +4418,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`modified`, `mailbox`.`modified`,
`quota2replica`.`bytes`, `quota2replica`.`bytes`,
`attributes`, `attributes`,
`custom_attributes`,
`quota2replica`.`messages` `quota2replica`.`messages`
FROM `mailbox`, `quota2replica`, `domain` FROM `mailbox`, `quota2replica`, `domain`
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL) WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
@@ -4272,6 +4441,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$mailboxdata['quota'] = $row['quota']; $mailboxdata['quota'] = $row['quota'];
$mailboxdata['messages'] = $row['messages']; $mailboxdata['messages'] = $row['messages'];
$mailboxdata['attributes'] = json_decode($row['attributes'], true); $mailboxdata['attributes'] = json_decode($row['attributes'], true);
$mailboxdata['custom_attributes'] = json_decode($row['custom_attributes'], true);
$mailboxdata['quota_used'] = intval($row['bytes']); $mailboxdata['quota_used'] = intval($row['bytes']);
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100); $mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
$mailboxdata['created'] = $row['created']; $mailboxdata['created'] = $row['created'];
@@ -4432,6 +4602,44 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
return $resourcedata; return $resourcedata;
break; break;
case 'domain_wide_footer':
$domain = idn_to_ascii(strtolower(trim($_data)), 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'], $_data)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
try {
$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 (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
return false;
}
return $footer;
break;
} }
break; break;
case 'delete': case 'delete':

View File

@@ -40,6 +40,7 @@ $globalVariables = [
'ui_texts' => $UI_TEXTS, 'ui_texts' => $UI_TEXTS,
'css_path' => '/cache/'.basename($CSSPath), 'css_path' => '/cache/'.basename($CSSPath),
'logo' => customize('get', 'main_logo'), 'logo' => customize('get', 'main_logo'),
'logo_dark' => customize('get', 'main_logo_dark'),
'available_languages' => $AVAILABLE_LANGUAGES, 'available_languages' => $AVAILABLE_LANGUAGES,
'lang' => $lang, 'lang' => $lang,
'skip_sogo' => (getenv('SKIP_SOGO') == 'y'), 'skip_sogo' => (getenv('SKIP_SOGO') == 'y'),

View File

@@ -3,7 +3,7 @@ function init_db_schema() {
try { try {
global $pdo; global $pdo;
$db_version = "14022023_1000"; $db_version = "21112023_1644";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $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" "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( "tags_domain" => array(
"cols" => array( "cols" => array(
"tag_name" => "VARCHAR(255) NOT NULL", "tag_name" => "VARCHAR(255) NOT NULL",
@@ -344,6 +358,7 @@ function init_db_schema() {
"local_part" => "VARCHAR(255) NOT NULL", "local_part" => "VARCHAR(255) NOT NULL",
"domain" => "VARCHAR(255) NOT NULL", "domain" => "VARCHAR(255) NOT NULL",
"attributes" => "JSON", "attributes" => "JSON",
"custom_attributes" => "JSON NOT NULL DEFAULT ('{}')",
"kind" => "VARCHAR(100) NOT NULL DEFAULT ''", "kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
"multiple_bookings" => "INT NOT NULL DEFAULT -1", "multiple_bookings" => "INT NOT NULL DEFAULT -1",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
@@ -704,6 +719,7 @@ function init_db_schema() {
"timeout1" => "SMALLINT NOT NULL DEFAULT '600'", "timeout1" => "SMALLINT NOT NULL DEFAULT '600'",
"timeout2" => "SMALLINT NOT NULL DEFAULT '600'", "timeout2" => "SMALLINT NOT NULL DEFAULT '600'",
"subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'", "subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'",
"dry" => "TINYINT(1) NOT NULL DEFAULT '0'",
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'", "is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
"returned_text" => "LONGTEXT", "returned_text" => "LONGTEXT",
"last_run" => "TIMESTAMP NULL DEFAULT NULL", "last_run" => "TIMESTAMP NULL DEFAULT NULL",

View File

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

View File

@@ -12,7 +12,7 @@ jobs:
dependency-version: [prefer-lowest, prefer-stable] dependency-version: [prefer-lowest, prefer-stable]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v1 uses: actions/checkout@v4
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v1 uses: actions/checkout@v4
- name: Install dependencies - name: Install dependencies
run: composer update --no-progress --ignore-platform-reqs run: composer update --no-progress --ignore-platform-reqs
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v1 uses: actions/checkout@v4
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 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'] php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2 - uses: shivammathur/setup-php@v2
with: with:

View File

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

View File

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

View File

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

@@ -120,10 +120,14 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
if (isset($_POST["submit_main_logo"])) { if (isset($_POST["submit_main_logo"])) {
if ($_FILES['main_logo']['error'] == 0) { if ($_FILES['main_logo']['error'] == 0) {
customize('add', 'main_logo', $_FILES); customize('add', 'main_logo', $_FILES);
}
if ($_FILES['main_logo_dark']['error'] == 0) {
customize('add', 'main_logo_dark', $_FILES);
} }
} }
if (isset($_POST["reset_main_logo"])) { if (isset($_POST["reset_main_logo"])) {
customize('delete', 'main_logo'); customize('delete', 'main_logo');
customize('delete', 'main_logo_dark');
} }
// Some actions will not be available via API // Some actions will not be available via API
if (isset($_POST["license_validate_now"])) { if (isset($_POST["license_validate_now"])) {

View File

@@ -90,15 +90,18 @@ $AVAILABLE_LANGUAGES = array(
'es-es' => 'Español (Spanish)', 'es-es' => 'Español (Spanish)',
'fi-fi' => 'Suomi (Finish)', 'fi-fi' => 'Suomi (Finish)',
'fr-fr' => 'Français (French)', 'fr-fr' => 'Français (French)',
'gr-gr' => 'Ελληνικά (Greek)',
'hu-hu' => 'Magyar (Hungarian)', 'hu-hu' => 'Magyar (Hungarian)',
'it-it' => 'Italiano (Italian)', 'it-it' => 'Italiano (Italian)',
'ko-kr' => '한국어 (Korean)', 'ko-kr' => '한국어 (Korean)',
'lv-lv' => 'latviešu (Latvian)', 'lv-lv' => 'latviešu (Latvian)',
'nl-nl' => 'Nederlands (Dutch)', 'nl-nl' => 'Nederlands (Dutch)',
'pl-pl' => 'Język Polski (Polish)', 'pl-pl' => 'Język Polski (Polish)',
'pt-br' => 'Português brasileiro (Brazilian Portuguese)',
'pt-pt' => 'Português (Portuguese)', 'pt-pt' => 'Português (Portuguese)',
'ro-ro' => 'Română (Romanian)', 'ro-ro' => 'Română (Romanian)',
'ru-ru' => 'Pусский (Russian)', 'ru-ru' => 'Pусский (Russian)',
'si-si' => 'Slovenščina (Slovenian)',
'sk-sk' => 'Slovenčina (Slovak)', 'sk-sk' => 'Slovenčina (Slovak)',
'sv-se' => 'Svenska (Swedish)', 'sv-se' => 'Svenska (Swedish)',
'tr-tr' => 'Türkçe (Turkish)', 'tr-tr' => 'Türkçe (Turkish)',
@@ -233,118 +236,120 @@ $RSPAMD_MAPS = array(
$IMAPSYNC_OPTIONS = array( $IMAPSYNC_OPTIONS = array(
'whitelist' => array( 'whitelist' => array(
'abort',
'authmd51',
'authmd52',
'authmech1', 'authmech1',
'authmech2', 'authmech2',
'authuser1', 'authuser1',
'authuser2', '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', 'debug',
'debugcontent',
'debugcrossduplicates',
'debugflags',
'debugfolders',
'debugimap',
'debugimap1',
'debugimap2', 'debugimap2',
'debugmemory',
'debugssl',
'delete1emptyfolders',
'delete2folders',
'disarmreadreceipts',
'domain1',
'domain2',
'domino1', 'domino1',
'domino2',
'dry',
'errorsmax',
'exchange1', 'exchange1',
'exchange2', 'exchange2',
'exitwhenover',
'expunge1',
'f1f2',
'filterbuggyflags',
'folder',
'folderfirst',
'folderlast',
'folderrec',
'gmail1',
'gmail2',
'idatefromheader',
'include',
'inet4',
'inet6',
'justconnect',
'justfolders',
'justfoldersizes',
'justlogin', 'justlogin',
'keepalive1', 'keepalive1',
'keepalive2', 'keepalive2',
'log',
'logdir',
'logfile',
'maxbytesafter',
'maxlinelength',
'maxmessagespersecond',
'maxsize',
'maxsleep',
'minage',
'minsize',
'noabletosearch',
'noabletosearch1',
'noabletosearch2', 'noabletosearch2',
'noexpunge1',
'noexpunge2', 'noexpunge2',
'nofoldersizesatend',
'noid',
'nolog',
'nomixfolders',
'noresyncflags', 'noresyncflags',
'nossl1', 'nossl1',
'nouidexpunge2', 'nossl2',
'syncinternaldates',
'idatefromheader',
'useuid',
'debugflags',
'debugimap',
'delete1emptyfolders',
'delete2folders',
'gmail2',
'office1',
'testslive6',
'debugimap1',
'errorsmax',
'tests',
'gmail1',
'maxmessagespersecond',
'maxbytesafter',
'maxsleep',
'abort',
'resyncflags',
'resynclabels',
'syncacls',
'nosyncacls', 'nosyncacls',
'notls1',
'notls2',
'nouidexpunge2',
'nousecache', 'nousecache',
'office2',
'testslive',
'debugmemory',
'exitwhenover',
'noid',
'noexpunge1',
'authmd51',
'logfile',
'proxyauth2',
'domain1',
'domain2',
'oauthaccesstoken1', 'oauthaccesstoken1',
'oauthaccesstoken2', 'oauthaccesstoken2',
'oauthdirect1', 'oauthdirect1',
'oauthdirect2', 'oauthdirect2',
'folder', 'office1',
'folderrec', 'office2',
'folderfirst', 'pidfile',
'folderlast', 'pidfilelocking',
'nomixfolders', 'prefix1',
'authmd52', 'prefix2',
'debugfolders', 'proxyauth1',
'nossl2', 'proxyauth2',
'resyncflags',
'resynclabels',
'search',
'search1',
'search2',
'sep1',
'sep2',
'showpasswords',
'skipemptyfolders',
'ssl2', 'ssl2',
'sslargs1',
'sslargs2',
'subfolder1',
'subscribe',
'subscribed',
'syncacls',
'syncduplicates',
'syncinternaldates',
'synclabels',
'tests',
'testslive',
'testslive6',
'tls2', 'tls2',
'notls2', 'truncmess',
'debugssl', 'usecache',
'notls1', 'useheader',
'inet4', 'useuid'
'inet6',
'log',
'showpasswords'
), ),
'blacklist' => array( 'blacklist' => array(
'skipmess', 'skipmess',

View File

@@ -15801,7 +15801,7 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
paginationEl.empty(); paginationEl.empty();
} }
else { else {
paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination'); paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination pagination-sm');
} }
attach( attach(

View File

@@ -121,10 +121,21 @@ $(document).ready(function() {
if (lastTab) { if (lastTab) {
$('[data-bs-target="#' + lastTab + '"]').click(); $('[data-bs-target="#' + lastTab + '"]').click();
var tab = $('[id^="' + lastTab + '"]'); var tab = $('[id^="' + lastTab + '"]');
$(tab).find('.card-body.collapse').collapse('show'); $(tab).find('.card-body.collapse:first').collapse('show');
} }
}); });
})(); })();
// responsive tabs, scroll to opened tab
$(document).on("shown.bs.collapse shown.bs.tab", function (e) {
var target = $(e.target);
if($(window).width() <= 767) {
var offset = target.offset().top - 60;
$("html, body").stop().animate({
scrollTop: offset
}, 100);
}
});
// IE fix to hide scrollbars when table body is empty // IE fix to hide scrollbars when table body is empty
$('tbody').filter(function (index) { $('tbody').filter(function (index) {
@@ -314,19 +325,28 @@ $(document).ready(function() {
$('#dark-mode-toggle').click(toggleDarkMode); $('#dark-mode-toggle').click(toggleDarkMode);
if ($('#dark-mode-theme').length) { if ($('#dark-mode-theme').length) {
$('#dark-mode-toggle').prop('checked', true); $('#dark-mode-toggle').prop('checked', true);
$('.main-logo').addClass('d-none');
$('.main-logo-dark').removeClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
} else {
$('.main-logo').removeClass('d-none');
$('.main-logo-dark').addClass('d-none');
} }
function toggleDarkMode(){ function toggleDarkMode(){
if($('#dark-mode-theme').length){ if($('#dark-mode-theme').length){
$('#dark-mode-theme').remove(); $('#dark-mode-theme').remove();
$('#dark-mode-toggle').prop('checked', false); $('#dark-mode-toggle').prop('checked', false);
$('.main-logo').removeClass('d-none');
$('.main-logo-dark').addClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png');
localStorage.setItem('theme', 'light'); localStorage.setItem('theme', 'light');
}else{ }else{
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">'); $('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">');
$('#dark-mode-toggle').prop('checked', true); $('#dark-mode-toggle').prop('checked', true);
$('.main-logo').addClass('d-none');
$('.main-logo-dark').removeClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
localStorage.setItem('theme', 'dark'); localStorage.setItem('theme', 'dark');

View File

@@ -510,14 +510,14 @@ jQuery(function($){
if (table == 'relayhoststable') { if (table == 'relayhoststable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="sender-dependent" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' + '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="sender-dependent" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
'<a href="/edit/relayhost/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/relayhost/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-rlyhost" data-api-url="delete/relayhost" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-rlyhost" data-api-url="delete/relayhost" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
if (item.used_by_mailboxes == '') { item.in_use_by = item.used_by_domains; } if (item.used_by_mailboxes == '') { item.in_use_by = item.used_by_domains; }
else if (item.used_by_domains == '') { item.in_use_by = item.used_by_mailboxes; } else if (item.used_by_domains == '') { item.in_use_by = item.used_by_mailboxes; }
else { item.in_use_by = item.used_by_mailboxes + '<hr style="margin:5px 0px 5px 0px;">' + item.used_by_domains; } else { item.in_use_by = item.used_by_mailboxes + '<hr style="margin:5px 0px 5px 0px;">' + item.used_by_domains; }
item.chkbox = '<input type="checkbox" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />';
}); });
} else if (table == 'transportstable') { } else if (table == 'transportstable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
@@ -528,49 +528,49 @@ jQuery(function($){
item.username = '<i style="color:#' + intToRGB(hashCode(item.nexthop)) + ';" class="bi bi-square-fill"></i> ' + item.username; item.username = '<i style="color:#' + intToRGB(hashCode(item.nexthop)) + ';" class="bi bi-square-fill"></i> ' + item.username;
} }
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' + '<a href="#" data-bs-toggle="modal" data-bs-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-caret-right-fill"></i> Test</a>' +
'<a href="/edit/transport/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/transport/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-transport" data-api-url="delete/transport" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-transport" data-api-url="delete/transport" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="transports" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="transports" name="multi_select" value="' + item.id + '" />';
}); });
} else if (table == 'queuetable') { } else if (table == 'queuetable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
rcpts = $.map(item.recipients, function(i) { rcpts = $.map(item.recipients, function(i) {
return escapeHtml(i); return escapeHtml(i);
}); });
item.recipients = rcpts.join('<hr style="margin:1px!important">'); item.recipients = rcpts.join('<hr style="margin:1px!important">');
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.queue_show_message + '</a>' + '<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-xs-lg btn-secondary">' + lang.queue_show_message + '</a>' +
'</div>'; '</div>';
}); });
} else if (table == 'forwardinghoststable') { } else if (table == 'forwardinghoststable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-action="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-xs-lg btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />';
}); });
} else if (table == 'oauth2clientstable') { } else if (table == 'oauth2clientstable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.scope = "profile"; item.scope = "profile";
item.grant_types = 'refresh_token password authorization_code'; item.grant_types = 'refresh_token password authorization_code';
item.chkbox = '<input type="checkbox" data-id="oauth2_clients" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="oauth2_clients" name="multi_select" value="' + item.id + '" />';
}); });
} else if (table == 'domainadminstable') { } else if (table == 'domainadminstable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.selected_domains = escapeHtml(item.selected_domains); item.selected_domains = escapeHtml(item.selected_domains);
item.selected_domains = item.selected_domains.toString().replace(/,/g, "<br>"); item.selected_domains = item.selected_domains.toString().replace(/,/g, "<br>");
item.chkbox = '<input type="checkbox" data-id="domain_admins" name="multi_select" value="' + item.username + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain_admins" name="multi_select" value="' + item.username + '" />';
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' + '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
'</div>'; '</div>';
}); });
} else if (table == 'adminstable') { } else if (table == 'adminstable') {
@@ -580,10 +580,10 @@ jQuery(function($){
} else { } else {
item.usr = item.username; item.usr = item.username;
} }
item.chkbox = '<input type="checkbox" data-id="admins" name="multi_select" value="' + item.username + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="admins" name="multi_select" value="' + item.username + '" />';
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/admin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/admin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-admin" data-api-url="delete/admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
}); });
} }

View File

@@ -1684,7 +1684,7 @@ function showVersionModal(title, version){
function parseGithubMarkdownLinks(inputText) { function parseGithubMarkdownLinks(inputText) {
var replacedText, replacePattern1; 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) => { replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
if (matched.includes('github.com')){ if (matched.includes('github.com')){
// return short link if it's github link // return short link if it's github link

View File

@@ -93,10 +93,10 @@ jQuery(function($){
dataSrc: function(data){ dataSrc: function(data){
$.each(data, function (i, item) { $.each(data, function (i, item) {
if (!validateEmail(item.object)) { if (!validateEmail(item.object)) {
item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
} }
else { else {
item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
} }
}); });
@@ -154,10 +154,10 @@ jQuery(function($){
dataSrc: function(data){ dataSrc: function(data){
$.each(data, function (i, item) { $.each(data, function (i, item) {
if (!validateEmail(item.object)) { if (!validateEmail(item.object)) {
item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
} }
else { else {
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
} }
}); });
@@ -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 // detect element visibility changes
function onVisible(element, callback) { function onVisible(element, callback) {

View File

@@ -466,16 +466,16 @@ jQuery(function($){
item.def_quota_for_mbox = humanFileSize(item.def_quota_for_mbox); item.def_quota_for_mbox = humanFileSize(item.def_quota_for_mbox);
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
item.chkbox = '<input type="checkbox" data-id="domain" name="multi_select" value="' + encodeURIComponent(item.domain_name) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain" name="multi_select" value="' + encodeURIComponent(item.domain_name) + '" />';
item.action = '<div class="btn-group">'; item.action = '<div class="btn-group">';
if (role == "admin") { if (role == "admin") {
item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'<a href="#dnsInfoModal" class="btn btn-sm btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>'; '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
} }
else { else {
item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#dnsInfoModal" class="btn btn-xs btn-xs-half btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>'; '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-xs-half btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
} }
if (Array.isArray(item.tags)){ if (Array.isArray(item.tags)){
@@ -650,7 +650,7 @@ jQuery(function($){
url: "/api/v1/get/domain/template/all", url: "/api/v1/get/domain/template/all",
dataSrc: function(json){ dataSrc: function(json){
$.each(json, function (i, item) { $.each(json, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="domain_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
item.attributes.def_quota_for_mbox = humanFileSize(item.attributes.def_quota_for_mbox); item.attributes.def_quota_for_mbox = humanFileSize(item.attributes.def_quota_for_mbox);
item.attributes.max_quota_for_mbox = humanFileSize(item.attributes.max_quota_for_mbox); item.attributes.max_quota_for_mbox = humanFileSize(item.attributes.max_quota_for_mbox);
@@ -671,13 +671,13 @@ jQuery(function($){
if (item.template.toLowerCase() == "default"){ if (item.template.toLowerCase() == "default"){
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'</div>'; '</div>';
} }
else { else {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/domain/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/domain/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
} }
@@ -851,8 +851,9 @@ jQuery(function($){
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: lang_datatables, language: lang_datatables,
initComplete: function(){ initComplete: function(settings, json){
hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table'); hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table');
filterByDomain(json, 8, table);
}, },
ajax: { ajax: {
type: "GET", type: "GET",
@@ -880,7 +881,7 @@ jQuery(function($){
} }
} }
*/ */
item.chkbox = '<input type="checkbox" data-id="mailbox" name="multi_select" value="' + encodeURIComponent(item.username) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailbox" name="multi_select" value="' + encodeURIComponent(item.username) + '" />';
if (item.attributes.passwd_update != '0') { if (item.attributes.passwd_update != '0') {
var last_pw_change = new Date(item.attributes.passwd_update.replace(/-/g, "/")); var last_pw_change = new Date(item.attributes.passwd_update.replace(/-/g, "/"));
item.last_pw_change = last_pw_change.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); item.last_pw_change = last_pw_change.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
@@ -912,18 +913,18 @@ jQuery(function($){
if (acl_data.login_as === 1) { if (acl_data.login_as === 1) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-half btn-success"><i class="bi bi-person-fill"></i> Login</a>'; '<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-lg btn-xs-half btn-success"><i class="bi bi-person-fill"></i> Login</a>';
if (ALLOW_ADMIN_EMAIL_LOGIN) { if (ALLOW_ADMIN_EMAIL_LOGIN) {
item.action += '<a href="/sogo-auth.php?login=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-half btn-primary" target="_blank"><i class="bi bi-envelope-fill"></i> SOGo</a>'; item.action += '<a href="/sogo-auth.php?login=' + encodeURIComponent(item.username) + '" class="login_as btn btn-sm btn-xs-lg btn-xs-half btn-primary" target="_blank"><i class="bi bi-envelope-fill"></i> SOGo</a>';
} }
item.action += '</div>'; item.action += '</div>';
} }
else { else {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/mailbox/' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-mailbox" data-api-url="delete/mailbox" data-item="' + encodeURIComponent(item.username) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
} }
item.in_use = { item.in_use = {
@@ -1148,7 +1149,7 @@ jQuery(function($){
url: "/api/v1/get/mailbox/template/all", url: "/api/v1/get/mailbox/template/all",
dataSrc: function(json){ dataSrc: function(json){
$.each(json, function (i, item) { $.each(json, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="mailbox_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailbox_template" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
item.template = escapeHtml(item.template); item.template = escapeHtml(item.template);
if (item.attributes.rl_frame === "s"){ if (item.attributes.rl_frame === "s"){
@@ -1190,13 +1191,13 @@ jQuery(function($){
if (item.template.toLowerCase() == "default"){ if (item.template.toLowerCase() == "default"){
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'</div>'; '</div>';
} }
else { else {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/mailbox/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-template" data-api-url="delete/mailbox/template" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
} }
@@ -1362,8 +1363,9 @@ jQuery(function($){
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: lang_datatables, language: lang_datatables,
initComplete: function(){ initComplete: function(settings, json){
hideTableExpandCollapseBtn('#tab-resources', '#resource_table'); hideTableExpandCollapseBtn('#tab-resources', '#resource_table');
filterByDomain(json, 5, table);
}, },
ajax: { ajax: {
type: "GET", type: "GET",
@@ -1378,10 +1380,10 @@ jQuery(function($){
item.multiple_bookings = '<span id="active-script" class="badge fs-6 bg-danger">' + lang.booking_custom_short + ' (' + item.multiple_bookings + ')</span>'; item.multiple_bookings = '<span id="active-script" class="badge fs-6 bg-danger">' + lang.booking_custom_short + ' (' + item.multiple_bookings + ')</span>';
} }
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/resource/' + encodeURIComponent(item.name) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/resource/' + encodeURIComponent(item.name) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-resource" data-api-url="delete/resource" data-item="' + item.name + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-resource" data-api-url="delete/resource" data-item="' + item.name + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="resource" name="multi_select" value="' + encodeURIComponent(item.name) + '" />';
item.name = escapeHtml(item.name); item.name = escapeHtml(item.name);
item.description = escapeHtml(item.description); item.description = escapeHtml(item.description);
}); });
@@ -1509,8 +1511,9 @@ jQuery(function($){
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: lang_datatables, language: lang_datatables,
order: [[2, 'desc']], order: [[2, 'desc']],
initComplete: function(){ initComplete: function(settings, json){
hideTableExpandCollapseBtn('#collapse-tab-bcc', '#bcc_table'); hideTableExpandCollapseBtn('#collapse-tab-bcc', '#bcc_table');
filterByDomain(json, 6, table);
}, },
ajax: { ajax: {
type: "GET", type: "GET",
@@ -1518,10 +1521,10 @@ jQuery(function($){
dataSrc: function(json){ dataSrc: function(json){
$.each(json, function (i, item) { $.each(json, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/bcc/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/bcc/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-bcc" data-api-url="delete/bcc" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-bcc" data-api-url="delete/bcc" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="bcc" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="bcc" name="multi_select" value="' + item.id + '" />';
item.local_dest = escapeHtml(item.local_dest); item.local_dest = escapeHtml(item.local_dest);
item.bcc_dest = escapeHtml(item.bcc_dest); item.bcc_dest = escapeHtml(item.bcc_dest);
if (item.type == 'sender') { if (item.type == 'sender') {
@@ -1632,10 +1635,10 @@ jQuery(function($){
item.recipient_map_old = escapeHtml(item.recipient_map_old); item.recipient_map_old = escapeHtml(item.recipient_map_old);
item.recipient_map_new = escapeHtml(item.recipient_map_new); item.recipient_map_new = escapeHtml(item.recipient_map_new);
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/recipient_map/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/recipient_map/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-recipient_map" data-api-url="delete/recipient_map" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-recipient_map" data-api-url="delete/recipient_map" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="recipient_map" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="recipient_map" name="multi_select" value="' + item.id + '" />';
}); });
return json; return json;
@@ -1734,10 +1737,10 @@ jQuery(function($){
item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>'; item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>';
} }
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/tls_policy_map/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/tls_policy_map/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-tls-policy-map" data-api-url="delete/tls-policy-map" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-tls-policy-map" data-api-url="delete/tls-policy-map" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="tls-policy-map" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tls-policy-map" name="multi_select" value="' + item.id + '" />';
}); });
return json; return json;
@@ -1823,8 +1826,9 @@ jQuery(function($){
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: lang_datatables, language: lang_datatables,
order: [[2, 'desc']], order: [[2, 'desc']],
initComplete: function(){ initComplete: function(settings, json){
hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table'); hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
filterByDomain(json, 5, table);
}, },
ajax: { ajax: {
type: "GET", type: "GET",
@@ -1832,10 +1836,10 @@ jQuery(function($){
dataSrc: function(json){ dataSrc: function(json){
$.each(json, function (i, item) { $.each(json, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/alias/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/alias/' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-alias" data-api-url="delete/alias" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-alias" data-api-url="delete/alias" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="alias" name="multi_select" value="' + encodeURIComponent(item.id) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="alias" name="multi_select" value="' + encodeURIComponent(item.id) + '" />';
item.goto = escapeHtml(item.goto.replace(/,/g, " ")); item.goto = escapeHtml(item.goto.replace(/,/g, " "));
if (item.public_comment !== null) { if (item.public_comment !== null) {
item.public_comment = escapeHtml(item.public_comment); item.public_comment = escapeHtml(item.public_comment);
@@ -1958,7 +1962,7 @@ jQuery(function($){
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table'); hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
}); });
table.on( 'draw', function (){ table.on( 'draw', function (){
$('#alias_table [data-bs-toggle="tooltip"]').tooltip(); $('#alias_table [data-bs-toggle="tooltip"]').tooltip();
}); });
@@ -1991,11 +1995,11 @@ jQuery(function($){
item.alias_domain = escapeHtml(item.alias_domain); item.alias_domain = escapeHtml(item.alias_domain);
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/aliasdomain/' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/aliasdomain/' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURIComponent(item.alias_domain) + '" class="btn btn-sm btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'<a href="#dnsInfoModal" class="btn btn-sm btn-xs-third btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.alias_domain) + '"><i class="bi bi-globe2"></i> DNS</a></div>' + '<a href="#dnsInfoModal" class="btn btn-sm btn-xs-lg btn-xs-third btn-info" data-bs-toggle="modal" data-domain="' + encodeURIComponent(item.alias_domain) + '"><i class="bi bi-globe2"></i> DNS</a></div>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="alias-domain" name="multi_select" value="' + encodeURIComponent(item.alias_domain) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="alias-domain" name="multi_select" value="' + encodeURIComponent(item.alias_domain) + '" />';
if(item.parent_is_backupmx == '1') { if(item.parent_is_backupmx == '1') {
item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a> <div class="badge fs-6 bg-warning">' + lang.alias_domain_backupmx + '</div></span>'; item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a> <div class="badge fs-6 bg-warning">' + lang.alias_domain_backupmx + '</div></span>';
} else { } else {
@@ -2093,10 +2097,10 @@ jQuery(function($){
} }
item.server_w_port = escapeHtml(item.user1) + '@' + escapeHtml(item.host1) + ':' + escapeHtml(item.port1); item.server_w_port = escapeHtml(item.user1) + '@' + escapeHtml(item.host1) + ':' + escapeHtml(item.port1);
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/syncjob/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/syncjob/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="syncjob" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
if (item.is_running == 1) { if (item.is_running == 1) {
item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>'; item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>';
} else { } else {
@@ -2247,10 +2251,10 @@ jQuery(function($){
item.script_data = '<pre class="text-break" style="margin:0px">' + escapeHtml(item.script_data) + '</pre>' item.script_data = '<pre class="text-break" style="margin:0px">' + escapeHtml(item.script_data) + '</pre>'
item.filter_type = '<div class="badge fs-6 bg-secondary">' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '</div>' item.filter_type = '<div class="badge fs-6 bg-secondary">' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '</div>'
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="/edit/filter/' + item.id + '" class="btn btn-sm btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/filter/' + item.id + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-filter" data-api-url="delete/filter" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-filter" data-api-url="delete/filter" data-item="' + encodeURIComponent(item.id) + '" class="btn btn-sm btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="filter_item" name="multi_select" value="' + item.id + '" />' item.chkbox = '<input type="checkbox" class="form-check-input" data-id="filter_item" name="multi_select" value="' + item.id + '" />'
}); });
return json; return json;
@@ -2329,6 +2333,40 @@ jQuery(function($){
else else
$(tab).find(".table_collapse_option").hide(); $(tab).find(".table_collapse_option").hide();
} }
function filterByDomain(json, column, table){
var tableId = $(table.table().container()).attr('id');
// Create the `select` element
var select = $('<select class="btn btn-sm btn-xs-lg btn-light text-start mx-2"><option value="">'+lang.all_domains+'</option></select>')
.insertBefore(
$('#'+tableId+' .dataTables_filter > label > input')
)
.on( 'change', function(){
table.column(column)
.search($(this).val())
.draw();
});
// get all domains
var domains = [];
json.forEach(obj => {
Object.entries(obj).forEach(([key, value]) => {
if(key === 'domain') {
domains.push(value)
}
});
});
// get unique domain list
domains = domains.filter(function(value, index, array) {
return array.indexOf(value) === index;
});
// add domains to select
domains.forEach(function(domain) {
select.append($('<option>' + domain + '</option>'));
});
}
// detect element visibility changes // detect element visibility changes
function onVisible(element, callback) { function onVisible(element, callback) {
@@ -2344,7 +2382,7 @@ jQuery(function($){
} }
}); });
}) })
observer.observe(element_object); observer.observe(element_object);
}); });
} }

View File

@@ -77,7 +77,7 @@ jQuery(function($){
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' + '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
'</div>'; '</div>';
} }
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="qitems" name="multi_select" value="' + item.id + '" />';
}); });
return data; return data;
@@ -220,7 +220,7 @@ jQuery(function($){
if (value.score > 0) highlightClass = 'negative'; if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive'; else if (value.score < 0) highlightClass = 'positive';
else highlightClass = 'neutral'; 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(); $('[data-bs-toggle="tooltip"]').tooltip();
} }
@@ -295,3 +295,7 @@ jQuery(function($){
$(".table_collapse_option").hide(); $(".table_collapse_option").hide();
} }
}); });

View File

@@ -48,7 +48,7 @@ jQuery(function($){
url: "/api/v1/get/mailq/all", url: "/api/v1/get/mailq/all",
dataSrc: function(data){ dataSrc: function(data){
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
rcpts = $.map(item.recipients, function(i) { rcpts = $.map(item.recipients, function(i) {
return escapeHtml(i); return escapeHtml(i);
}); });

View File

@@ -127,7 +127,7 @@ jQuery(function($){
} }
} }
function createSortableDate(td, cellData, date_string = false) { function createSortableDate(td, cellData, date_string = false) {
if (date_string) if (date_string)
var date = new Date(cellData); var date = new Date(cellData);
@@ -169,11 +169,11 @@ jQuery(function($){
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-action="delete_selected" data-id="single-tla" data-api-url="delete/time_limited_alias" data-item="' + encodeURIComponent(item.address) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-tla" data-api-url="delete/time_limited_alias" data-item="' + encodeURIComponent(item.address) + '" class="btn btn-xs btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
item.address = escapeHtml(item.address); item.address = escapeHtml(item.address);
} }
else { else {
item.chkbox = '<input type="checkbox" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
item.action = '<span>-</span>'; item.action = '<span>-</span>';
} }
}); });
@@ -263,11 +263,11 @@ jQuery(function($){
'<a href="/edit/syncjob/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/syncjob/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="syncjob" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="syncjob" name="multi_select" value="' + item.id + '" />';
} }
else { else {
item.action = '<span>-</span>'; item.action = '<span>-</span>';
item.chkbox = '<input type="checkbox" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
} }
if (item.is_running == 1) { if (item.is_running == 1) {
item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>'; item.is_running = '<span id="active-script" class="badge fs-6 bg-success">' + lang.running + '</span>';
@@ -420,11 +420,11 @@ jQuery(function($){
'<a href="/edit/app-passwd/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' + '<a href="/edit/app-passwd/' + item.id + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
'<a href="#" data-action="delete_selected" data-id="single-apppasswd" data-api-url="delete/app-passwd" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + '<a href="#" data-action="delete_selected" data-id="single-apppasswd" data-api-url="delete/app-passwd" data-item="' + item.id + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" data-id="apppasswd" name="multi_select" value="' + item.id + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="apppasswd" name="multi_select" value="' + item.id + '" />';
} }
else { else {
item.action = '<span>-</span>'; item.action = '<span>-</span>';
item.chkbox = '<input type="checkbox" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
} }
}); });
@@ -503,13 +503,13 @@ jQuery(function($){
console.log(data); console.log(data);
$.each(data, function (i, item) { $.each(data, function (i, item) {
if (validateEmail(item.object)) { if (validateEmail(item.object)) {
item.chkbox = '<input type="checkbox" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
} }
else { else {
item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
} }
if (acl_data.spam_policy === 0) { if (acl_data.spam_policy === 0) {
item.chkbox = '<input type="checkbox" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
} }
}); });
@@ -574,13 +574,13 @@ jQuery(function($){
console.log(data); console.log(data);
$.each(data, function (i, item) { $.each(data, function (i, item) {
if (validateEmail(item.object)) { if (validateEmail(item.object)) {
item.chkbox = '<input type="checkbox" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
} }
else { else {
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
} }
if (acl_data.spam_policy === 0) { if (acl_data.spam_policy === 0) {
item.chkbox = '<input type="checkbox" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
} }
}); });

View File

@@ -1605,6 +1605,12 @@ if (isset($_GET['query'])) {
} }
} }
break; 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; break;
// return no route found if no case is matched // return no route found if no case is matched
default: default:
@@ -1881,6 +1887,7 @@ if (isset($_GET['query'])) {
case "quota_notification_bcc": case "quota_notification_bcc":
process_edit_return(quota_notification_bcc('edit', $attr)); process_edit_return(quota_notification_bcc('edit', $attr));
break; break;
break;
case "mailq": case "mailq":
process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr))); process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
break; break;
@@ -1892,6 +1899,9 @@ if (isset($_GET['query'])) {
case "template": case "template":
process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr))); process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr)));
break; break;
case "custom-attribute":
process_edit_return(mailbox('edit', 'mailbox_custom_attribute', array_merge(array('mailboxes' => $items), $attr)));
break;
default: default:
process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr))); process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
break; break;
@@ -1911,6 +1921,9 @@ if (isset($_GET['query'])) {
case "template": case "template":
process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr))); process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr)));
break; break;
case "footer":
process_edit_return(mailbox('edit', 'domain_wide_footer', array_merge(array('domains' => $items), $attr)));
break;
default: default:
process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr))); process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
break; break;

View File

@@ -3,7 +3,23 @@
"bcc_maps": "BCC maps", "bcc_maps": "BCC maps",
"filters": "Filtres", "filters": "Filtres",
"recipient_maps": "Recipient maps", "recipient_maps": "Recipient maps",
"syncjobs": "Feines de sincronització" "syncjobs": "Feines de sincronització",
"quarantine_category": "Canvia la categoria de les notificacions de quarantena",
"quarantine_notification": "Canvia les notificacions de quarantena",
"sogo_profile_reset": "Restableix el prefil SOGo",
"alias_domains": "Afegir àlies de domini",
"app_passwds": "Gestiona les contrasenyes de les aplicacions",
"domain_desc": "Canvia la descripció del domini",
"eas_reset": "Restableix els dispositius EAS",
"login_as": "Inicia sessió com a usuari de la bústia de correu",
"prohibited": "Prohibit per ACL",
"protocol_access": "Canvia el protocol d'accés",
"quarantine": "Accions de quarantena",
"quarantine_attachments": "Fitxers adjunts en quarantena",
"spam_alias": "Àlies temporals",
"spam_score": "Puntuació de correu brossa",
"tls_policy": "Política TLS",
"unlimited_quota": "Quota ilimitada per bústies de correo"
}, },
"add": { "add": {
"activate_filter_warn": "All other filters will be deactivated, when active is checked.", "activate_filter_warn": "All other filters will be deactivated, when active is checked.",
@@ -55,7 +71,9 @@
"target_domain": "Domini destí:", "target_domain": "Domini destí:",
"username": "Username", "username": "Username",
"validate": "Validar", "validate": "Validar",
"validation_success": "Validated successfully" "validation_success": "Validated successfully",
"app_name": "Nom de l'aplicació",
"app_password": "Afegir contrasenya a l'aplicació"
}, },
"admin": { "admin": {
"access": "Accés", "access": "Accés",
@@ -259,7 +277,7 @@
}, },
"footer": { "footer": {
"cancel": "Cancel·lar", "cancel": "Cancel·lar",
"confirm_delete": "Confirma l'esborrat ", "confirm_delete": "Confirma l'esborrat",
"delete_now": "Esborrar ara", "delete_now": "Esborrar ara",
"delete_these_items": "Si et plau confirma els canvis al objecte amb id:", "delete_these_items": "Si et plau confirma els canvis al objecte amb id:",
"loading": "Si et plau espera ...", "loading": "Si et plau espera ...",

View File

@@ -41,6 +41,7 @@
"alias_domain": "Doménový alias", "alias_domain": "Doménový alias",
"alias_domain_info": "<small>Platné názvy domén (oddělené čárkami).</small>", "alias_domain_info": "<small>Platné názvy domén (oddělené čárkami).</small>",
"app_name": "Název aplikace", "app_name": "Název aplikace",
"app_passwd_protocols": "Povolené protokoly pro hesla aplikací",
"app_password": "Přidat heslo aplikace", "app_password": "Přidat heslo aplikace",
"automap": "Pokusit se automaticky mapovat složky (\"Sent items\", \"Sent\" => \"Sent\" atd.)", "automap": "Pokusit se automaticky mapovat složky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti záložního MX", "backup_mx_options": "Možnosti záložního MX",
@@ -106,7 +107,8 @@
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
"validate": "Ověřit", "validate": "Ověřit",
"validation_success": "Úspěšně ověřeno", "validation_success": "Úspěšně ověřeno",
"tags": "Štítky" "tags": "Štítky",
"dry": "Simulovat synchronizaci"
}, },
"admin": { "admin": {
"access": "Přístupy", "access": "Přístupy",
@@ -146,6 +148,8 @@
"ays": "Opravdu chcete pokračovat?", "ays": "Opravdu chcete pokračovat?",
"ban_list_info": "Seznam blokovaných IP adres je zobrazen níže: <b>síť (zbývající čas blokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během několika sekund.<br />Červeně označené položky jsou pernamentní bloky z blacklistu.", "ban_list_info": "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", "change_logo": "Změnit logo",
"logo_normal_label": "Normální",
"logo_dark_label": "Inverzní pro tmavý režim",
"configuration": "Nastavení", "configuration": "Nastavení",
"convert_html_to_text": "Převést HTML do prostého textu", "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.", "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.",
@@ -205,6 +209,9 @@
"include_exclude": "Zahrnout/Vyloučit", "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>", "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", "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", "is_mx_based": "Na základě MX",
"last_applied": "Naposledy použité", "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>", "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>",
@@ -212,7 +219,7 @@
"loading": "Prosím čekejte...", "loading": "Prosím čekejte...",
"login_time": "Čas přihlášení", "login_time": "Čas přihlášení",
"logo_info": "Obrázek bude zmenšen na výšku 40 pixelů pro horní navigační lištu a na max. šířku 250 pixelů pro úvodní stránku.", "logo_info": "Obrázek bude zmenšen na výšku 40 pixelů pro horní navigační lištu a na max. šířku 250 pixelů pro úvodní stránku.",
"lookup_mx": "Ověřit cíl proti MX záznamu (.outlook.com bude směrovat všechnu poštu pro MX *.outlook.com přes tento uzel)", "lookup_mx": "Cíl je regulární výraz, který se porovná s názvem MX (<code>.*\\.google\\.com</code> pro směrování veškeré pošty cílené na MX, který končí na google.com přes tento skok)",
"main_name": "Název webu (\"mailcow UI\")", "main_name": "Název webu (\"mailcow UI\")",
"merged_vars_hint": "Šedé řádky byly přidány z <code>vars.(local.)inc.php</code> a zde je nelze upravit.", "merged_vars_hint": "Šedé řádky byly přidány z <code>vars.(local.)inc.php</code> a zde je nelze upravit.",
"message": "Zpráva", "message": "Zpráva",
@@ -231,6 +238,7 @@
"oauth2_renew_secret": "Vytvořit nový tajný klíč", "oauth2_renew_secret": "Vytvořit nový tajný klíč",
"oauth2_revoke_tokens": "Odvolat všechny klientské tokeny", "oauth2_revoke_tokens": "Odvolat všechny klientské tokeny",
"optional": "volitelné", "optional": "volitelné",
"options": "Možnosti",
"password": "Heslo", "password": "Heslo",
"password_length": "Délka hesla", "password_length": "Délka hesla",
"password_policy": "Politika hesel", "password_policy": "Politika hesel",
@@ -337,8 +345,8 @@
"yes": "&#10003;", "yes": "&#10003;",
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem", "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)", "f2b_max_ban_time": "Maximální délka banu (s)",
"ip_check": "Kontrola IP", "cors_settings": "Nastavení CORS",
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>" "queue_unban": "zrušit ban"
}, },
"danger": { "danger": {
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři", "access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
@@ -442,6 +450,9 @@
"target_domain_invalid": "Cílová doména %s je neplatná", "target_domain_invalid": "Cílová doména %s je neplatná",
"targetd_not_found": "Cílová doména %s nenalezena", "targetd_not_found": "Cílová doména %s nenalezena",
"targetd_relay_domain": "Cílová doména %s je předávaná", "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", "temp_error": "Dočasná chyba",
"text_empty": "Text nesmí být prázdný", "text_empty": "Text nesmí být prázdný",
"tfa_token_invalid": "Neplatný TFA token", "tfa_token_invalid": "Neplatný TFA token",
@@ -457,7 +468,39 @@
"username_invalid": "Uživatelské jméno %s nelze použít", "username_invalid": "Uživatelské jméno %s nelze použít",
"validity_missing": "Zdejte dobu platnosti", "validity_missing": "Zdejte dobu platnosti",
"value_missing": "Prosím, uveďte všechny hodnoty", "value_missing": "Prosím, uveďte všechny hodnoty",
"yotp_verification_failed": "Yubico OTP ověření selhalo: %s" "yotp_verification_failed": "Yubico OTP ověření selhalo: %s",
"webauthn_authenticator_failed": "Zvolený ověřovací prostředek nebyl nalezen",
"cors_invalid_method": "Zadaná neplatná metoda Allow-Method",
"cors_invalid_origin": "Zadán neplatný Allow-Origin",
"webauthn_publickey_failed": "Pro vybraný ověřovací prostředek nebyl uložen žádný veřejný klíč",
"webauthn_username_failed": "Zvolený ověřovací prostředek patří k jinému účtu",
"extended_sender_acl_denied": "chybějící ACL pro nastavení externích adres odesílatele",
"demo_mode_enabled": "Demo režim je zapnutý"
},
"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": " ",
"collapse_all": "Sbalit vše",
"expand_all": "Rozbalit vše"
}, },
"debug": { "debug": {
"chart_this_server": "Graf (tento server)", "chart_this_server": "Graf (tento server)",
@@ -484,20 +527,34 @@
"success": "Úspěch", "success": "Úspěch",
"system_containers": "Systém a kontejnery", "system_containers": "Systém a kontejnery",
"uptime": "Doba běhu", "uptime": "Doba běhu",
"username": "Uživatelské meno" "username": "Uživatelské meno",
"architecture": "Architektura",
"error_show_ip": "Nepodařilo se přeložit veřejné IP adresy",
"show_ip": "Zobrazit veřejné IP adresy",
"container_running": "Běží",
"container_stopped": "Zastaven",
"current_time": "Systémový čas",
"timezone": "Časové pásmo",
"update_available": "K dispozici je aktualizace",
"no_update_available": "Systém je na nejnovější verzi",
"update_failed": "Nepodařilo se zkontrolovat aktualizace",
"wip": "Nedokončená vývojová verze",
"memory": "Paměť",
"container_disabled": "Kontejner je zastaven nebo zakázán"
}, },
"diagnostics": { "diagnostics": {
"cname_from_a": "Hodnota odvozena z A/AAAA záznamu. Lze použít, pokud záznam ukazuje na správný zdroj.", "cname_from_a": "Hodnota odvozena z A/AAAA záznamu. Lze použít, pokud záznam ukazuje na správný zdroj.",
"dns_records": "DNS záznamy", "dns_records": "DNS záznamy",
"dns_records_24hours": "Upozornění: Změnám v systému DNS může trvat až 24 hodin, než se zde správně zobrazí jejich aktuální stav. Můžete zde snadno zjistit, jak nastavit DNS záznamy a zda jsou všechny záznamy správně uloženy.", "dns_records_24hours": "Upozornění: Změnám v systému DNS může trvat až 24 hodin, než se zde správně zobrazí jejich aktuální stav. Můžete zde snadno zjistit, jak nastavit DNS záznamy a zda jsou všechny záznamy správně uloženy.",
"dns_records_data": "Správný záznam", "dns_records_data": "Správný záznam",
"dns_records_docs": "Přečtěte si prosím <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">dokumentaci</a>.", "dns_records_docs": "Přečtěte si prosím <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentaci</a>.",
"dns_records_name": "Název", "dns_records_name": "Název",
"dns_records_status": "Současný stav", "dns_records_status": "Současný stav",
"dns_records_type": "Typ", "dns_records_type": "Typ",
"optional": "Tento záznam je volitelný." "optional": "Tento záznam je volitelný."
}, },
"edit": { "edit": {
"acl": "ACL (Oprávnění)",
"active": "Aktivní", "active": "Aktivní",
"admin": "Upravit administrátora", "admin": "Upravit administrátora",
"advanced_settings": "Pokročilá nastavení", "advanced_settings": "Pokročilá nastavení",
@@ -507,6 +564,7 @@
"allowed_protocols": "Povolené protokoly", "allowed_protocols": "Povolené protokoly",
"app_name": "Název aplikace", "app_name": "Název aplikace",
"app_passwd": "Heslo aplikace", "app_passwd": "Heslo aplikace",
"app_passwd_protocols": "Povolené protokoly pro hesla aplikací",
"automap": "Pokusit se automaticky mapovat složky (\"Sent items\", \"Sent\" => \"Sent\" atd.)", "automap": "Pokusit se automaticky mapovat složky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti záložního MX", "backup_mx_options": "Možnosti záložního MX",
"bcc_dest_format": "Cíl kopie musí být jedna platná email adresa. Pokud potřebujete posílat kopie na více adres, vytvořte Alias a použijte jej zde.", "bcc_dest_format": "Cíl kopie musí být jedna platná email adresa. Pokud potřebujete posílat kopie na více adres, vytvořte Alias a použijte jej zde.",
@@ -590,6 +648,8 @@
"sieve_desc": "Krátký popis", "sieve_desc": "Krátký popis",
"sieve_type": "Typ filtru", "sieve_type": "Typ filtru",
"skipcrossduplicates": "Přeskočit duplicitní zprávy (\"první přijde, první mele\")", "skipcrossduplicates": "Přeskočit duplicitní zprávy (\"první přijde, první mele\")",
"sogo_access": "Udělit přímý přihlašovací přístup do služby SOGo",
"sogo_access_info": "Jednotné přihlášení (SSO) z mail UI zůstává funkční. Toto nastavení neovlivňuje přístup ke všem ostatním službám ani neodstraňuje či nemění stávající profil uživatele SOGo.",
"sogo_visible": "Alias dostupný v SOGo", "sogo_visible": "Alias dostupný v SOGo",
"sogo_visible_info": "Tato volba určuje objekty, jež lze zobrazit v SOGo (sdílené nebo nesdílené aliasy, jež ukazuje alespoň na jednu schránku).", "sogo_visible_info": "Tato volba určuje objekty, jež lze zobrazit v SOGo (sdílené nebo nesdílené aliasy, jež ukazuje alespoň na jednu schránku).",
"spam_alias": "Vytvořit nebo změnit dočasné aliasy", "spam_alias": "Vytvořit nebo změnit dočasné aliasy",
@@ -605,7 +665,19 @@
"title": "Úprava objektu", "title": "Úprava objektu",
"unchanged_if_empty": "Pokud se nemění, ponechte prázdné", "unchanged_if_empty": "Pokud se nemění, ponechte prázdné",
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
"validate_save": "Ověřit a uložit" "validate_save": "Ověřit a uložit",
"domain_footer_info": "Patičky pro celou doménu se přidávají ke všem odchozím e-mailům spojeným s adresou v rámci této domény. <br> Pro patičku lze použít následující proměnné:",
"domain_footer_info_vars": {
"from_name": "{= from_name =} - Jméno odesílatele, např. pro \"Mailcow &lt;moo@mailcow.tld&gt;\" vrátí \"Mailcow\"",
"auth_user": "{= auth_user =} - Ověřené uživatelské jméno zadané MTA",
"from_user": "{= from_user =} - uživatelská část odesílatele, např. pro \"moo@mailcow.tld\" vrátí \"moo\"",
"from_domain": "{= from_domain =} - Doména odesílatele",
"from_addr": "{= from_addr =} - E-mailová adresa odesílatele"
},
"domain_footer": "Patička pro celou doménu",
"domain_footer_html": "HTML text",
"domain_footer_plain": "Prostý text",
"pushover_sound": "Zvukové upozornění"
}, },
"fido2": { "fido2": {
"confirm": "Potvrdit", "confirm": "Potvrdit",
@@ -642,6 +714,7 @@
"apps": "Aplikace", "apps": "Aplikace",
"debug": "Systémové informace", "debug": "Systémové informace",
"email": "E-Mail", "email": "E-Mail",
"mailcow_system": "Systém",
"mailcow_config": "Nastavení", "mailcow_config": "Nastavení",
"quarantine": "Karanténa", "quarantine": "Karanténa",
"restart_netfilter": "Restartovat netfilter", "restart_netfilter": "Restartovat netfilter",
@@ -677,6 +750,7 @@
"add_mailbox": "Přidat mailovou schránku", "add_mailbox": "Přidat mailovou schránku",
"add_recipient_map_entry": "Přidat mapu příjemce", "add_recipient_map_entry": "Přidat mapu příjemce",
"add_resource": "Přidat zdroj", "add_resource": "Přidat zdroj",
"add_template": "Přidat šablonu",
"add_tls_policy_map": "Přidat mapu TLS pravidel", "add_tls_policy_map": "Přidat mapu TLS pravidel",
"address_rewriting": "Přepisování adres", "address_rewriting": "Přepisování adres",
"alias": "Alias", "alias": "Alias",
@@ -719,6 +793,7 @@
"domain": "Doména", "domain": "Doména",
"domain_admins": "Správci domén", "domain_admins": "Správci domén",
"domain_aliases": "Doménové aliasy", "domain_aliases": "Doménové aliasy",
"domain_templates": "Šablony domén",
"domain_quota": "Kvóta", "domain_quota": "Kvóta",
"domain_quota_total": "Celková kvóta domény", "domain_quota_total": "Celková kvóta domény",
"domains": "Domény", "domains": "Domény",
@@ -747,6 +822,7 @@
"mailbox_defaults": "Výchozí nastavení", "mailbox_defaults": "Výchozí nastavení",
"mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky", "mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky",
"mailbox_defquota": "Výchozí velikost schránky", "mailbox_defquota": "Výchozí velikost schránky",
"mailbox_templates": "Šablony schránek",
"mailbox_quota": "Max. velikost schránky", "mailbox_quota": "Max. velikost schránky",
"mailboxes": "Mailové schránky", "mailboxes": "Mailové schránky",
"max_aliases": "Max. počet aliasů", "max_aliases": "Max. počet aliasů",
@@ -814,6 +890,8 @@
"table_size_show_n": "Zobrazit %s položek", "table_size_show_n": "Zobrazit %s položek",
"target_address": "Cílová adresa", "target_address": "Cílová adresa",
"target_domain": "Cílová doména", "target_domain": "Cílová doména",
"templates": "Šablony",
"template": "Šablona",
"tls_enforce_in": "Vynutit TLS pro příchozí", "tls_enforce_in": "Vynutit TLS pro příchozí",
"tls_enforce_out": "Vynutit TLS pro odchozí", "tls_enforce_out": "Vynutit TLS pro odchozí",
"tls_map_dest": "Cíl", "tls_map_dest": "Cíl",
@@ -829,7 +907,8 @@
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
"waiting": "Čekání", "waiting": "Čekání",
"weekly": "Každý týden", "weekly": "Každý týden",
"yes": "&#10003;" "yes": "&#10003;",
"relay_unknown": "Předávání neexistujících schránek"
}, },
"oauth2": { "oauth2": {
"access_denied": "K udělení přístupu se přihlašte jako vlastník mailové schránky.", "access_denied": "K udělení přístupu se přihlašte jako vlastník mailové schránky.",
@@ -894,7 +973,19 @@
"type": "Typ" "type": "Typ"
}, },
"queue": { "queue": {
"queue_manager": "Správce fronty" "queue_manager": "Správce fronty",
"delete": "Vymazat vše",
"info": "Poštovní fronta obsahuje všechny e-maily, které čekají na doručení. Pokud e-mail uvízne v poštovní frontě na delší dobu, systém jej automaticky odstraní.<br>Chybové hlášení příslušného e-mailu poskytuje informace o tom, proč se e-mail nepodařilo doručit.",
"flush": "Vyprázdnit frontu",
"legend": "Funkce operací poštovní fronty:",
"ays": "Potvrďte, že chcete opravdu odstranit všechny položky z aktuální fronty.",
"deliver_mail": "Doručit",
"deliver_mail_legend": "Opětovný pokus o doručení vybraných e-mailů.",
"hold_mail": "Podržet",
"hold_mail_legend": "Podrží vybrané e-maily. (Zabrání dalším pokusům o doručení)",
"show_message": "Zobrazit zprávu",
"unhold_mail": "Uvolnit",
"unhold_mail_legend": "Uvolnit vybrané e-maily k doručení. (Pouze v případě předchozího podržení)"
}, },
"ratelimit": { "ratelimit": {
"disabled": "Vypnuto", "disabled": "Vypnuto",
@@ -978,6 +1069,9 @@
"settings_map_added": "Přidána položka mapování nastavení", "settings_map_added": "Přidána položka mapování nastavení",
"settings_map_removed": "Položka mapování nastavení: %s smazána", "settings_map_removed": "Položka mapování nastavení: %s smazána",
"sogo_profile_reset": "SOGo profil uživatele %s vyresetován", "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_deleted": "Položka mapy TLS pravidel ID %s smazána",
"tls_policy_map_entry_saved": "Položka mapy TLS pravidel \"%s\" uložena", "tls_policy_map_entry_saved": "Položka mapy TLS pravidel \"%s\" uložena",
"ui_texts": "Změny UI textů uloženy", "ui_texts": "Změny UI textů uloženy",
@@ -985,7 +1079,9 @@
"verified_fido2_login": "Ověřené FIDO2 přihlášení", "verified_fido2_login": "Ověřené FIDO2 přihlášení",
"verified_totp_login": "TOTP přihlášení ověřeno", "verified_totp_login": "TOTP přihlášení ověřeno",
"verified_webauthn_login": "WebAuthn přihlášení ověřeno", "verified_webauthn_login": "WebAuthn přihlášení ověřeno",
"verified_yotp_login": "Yubico OTP přihlášení ověřeno" "verified_yotp_login": "Yubico OTP přihlášení ověřeno",
"cors_headers_edited": "Nastavení CORS byla uložena",
"domain_footer_modified": "Změny patičky domény %s byly uloženy"
}, },
"tfa": { "tfa": {
"api_register": "%s používá Yubico Cloud API. Prosím získejte API klíč pro své Yubico <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ZDE</a>", "api_register": "%s používá Yubico Cloud API. Prosím získejte API klíč pro své Yubico <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ZDE</a>",
@@ -1027,13 +1123,16 @@
"alias_valid_until": "Platný do", "alias_valid_until": "Platný do",
"aliases_also_send_as": "Smí odesílat také jako uživatel", "aliases_also_send_as": "Smí odesílat také jako uživatel",
"aliases_send_as_all": "Nekontrolovat přístup odesílatele pro následující doménu(y) a jejich aliasy domény:", "aliases_send_as_all": "Nekontrolovat přístup odesílatele pro následující doménu(y) a jejich aliasy domény:",
"allowed_protocols": "Povolené protokoly",
"app_hint": "Hesla aplikací jsou alternativní heslo pro přihlášení k IMAP, SMTP, CalDAV, CardDAV a EAS. Uživatelské jméno zůstává stejné.<br>SOGo však nelze s heslem aplikace použít.", "app_hint": "Hesla aplikací jsou alternativní heslo pro přihlášení k IMAP, SMTP, CalDAV, CardDAV a EAS. Uživatelské jméno zůstává stejné.<br>SOGo však nelze s heslem aplikace použít.",
"app_name": "Název aplikace", "app_name": "Název aplikace",
"app_passwds": "Hesla aplikací", "app_passwds": "Hesla aplikací",
"apple_connection_profile": "Profil připojení Apple", "apple_connection_profile": "Profil připojení Apple",
"apple_connection_profile_complete": "Tento profil obsahuje parametry připojení k IMAP, SMTP, CalDAV (kalendáře) a CardDAV (kontakty) pro zařízení Apple.", "apple_connection_profile_complete": "Tento profil obsahuje parametry připojení k IMAP, SMTP, CalDAV (kalendáře) a CardDAV (kontakty) pro zařízení Apple.",
"apple_connection_profile_mailonly": "Tento profil obsahuje parametry připojení k IMAP a SMTP pro zařízení Apple.", "apple_connection_profile_mailonly": "Tento profil obsahuje parametry připojení k IMAP a SMTP pro zařízení Apple.",
"apple_connection_profile_with_app_password": "Nové heslo aplikace se vygeneruje a přidá do profilu, takže při nastavování zařízení není třeba zadávat žádné heslo. Soubor nesdílejte, protože poskytuje plný přístup k vaší poštovní schránce.",
"change_password": "Změnit heslo", "change_password": "Změnit heslo",
"change_password_hint_app_passwords": "Váš účet má %d hesel aplikací, která nebudou změněna. Chcete-li je spravovat, přejděte na kartu Hesla aplikací.",
"clear_recent_successful_connections": "Vymazat nedávné úspěšné přihlášení", "clear_recent_successful_connections": "Vymazat nedávné úspěšné přihlášení",
"client_configuration": "Zobrazit průvodce nastavením e-mailových klientů a smartphonů", "client_configuration": "Zobrazit průvodce nastavením e-mailových klientů a smartphonů",
"create_app_passwd": "Vytvořit heslo aplikace", "create_app_passwd": "Vytvořit heslo aplikace",
@@ -1044,6 +1143,7 @@
"delete_ays": "Potvrďte odstranění.", "delete_ays": "Potvrďte odstranění.",
"direct_aliases": "Přímé aliasy", "direct_aliases": "Přímé aliasy",
"direct_aliases_desc": "Na přímé aliasy se uplatňuje filtr spamu a nastavení pravidel TLS", "direct_aliases_desc": "Na přímé aliasy se uplatňuje filtr spamu a nastavení pravidel TLS",
"direct_protocol_access": "Tento uživatel mailové schránky má <b>přímý externí přístup</b> k následujícím protokolům a aplikacím. Toto nastavení je řízeno správcem. Pro udělení přístupu k jednotlivým protokolům a aplikacím lze vytvořit hesla aplikací.<br>Tlačítko \" Přihlaste se do webmailu\" zajišťuje jednotné přihlášení k SOGo a je vždy k dispozici.",
"eas_reset": "Smazat mezipaměť zařízení ActiveSync", "eas_reset": "Smazat mezipaměť zařízení ActiveSync",
"eas_reset_help": "Obnovení mezipaměti zařízení pomůže zpravidla obnovit poškozený profil služby ActiveSync.<br><b>Upozornění:</b> Všechna data budou opětovně stažena!", "eas_reset_help": "Obnovení mezipaměti zařízení pomůže zpravidla obnovit poškozený profil služby ActiveSync.<br><b>Upozornění:</b> Všechna data budou opětovně stažena!",
"eas_reset_now": "Smazat", "eas_reset_now": "Smazat",
@@ -1137,15 +1237,15 @@
"spamfilter_yellow": "Žlutá: tato zpráva může být spam, bude označena jako spam a přesunuta do složky nevyžádané pošty", "spamfilter_yellow": "Žlutá: tato zpráva může být spam, bude označena jako spam a přesunuta do složky nevyžádané pošty",
"status": "Stav", "status": "Stav",
"sync_jobs": "Synchronizační úlohy", "sync_jobs": "Synchronizační úlohy",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Problém s autentifikací",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Chybné uživatelské jméno nebo heslo",
"syncjob_EXIT_CONNECTION_FAILURE": "Problém se spojením",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Nelze se připojit ke vzdálenému serveru",
"syncjob_EXIT_OVERQUOTA": "Cílová schránka je plná",
"syncjob_EXIT_TLS_FAILURE": "Problém se šifrovaným spojením",
"syncjob_EX_OK": "Úspěch",
"syncjob_check_log": "Zkontrolujte záznam", "syncjob_check_log": "Zkontrolujte záznam",
"syncjob_last_run_result": "Výsledek posledního spuštění", "syncjob_last_run_result": "Výsledek posledního spuštění",
"syncjob_EX_OK": "Úspěch",
"syncjob_EXIT_CONNECTION_FAILURE": "Problém se spojením",
"syncjob_EXIT_TLS_FAILURE": "Problém se šifrovaným spojením",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Problém s autentifikací",
"syncjob_EXIT_OVERQUOTA": "Cílová schránka je plná",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Nelze se připojit ke vzdálenému serveru",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Chybné uživatelské jméno nebo heslo",
"tag_handling": "Zacházení s označkovanou poštou", "tag_handling": "Zacházení s označkovanou poštou",
"tag_help_example": "Příklad e-mailové adresy se značkou: me<b>+Facebook</b>@example.org", "tag_help_example": "Příklad e-mailové adresy se značkou: me<b>+Facebook</b>@example.org",
"tag_help_explain": "V podsložce: v doručené poště bude vytvořena nová podsložka pojmenovaná po značce zprávy (\"INBOX / Facebook\").<br>\r\nV předmětu: název značky bude přidáván k předmětu mailu, například: \"[Facebook] Moje zprávy\".", "tag_help_explain": "V podsložce: v doručené poště bude vytvořena nová podsložka pojmenovaná po značce zprávy (\"INBOX / Facebook\").<br>\r\nV předmětu: název značky bude přidáván k předmětu mailu, například: \"[Facebook] Moje zprávy\".",
@@ -1165,8 +1265,10 @@
"week": "týden", "week": "týden",
"weekly": "Každý týden", "weekly": "Každý týden",
"weeks": "týdny", "weeks": "týdny",
"with_app_password": "s heslem aplikace",
"year": "rok", "year": "rok",
"years": "let" "years": "let",
"pushover_sound": "Zvukové upozornění"
}, },
"warning": { "warning": {
"cannot_delete_self": "Nelze smazat právě přihlášeného uživatele", "cannot_delete_self": "Nelze smazat právě přihlášeného uživatele",

View File

@@ -459,7 +459,7 @@
"cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.", "cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.",
"dns_records": "DNS-poster", "dns_records": "DNS-poster",
"dns_records_24hours": "Bemærk, at ændringer, der foretages i DNS, kan tage op til 24 timer for at få deres aktuelle status korrekt reflekteret på denne side. Det er beregnet som en måde for dig let at se, hvordan du konfigurerer dine DNS-poster og kontrollere, om alle dine poster er korrekt gemt i DNS.", "dns_records_24hours": "Bemærk, at ændringer, der foretages i DNS, kan tage op til 24 timer for at få deres aktuelle status korrekt reflekteret på denne side. Det er beregnet som en måde for dig let at se, hvordan du konfigurerer dine DNS-poster og kontrollere, om alle dine poster er korrekt gemt i DNS.",
"dns_records_docs": "Se også <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">dokumentationen</a>.", "dns_records_docs": "Se også <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentationen</a>.",
"dns_records_data": "Korrekte data", "dns_records_data": "Korrekte data",
"dns_records_name": "Navn", "dns_records_name": "Navn",
"dns_records_status": "Nuværende tilstand", "dns_records_status": "Nuværende tilstand",

View File

@@ -58,6 +58,7 @@
"domain": "Domain", "domain": "Domain",
"domain_matches_hostname": "Domain %s darf nicht dem Hostnamen entsprechen", "domain_matches_hostname": "Domain %s darf nicht dem Hostnamen entsprechen",
"domain_quota_m": "Domain-Speicherplatz gesamt (MiB)", "domain_quota_m": "Domain-Speicherplatz gesamt (MiB)",
"dry": "Synchronisation simulieren",
"enc_method": "Verschlüsselung", "enc_method": "Verschlüsselung",
"exclude": "Elemente ausschließen (Regex)", "exclude": "Elemente ausschließen (Regex)",
"full_name": "Vor- und Nachname", "full_name": "Vor- und Nachname",
@@ -346,7 +347,11 @@
"api_read_only": "Schreibgeschützter Zugriff", "api_read_only": "Schreibgeschützter Zugriff",
"api_read_write": "Lese-Schreib-Zugriff", "api_read_write": "Lese-Schreib-Zugriff",
"oauth2_apps": "OAuth2 Apps", "oauth2_apps": "OAuth2 Apps",
"queue_unban": "entsperren" "queue_unban": "entsperren",
"allowed_methods": "Access-Control-Allow-Methods",
"allowed_origins": "Access-Control-Allow-Origin",
"logo_dark_label": "Invertiert für den Darkmode",
"logo_normal_label": "Normal"
}, },
"danger": { "danger": {
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten", "access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
@@ -549,7 +554,7 @@
"dns_records": "DNS-Einträge", "dns_records": "DNS-Einträge",
"dns_records_24hours": "Bitte beachten Sie, dass es bis zu 24 Stunden dauern kann, bis Änderungen an Ihren DNS-Einträgen als aktueller Status auf dieser Seite dargestellt werden. Diese Seite ist nur als Hilfsmittel gedacht, um die korrekten Werte für DNS-Einträge anzuzeigen und zu überprüfen, ob die Daten im DNS hinterlegt sind.", "dns_records_24hours": "Bitte beachten Sie, dass es bis zu 24 Stunden dauern kann, bis Änderungen an Ihren DNS-Einträgen als aktueller Status auf dieser Seite dargestellt werden. Diese Seite ist nur als Hilfsmittel gedacht, um die korrekten Werte für DNS-Einträge anzuzeigen und zu überprüfen, ob die Daten im DNS hinterlegt sind.",
"dns_records_data": "Korrekte Daten", "dns_records_data": "Korrekte Daten",
"dns_records_docs": "Die <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">Online-Dokumentation</a> enthält weitere Informationen zur DNS-Konfiguration.", "dns_records_docs": "Die <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">Online-Dokumentation</a> enthält weitere Informationen zur DNS-Konfiguration.",
"dns_records_name": "Name", "dns_records_name": "Name",
"dns_records_status": "Aktueller Status", "dns_records_status": "Aktueller Status",
"dns_records_type": "Typ", "dns_records_type": "Typ",
@@ -574,6 +579,7 @@
"client_secret": "Client-Secret", "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.", "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", "created_on": "Erstellt am",
"custom_attributes": "benutzerdefinierte Attribute",
"delete1": "Lösche Nachricht nach Übertragung vom Quell-Server", "delete1": "Lösche Nachricht nach Übertragung vom Quell-Server",
"delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind", "delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind",
"delete2duplicates": "Lösche Duplikate im Ziel", "delete2duplicates": "Lösche Duplikate im Ziel",
@@ -582,6 +588,10 @@
"disable_login": "Login verbieten (Mails werden weiterhin angenommen)", "disable_login": "Login verbieten (Mails werden weiterhin angenommen)",
"domain": "Domain bearbeiten", "domain": "Domain bearbeiten",
"domain_admin": "Domain-Administrator bearbeiten", "domain_admin": "Domain-Administrator bearbeiten",
"domain_footer": "Domain wide footer",
"domain_footer_html": "HTML footer",
"domain_footer_info": "Domain wide footer werden allen ausgehenden E-Mails hinzugefügt, die einer Adresse innerhalb dieser Domain gehört.<br>Die folgenden Variablen können für den Footer benutzt werden:",
"domain_footer_plain": "PLAIN footer",
"domain_quota": "Domain Speicherplatz gesamt (MiB)", "domain_quota": "Domain Speicherplatz gesamt (MiB)",
"domains": "Domains", "domains": "Domains",
"dont_check_sender_acl": "Absender für Domain %s u. Alias-Domain nicht prüfen", "dont_check_sender_acl": "Absender für Domain %s u. Alias-Domain nicht prüfen",
@@ -610,6 +620,7 @@
"max_quota": "Max. Größe per Mailbox (MiB)", "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>", "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)", "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.", "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)", "mins_interval": "Intervall (min)",
"multiple_bookings": "Mehrfaches Buchen", "multiple_bookings": "Mehrfaches Buchen",
@@ -669,7 +680,11 @@
"unchanged_if_empty": "Unverändert, wenn leer", "unchanged_if_empty": "Unverändert, wenn leer",
"username": "Benutzername", "username": "Benutzername",
"validate_save": "Validieren und speichern", "validate_save": "Validieren und speichern",
"pushover_sound": "Ton" "pushover_sound": "Ton",
"domain_footer_info_vars": {
"auth_user": "{= auth_user =} - Angemeldeter Benutzername vom MTA",
"from_user": "{= from_user =} - Von Teil des Benutzers z.B. \"moo@mailcow.tld\" wird \"moo\" zurückgeben."
}
}, },
"fido2": { "fido2": {
"confirm": "Bestätigen", "confirm": "Bestätigen",
@@ -854,7 +869,7 @@
"sieve_preset_5": "Auto-Responder (Vacation, Urlaub)", "sieve_preset_5": "Auto-Responder (Vacation, Urlaub)",
"sieve_preset_6": "E-Mails mit Nachricht abweisen", "sieve_preset_6": "E-Mails mit Nachricht abweisen",
"sieve_preset_7": "Weiterleiten und behalten oder verwerfen", "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>.", "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": "Alias Sichtbarkeit in SOGo",
"sogo_visible_n": "Alias in SOGo verbergen", "sogo_visible_n": "Alias in SOGo verbergen",
@@ -967,7 +982,7 @@
"queue": { "queue": {
"delete": "Queue löschen", "delete": "Queue löschen",
"flush": "Queue flushen", "flush": "Queue flushen",
"info": "In der Mailqueue befinden sich alle E-Mails, welche auf eine Zustellung warten. Sollte eine E-Mail eine längere Zeit innerhalb der Mailqueue stecken wird diese automatisch vom System gelöscht.<br>Die Fehlermeldung der jeweiligen Mail gibt aufschluss darüber, warum diese nicht zugestellt werden konnte", "info": "In der Mailqueue befinden sich alle E-Mails, welche auf eine Zustellung warten. Sollte eine E-Mail eine längere Zeit innerhalb der Mailqueue stecken wird diese automatisch vom System gelöscht.<br>Die Fehlermeldung der jeweiligen Mail gibt Aufschluss darüber, warum diese nicht zugestellt werden konnte",
"legend": "Funktionen der Mailqueue Aktionen:", "legend": "Funktionen der Mailqueue Aktionen:",
"ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?", "ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?",
"deliver_mail": "Ausliefern", "deliver_mail": "Ausliefern",
@@ -1018,6 +1033,7 @@
"domain_admin_added": "Domain-Administrator %s wurde angelegt", "domain_admin_added": "Domain-Administrator %s wurde angelegt",
"domain_admin_modified": "Änderungen an Domain-Administrator %s wurden gespeichert", "domain_admin_modified": "Änderungen an Domain-Administrator %s wurden gespeichert",
"domain_admin_removed": "Domain-Administrator %s wurde entfernt", "domain_admin_removed": "Domain-Administrator %s wurde entfernt",
"domain_footer_modified": "Änderungen an Domain Footer %s wurden gespeichert",
"domain_modified": "Änderungen an Domain %s wurden gespeichert", "domain_modified": "Änderungen an Domain %s wurden gespeichert",
"domain_removed": "Domain %s wurde entfernt", "domain_removed": "Domain %s wurde entfernt",
"dovecot_restart_success": "Dovecot wurde erfolgreich neu gestartet", "dovecot_restart_success": "Dovecot wurde erfolgreich neu gestartet",
@@ -1121,8 +1137,9 @@
"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_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_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.", "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": "Passwort ändern",
"change_password_hint_app_passwords": "Ihre Mailbox hat {{number_of_app_passwords}} App-Passwörter, die nicht geändert werden. Um diese zu verwalten, gehen Sie bitte zum App-Passwörter-Tab.", "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", "clear_recent_successful_connections": "Alle erfolgreichen Verbindungen bereinigen",
"client_configuration": "Konfigurationsanleitungen für E-Mail-Programme und Smartphones anzeigen", "client_configuration": "Konfigurationsanleitungen für E-Mail-Programme und Smartphones anzeigen",
"create_app_passwd": "Erstelle App-Passwort", "create_app_passwd": "Erstelle App-Passwort",
@@ -1240,6 +1257,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.", "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", "user_settings": "Benutzereinstellungen",
"username": "Benutzername", "username": "Benutzername",
"value": "Wert",
"verify": "Verifizieren", "verify": "Verifizieren",
"waiting": "Warte auf Ausführung", "waiting": "Warte auf Ausführung",
"week": "Woche", "week": "Woche",

View File

@@ -58,6 +58,7 @@
"domain": "Domain", "domain": "Domain",
"domain_matches_hostname": "Domain %s matches hostname", "domain_matches_hostname": "Domain %s matches hostname",
"domain_quota_m": "Total domain quota (MiB)", "domain_quota_m": "Total domain quota (MiB)",
"dry": "Simulate synchronization",
"enc_method": "Encryption method", "enc_method": "Encryption method",
"exclude": "Exclude objects (regex)", "exclude": "Exclude objects (regex)",
"full_name": "Full name", "full_name": "Full name",
@@ -149,6 +150,8 @@
"ays": "Are you sure you want to proceed?", "ays": "Are you sure you want to proceed?",
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.", "ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.",
"change_logo": "Change logo", "change_logo": "Change logo",
"logo_normal_label": "Normal",
"logo_dark_label": "Inverted for dark mode",
"configuration": "Configuration", "configuration": "Configuration",
"convert_html_to_text": "Convert HTML to plain text", "convert_html_to_text": "Convert HTML to plain text",
"copy_to_clipboard": "Text copied to clipboard!", "copy_to_clipboard": "Text copied to clipboard!",
@@ -551,7 +554,7 @@
"dns_records": "DNS Records", "dns_records": "DNS Records",
"dns_records_24hours": "Please note that changes made to DNS may take up to 24 hours to correctly have their current state reflected on this page. It is intended as a way for you to easily see how to configure your DNS records and to check whether all your records are correctly stored in DNS.", "dns_records_24hours": "Please note that changes made to DNS may take up to 24 hours to correctly have their current state reflected on this page. It is intended as a way for you to easily see how to configure your DNS records and to check whether all your records are correctly stored in DNS.",
"dns_records_data": "Correct Data", "dns_records_data": "Correct Data",
"dns_records_docs": "Please also consult <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">the documentation</a>.", "dns_records_docs": "Please also consult <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">the documentation</a>.",
"dns_records_name": "Name", "dns_records_name": "Name",
"dns_records_status": "Current State", "dns_records_status": "Current State",
"dns_records_type": "Type", "dns_records_type": "Type",
@@ -576,6 +579,7 @@
"client_secret": "Client secret", "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", "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", "created_on": "Created on",
"custom_attributes": "Custom attributes",
"delete1": "Delete from source when completed", "delete1": "Delete from source when completed",
"delete2": "Delete messages on destination that are not on source", "delete2": "Delete messages on destination that are not on source",
"delete2duplicates": "Delete duplicates on destination", "delete2duplicates": "Delete duplicates on destination",
@@ -584,6 +588,18 @@
"disable_login": "Disallow login (incoming mail is still accepted)", "disable_login": "Disallow login (incoming mail is still accepted)",
"domain": "Edit domain", "domain": "Edit domain",
"domain_admin": "Edit domain administrator", "domain_admin": "Edit domain administrator",
"domain_footer": "Domain wide footer",
"domain_footer_html": "HTML footer",
"domain_footer_info": "Domain-wide footers are added to all outgoing emails associated with an address within this domain. <br> The following variables can be used for the footer:",
"domain_footer_info_vars": {
"auth_user": "{= auth_user =} - Authenticated Username specified by an MTA",
"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",
"custom": "{= foo =} - If mailbox has the custom attribute \"foo\" with value \"bar\" it returns \"bar\""
},
"domain_footer_plain": "PLAIN footer",
"domain_quota": "Domain quota", "domain_quota": "Domain quota",
"domains": "Domains", "domains": "Domains",
"dont_check_sender_acl": "Disable sender check for domain %s (+ alias domains)", "dont_check_sender_acl": "Disable sender check for domain %s (+ alias domains)",
@@ -612,6 +628,7 @@
"max_quota": "Max. quota per mailbox (MiB)", "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>", "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>", "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.", "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)", "mins_interval": "Interval (min)",
"multiple_bookings": "Multiple bookings", "multiple_bookings": "Multiple bookings",
@@ -863,7 +880,7 @@
"sieve_preset_5": "Auto responder (vacation)", "sieve_preset_5": "Auto responder (vacation)",
"sieve_preset_6": "Reject mail with reponse", "sieve_preset_6": "Reject mail with reponse",
"sieve_preset_7": "Redirect and keep/drop", "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>.", "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": "Alias is visible in SOGo",
"sogo_visible_n": "Hide alias in SOGo", "sogo_visible_n": "Hide alias in SOGo",
@@ -1027,6 +1044,7 @@
"domain_admin_added": "Domain administrator %s has been added", "domain_admin_added": "Domain administrator %s has been added",
"domain_admin_modified": "Changes to domain administrator %s have been saved", "domain_admin_modified": "Changes to domain administrator %s have been saved",
"domain_admin_removed": "Domain administrator %s has been removed", "domain_admin_removed": "Domain administrator %s has been removed",
"domain_footer_modified": "Changes to domain footer %s have been saved",
"domain_modified": "Changes to domain %s have been saved", "domain_modified": "Changes to domain %s have been saved",
"domain_removed": "Domain %s has been removed", "domain_removed": "Domain %s has been removed",
"dovecot_restart_success": "Dovecot was restarted successfully", "dovecot_restart_success": "Dovecot was restarted successfully",
@@ -1130,8 +1148,9 @@
"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_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_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.", "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": "Change password",
"change_password_hint_app_passwords": "Your account has {{number_of_app_passwords}} app passwords that will not be changed. To manage these, go to the App passwords tab.", "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", "clear_recent_successful_connections": "Clear seen successful connections",
"client_configuration": "Show configuration guides for email clients and smartphones", "client_configuration": "Show configuration guides for email clients and smartphones",
"create_app_passwd": "Create app password", "create_app_passwd": "Create app password",
@@ -1260,6 +1279,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.", "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", "user_settings": "User settings",
"username": "Username", "username": "Username",
"value": "Value",
"verify": "Verify", "verify": "Verify",
"waiting": "Waiting", "waiting": "Waiting",
"week": "week", "week": "week",

View File

@@ -20,7 +20,9 @@
"tls_policy": "Póliza de TLS", "tls_policy": "Póliza de TLS",
"unlimited_quota": "Cuota ilimitada para buzones", "unlimited_quota": "Cuota ilimitada para buzones",
"app_passwds": "Gestionar las contraseñas de aplicaciones", "app_passwds": "Gestionar las contraseñas de aplicaciones",
"domain_desc": "Cambiar descripción del dominio" "domain_desc": "Cambiar descripción del dominio",
"protocol_access": "Cambiar protocolo de acceso",
"quarantine_category": "Cambiar categoría de las notificaciones de cuarentena"
}, },
"add": { "add": {
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.", "activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
@@ -85,7 +87,13 @@
"timeout2": "Tiempo de espera para la conexión al host local", "timeout2": "Tiempo de espera para la conexión al host local",
"username": "Usuario", "username": "Usuario",
"validate": "Validar", "validate": "Validar",
"validation_success": "Validado exitosamente" "validation_success": "Validado exitosamente",
"inactive": "Inactivo",
"app_name": "Nombre de la App",
"app_password": "Añadir contraseña para la app",
"public_comment": "Comentarios públicos",
"disable_login": "Desactivar login (el correo entrante seguirá activo)",
"comment_info": "Los comentarios privados no son visibles al usuario, mientras que los comentarios públicos aparecerán sobre la información general del usuario"
}, },
"admin": { "admin": {
"access": "Acceso", "access": "Acceso",
@@ -114,7 +122,7 @@
"app_name": "Nombre de la app", "app_name": "Nombre de la app",
"apps_name": "Nombre \"mailcow Apps\"", "apps_name": "Nombre \"mailcow Apps\"",
"arrival_time": "Tiempo de llegada (hora del servidor)", "arrival_time": "Tiempo de llegada (hora del servidor)",
"ban_list_info": "La lista de IPs bloqueadas sigue a continuación: <b> red (tiempo de prohibición restante) - [acciones]</b>.<br/> Las IPs en cola para ser desbloquadas se eliminarán de la lista de bloqueos en unos pocos segundos. <br/> Las etiquetas rojas indican bloqueos permanentes permanentes mediante la inclusión en una lista negra.", "ban_list_info": "Lista de IPs bloqueadas: <b>red (tiempo de prohibición restante) - [acciones]</b>.<br />Las IPs en cola para ser desbloqueadas se eliminarán de la lista de bloqueos en unos pocos segundos.<br />Las etiquetas rojas indican bloqueos permanentes mediante la inclusión en la lista negra.",
"change_logo": "Cambiar logo", "change_logo": "Cambiar logo",
"configuration": "Configuración", "configuration": "Configuración",
"credentials_transport_warning": "<b>Advertencia</b>: al agregar una nueva entrada de ruta de transporte se actualizarán las credenciales para todas las entradas con una columna de \"siguiente destino\" coincidente.", "credentials_transport_warning": "<b>Advertencia</b>: al agregar una nueva entrada de ruta de transporte se actualizarán las credenciales para todas las entradas con una columna de \"siguiente destino\" coincidente.",
@@ -432,7 +440,7 @@
}, },
"header": { "header": {
"administration": "Administración", "administration": "Administración",
"debug": "Información del sistema", "debug": "Información",
"email": "E-Mail", "email": "E-Mail",
"mailcow_config": "Configuración", "mailcow_config": "Configuración",
"quarantine": "Cuarentena", "quarantine": "Cuarentena",

View File

@@ -891,5 +891,17 @@
"no_active_admin": "Viimeistä aktiivista järjestelmänvalvojaa ei voi poistaa käytöstä", "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_token": "Lomakkeen tunnus sanoma ei kelpaa: tunnus sanoman risti riita",
"session_ua": "Lomakkeen tunnus sanoma ei kelpaa: käyttäjä agentin tarkistus virhe" "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_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.", "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\".", "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", "select_domain": "Sélectionner d'abord un domaine",
"sieve_desc": "Description courte", "sieve_desc": "Description courte",
"sieve_type": "Type de filtre", "sieve_type": "Type de filtre",
@@ -207,7 +207,7 @@
"last_applied": "Dernière application", "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>", "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", "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é.", "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)", "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", "main_name": "\"mailcow UI\" nom",
@@ -326,7 +326,11 @@
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules", "password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
"password_policy_numbers": "Doit contenir au moins un chiffre", "password_policy_numbers": "Doit contenir au moins un chiffre",
"ip_check": "Vérification IP", "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": { "danger": {
"access_denied": "Accès refusé ou données de formulaire non valides", "access_denied": "Accès refusé ou données de formulaire non valides",
@@ -479,7 +483,7 @@
"cname_from_a": "Valeur dérivée de lenregistrement A/AAAA. Ceci est supporté tant que lenregistrement indique la bonne ressource.", "cname_from_a": "Valeur dérivée de lenregistrement A/AAAA. Ceci est supporté tant que lenregistrement indique la bonne ressource.",
"dns_records": "Enregistrements DNS", "dns_records": "Enregistrements DNS",
"dns_records_24hours": "Veuillez noter que les modifications apportées au DNS peuvent prendre jusquà 24 heures pour que leurs états actuels soient correctement reflétés sur cette page. Il est conçu comme un moyen pour vous de voir facilement comment configurer vos enregistrements DNS et de vérifier si tous vos enregistrements sont correctement stockés dans les DNS.", "dns_records_24hours": "Veuillez noter que les modifications apportées au DNS peuvent prendre jusquà 24 heures pour que leurs états actuels soient correctement reflétés sur cette page. Il est conçu comme un moyen pour vous de voir facilement comment configurer vos enregistrements DNS et de vérifier si tous vos enregistrements sont correctement stockés dans les DNS.",
"dns_records_docs": "Veuillez également consulter <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">la documentation</a>.", "dns_records_docs": "Veuillez également consulter <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">la documentation</a>.",
"dns_records_data": "Données correcte", "dns_records_data": "Données correcte",
"dns_records_name": "Nom", "dns_records_name": "Nom",
"dns_records_status": "Etat courant", "dns_records_status": "Etat courant",
@@ -598,9 +602,9 @@
"delete_these_items": "Veuillez confirmer les modifications apportées à lidentifiant dobjet suivant", "delete_these_items": "Veuillez confirmer les modifications apportées à lidentifiant dobjet suivant",
"hibp_nok": "Trouvé ! Il sagit dun mot de passe potentiellement dangereux !", "hibp_nok": "Trouvé ! Il sagit dun mot de passe potentiellement dangereux !",
"hibp_ok": "Aucune correspondance trouvée.", "hibp_ok": "Aucune correspondance trouvée.",
"loading": "Veuillez patienter...", "loading": "Veuillez patienter",
"restart_container": "Redémarrer le conteneur", "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", "restart_now": "Redémarrer maintenant",
"restarting_container": "Redémarrage du conteneur, cela peut prendre un certain temps" "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", "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", "enter_qr_code": "Votre code TOTP si votre appareil ne peut pas scanner les codes QR",
"error_code": "Code d'erreur", "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": "Un identifiant pour votre Périphérique",
"key_id_totp": "Un identifiant pour votre clé", "key_id_totp": "Un identifiant pour votre clé",
"none": "Désactiver", "none": "Désactiver",
@@ -948,8 +952,8 @@
"tfa_token_invalid": "Token TFA invalide", "tfa_token_invalid": "Token TFA invalide",
"totp": "OTP (One Time Password = Mot de passe à usage unique : Google Authenticator, Authy, etc.)", "totp": "OTP (One Time Password = Mot de passe à usage unique : Google Authenticator, Authy, etc.)",
"webauthn": "Authentification WebAuthn", "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_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_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" "yubi_otp": "Authentification OTP Yubico"
}, },
"fido2": { "fido2": {
@@ -999,9 +1003,9 @@
"eas_reset": "Réinitialiser le cache de lappareil Activesync", "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_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", "eas_reset_now": "Réinitialiser maintenant",
"edit": "Editer", "edit": "Éditer",
"email": "Email", "email": "E-mail",
"email_and_dav": "Email, calendriers et contacts", "email_and_dav": "E-mail, calendriers et contacts",
"encryption": "Cryptage", "encryption": "Cryptage",
"excludes": "Exclut", "excludes": "Exclut",
"expire_in": "Expire dans", "expire_in": "Expire dans",
@@ -1015,7 +1019,7 @@
"is_catch_all": "Attrape-tout pour le domaine(s)", "is_catch_all": "Attrape-tout pour le domaine(s)",
"last_mail_login": "Dernière connexion mail", "last_mail_login": "Dernière connexion mail",
"last_run": "Dernière exécution", "last_run": "Dernière exécution",
"loading": "Chargement...", "loading": "Chargement",
"mailbox_details": "Détails de la boîte", "mailbox_details": "Détails de la boîte",
"messages": "messages", "messages": "messages",
"never": "jamais", "never": "jamais",
@@ -1051,7 +1055,7 @@
"shared_aliases": "Adresses alias partagées", "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.", "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", "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_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", "sogo_profile_reset_now": "Remise à zéro du profil maintenant",
"spam_aliases": "Alias de courriel temporaire", "spam_aliases": "Alias de courriel temporaire",
@@ -1096,7 +1100,8 @@
"weeks": "semaines", "weeks": "semaines",
"months": "mois", "months": "mois",
"year": "année", "year": "année",
"years": "années" "years": "années",
"with_app_password": "avec le mot de passe de l'application"
}, },
"warning": { "warning": {
"cannot_delete_self": "Impossible de supprimer lutilisateur connecté", "cannot_delete_self": "Impossible de supprimer lutilisateur connecté",

View File

@@ -0,0 +1,20 @@
{
"user": {
"verify": "Επαλήθευση",
"week": "εβδομάδα",
"weekly": "Εβδομαδιαία",
"weeks": "Εβδομάδες",
"with_app_password": "με κωδικό εφαρμογής",
"year": "χρόνος",
"years": "χρόνια"
},
"warning": {
"cannot_delete_self": "Αδυναμία διαγραφής συνδεδεμένου χρήστη",
"dovecot_restart_failed": "Απέτυχε η επανεκκίνηση του Dovecot, παρακαλώ ελέγξτε τα αρχεία καταγραφής.",
"no_active_admin": "Αδυναμία απενεργοποίησης του τελευταίου ενεργού διαχειριστή",
"domain_added_sogo_failed": "Προστέθηκε το όνομα χώρου αλλά απέτυχε η επανεκκίνηση του SOGo.",
"hash_not_found": "Η κατακερματισμένη τιμή (hash value) δεν βρέθηκε ή έχει είδη διαγραφεί.",
"ip_invalid": "Παραλείφθηκε μη έγκυρη διεύθυνση IP: %s",
"is_not_primary_alias": "Παραλείφθηκε μη πρωτεύον ψευδώνυμο %s"
}
}

View File

@@ -18,7 +18,11 @@
"transport_dest_format": "Szintaxis: pelda.hu, .pelda.hu, *, fiok@pelda.hu (több érték esetén vesszővel elválasztva)", "transport_dest_format": "Szintaxis: pelda.hu, .pelda.hu, *, fiok@pelda.hu (több érték esetén vesszővel elválasztva)",
"upload": "Feltöltés", "upload": "Feltöltés",
"username": "Felhasználónév", "username": "Felhasználónév",
"verify": "Ellenőrzés" "verify": "Ellenőrzés",
"activate_api": "API aktiválása",
"activate_send": "Küldés gomb aktiválása",
"add": "Hozzáad",
"active": "Aktív"
}, },
"edit": { "edit": {
"active": "Aktív", "active": "Aktív",
@@ -385,9 +389,23 @@
"acl": { "acl": {
"delimiter_action": "Elhatárolás", "delimiter_action": "Elhatárolás",
"alias_domains": "Alias domainek hozzáadása", "alias_domains": "Alias domainek hozzáadása",
"app_passwds": "Alkalmazás jelszavak kezelése" "app_passwds": "Alkalmazás jelszavak kezelése",
"domain_desc": "Domain leírás módosítása",
"filters": "Szűrők",
"login_as": "Bejelentkezés mint",
"quarantine": "Karantén műveletek",
"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": { "diagnostics": {
"dns_records": "DNS bejegyzések" "dns_records": "DNS bejegyzések"
},
"add": {
"username": "Felhasználónév",
"validation_success": "Sikeres ellenőrzés"
} }
} }

View File

@@ -134,10 +134,10 @@
"admins_ldap": "Amministratori LDAP", "admins_ldap": "Amministratori LDAP",
"advanced_settings": "Impostazioni avanzate", "advanced_settings": "Impostazioni avanzate",
"api_allow_from": "Allow API access from these IPs/CIDR network notations", "api_allow_from": "Allow API access from these IPs/CIDR network notations",
"api_info": "The API is a work in progress. The documentation can be found at <a href=\"/api\">/api</a>", "api_info": "Questa API è in modifica. La documentazione può essere trovata su <a href=\"/api\">/api</a>",
"api_key": "Chiave API", "api_key": "Chiave API",
"api_skip_ip_check": "Salta il controllo dell'IP per l'API", "api_skip_ip_check": "Salta il controllo dell'IP per l'API",
"app_links": "App links", "app_links": "Link dell'app",
"app_name": "Nome dell'app", "app_name": "Nome dell'app",
"apps_name": "Nome \"mailcow Apps\"", "apps_name": "Nome \"mailcow Apps\"",
"arrival_time": "Ora di arrivo (ora del server)", "arrival_time": "Ora di arrivo (ora del server)",
@@ -338,7 +338,8 @@
"oauth2_apps": "App OAuth2", "oauth2_apps": "App OAuth2",
"oauth2_add_client": "Aggiungere il client OAuth2", "oauth2_add_client": "Aggiungere il client OAuth2",
"rsettings_preset_4": "Disattivare Rspamd per un dominio", "rsettings_preset_4": "Disattivare Rspamd per un dominio",
"options": "Opzioni" "options": "Opzioni",
"cors_settings": "Impostazioni CORS"
}, },
"danger": { "danger": {
"access_denied": "Accesso negato o form di login non corretto", "access_denied": "Accesso negato o form di login non corretto",
@@ -505,7 +506,7 @@
"dns_records": "Record DNS", "dns_records": "Record DNS",
"dns_records_24hours": "Tieni presente che le modifiche apportate ai record DNS potrebbero richiedere fino a 24 ore per poter essere visualizzate correttamente in questa pagina. Tutto ciò è da intendersi come un modo per voi di vedere come configurare i record DNS e per controllare se tutti i record DNS sono stati inseriti correttamente.", "dns_records_24hours": "Tieni presente che le modifiche apportate ai record DNS potrebbero richiedere fino a 24 ore per poter essere visualizzate correttamente in questa pagina. Tutto ciò è da intendersi come un modo per voi di vedere come configurare i record DNS e per controllare se tutti i record DNS sono stati inseriti correttamente.",
"dns_records_data": "Dati corretti", "dns_records_data": "Dati corretti",
"dns_records_docs": "Si prega di consultare anche <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">la documentazione</a>.", "dns_records_docs": "Si prega di consultare anche <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">la documentazione</a>.",
"dns_records_name": "Nome", "dns_records_name": "Nome",
"dns_records_status": "Stato attuale", "dns_records_status": "Stato attuale",
"dns_records_type": "Tipo", "dns_records_type": "Tipo",
@@ -1194,7 +1195,7 @@
"weeks": "settimane", "weeks": "settimane",
"year": "anno", "year": "anno",
"years": "anni", "years": "anni",
"change_password_hint_app_passwords": "Il tuo account ha {{number_of_app_passwords}} password delle app che non verranno modificate. Per gestirle, vai alla scheda App passwords.", "change_password_hint_app_passwords": "Il tuo account ha %d password delle app che non verranno modificate. Per gestirle, vai alla scheda App passwords.",
"syncjob_check_log": "Controlla i log", "syncjob_check_log": "Controlla i log",
"syncjob_last_run_result": "Risultato dell'ultima esecuzione", "syncjob_last_run_result": "Risultato dell'ultima esecuzione",
"open_logs": "Apri i log", "open_logs": "Apri i log",

View File

@@ -102,7 +102,9 @@
"timeout2": "Time-out voor verbinding met lokale hosts", "timeout2": "Time-out voor verbinding met lokale hosts",
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"validate": "Verifieer", "validate": "Verifieer",
"validation_success": "Succesvol geverifieerd" "validation_success": "Succesvol geverifieerd",
"tags": "Tags",
"bcc_dest_format": "BCC-bestemming moet één geldig e-mailadres zijn.<br>Als u een kopie naar meerdere adressen wilt sturen, maak dan een alias aan en gebruik die hier."
}, },
"admin": { "admin": {
"access": "Toegang", "access": "Toegang",
@@ -310,7 +312,32 @@
"upload": "Upload", "upload": "Upload",
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"validate_license_now": "Valideer licentie", "validate_license_now": "Valideer licentie",
"verify": "Verifieer" "verify": "Verifieer",
"logo_normal_label": "Normaal",
"logo_dark_label": "Omgekeerd voor donkere modus",
"cors_settings": "CORS instellingen",
"is_mx_based": "MX gebasseerd",
"password_length": "Wachtwoordlengte",
"password_policy": "Wachtwoordbeleid",
"password_policy_chars": "Moet tenminste één alfanumeriek karakter bevatten",
"password_policy_length": "Minimale wachtwoord lengte is %d",
"ip_check": "IP controle",
"ip_check_disabled": "IP controle is uitgeschakeld. Je kan het inschakelen onder<br> <strong>Systeem > Configuratie > Opties > Aanpassen</strong>",
"oauth2_apps": "OAuth2 applicaties",
"oauth2_add_client": "Voeg OAuth2 client toe",
"options": "Opties",
"password_policy_lowerupper": "Moet hoofdletters en kleine letters bevatten",
"password_policy_numbers": "Moet ten minste één nummer bevatten",
"password_policy_special_chars": "Moet tenminste één speciaal teken bevatten",
"relay_rcpt": "\"Aan:\" adres",
"rsettings_preset_4": "Rspamd voor domein uitschakelen",
"service": "Dienst",
"success": "Succes",
"admins": "Administrators",
"admins_ldap": "LDAP administrators",
"api_read_only": "Alleen-lezen toegang",
"api_read_write": "Lees en schrijf toegang",
"login_time": "Login tijd"
}, },
"danger": { "danger": {
"access_denied": "Toegang geweigerd of ongeldige gegevens", "access_denied": "Toegang geweigerd of ongeldige gegevens",
@@ -429,7 +456,14 @@
"username_invalid": "Gebruikersnaam %s kan niet worden gebruikt", "username_invalid": "Gebruikersnaam %s kan niet worden gebruikt",
"validity_missing": "Wijs een geldigheidstermijn toe", "validity_missing": "Wijs een geldigheidstermijn toe",
"value_missing": "Niet alle waarden zijn ingevuld", "value_missing": "Niet alle waarden zijn ingevuld",
"yotp_verification_failed": "Yubico OTP-verificatie mislukt: %s" "yotp_verification_failed": "Yubico OTP-verificatie mislukt: %s",
"cors_invalid_method": "Ongeldige Allow-Method opgegeven",
"webauthn_authenticator_failed": "De geselecteerde authenticator kon niet wordcen gevonden",
"webauthn_publickey_failed": "Er was geen publieke sleutel opgeslagen voor de geselecteerde authenticator",
"demo_mode_enabled": "Demo modus is ingeschakeld",
"template_exists": "Sjabloon %s bestaat al",
"template_id_invalid": "Sjabloon ID %s ongeldig",
"template_name_invalid": "Sjabloon naam ongeldig"
}, },
"debug": { "debug": {
"chart_this_server": "Grafiek (deze server)", "chart_this_server": "Grafiek (deze server)",
@@ -452,13 +486,31 @@
"uptime": "Uptime", "uptime": "Uptime",
"started_on": "Gestart op", "started_on": "Gestart op",
"static_logs": "Statische logs", "static_logs": "Statische logs",
"system_containers": "Systeem & containers" "system_containers": "Systeem & containers",
"container_running": "Actief",
"container_disabled": "Container gestopt of uitgeschakeld",
"container_stopped": "Gestopt",
"current_time": "Systeemtijd",
"error_show_ip": "Kon het publieke IP adres niet resolven",
"login_time": "Tijd",
"memory": "Geheugen",
"show_ip": "Toon publiek IP",
"success": "Succes",
"timezone": "Tijdzone",
"update_available": "Er is een update beschikbaar",
"no_update_available": "Het systeem heeft de laatste versie",
"update_failed": "Kon niet op updates controleren",
"username": "Gebruikersnaam",
"wip": "Momenteel werk in uitvoering",
"architecture": "Architectuur",
"cores": "Kernen",
"service": "Dienst"
}, },
"diagnostics": { "diagnostics": {
"cname_from_a": "Waarde afgeleid van een A- of AAAA-vermelding.", "cname_from_a": "Waarde afgeleid van een A- of AAAA-vermelding.",
"dns_records": "DNS-configuratie", "dns_records": "DNS-configuratie",
"dns_records_24hours": "Houd er rekening mee dat wijzigingen aan DNS tot wel 24 uur in beslag kunnen nemen voordat ze op deze pagina worden weergegeven. Deze informatie is bedoeld om gemakkelijk te bekijken of de DNS-configuratie aan de eisen voldoet.", "dns_records_24hours": "Houd er rekening mee dat wijzigingen aan DNS tot wel 24 uur in beslag kunnen nemen voordat ze op deze pagina worden weergegeven. Deze informatie is bedoeld om gemakkelijk te bekijken of de DNS-configuratie aan de eisen voldoet.",
"dns_records_docs": "Raadpleeg ook <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">de documentatie</a>.", "dns_records_docs": "Raadpleeg ook <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">de documentatie</a>.",
"dns_records_data": "Correcte gegevens", "dns_records_data": "Correcte gegevens",
"dns_records_name": "Naam", "dns_records_name": "Naam",
"dns_records_status": "Huidige staat", "dns_records_status": "Huidige staat",
@@ -567,7 +619,15 @@
"title": "Wijzig object", "title": "Wijzig object",
"unchanged_if_empty": "Laat leeg wanneer ongewijzigd", "unchanged_if_empty": "Laat leeg wanneer ongewijzigd",
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"validate_save": "Verifieer en sla op" "validate_save": "Verifieer en sla op",
"domain_footer_info": "Domeinwijde voetteksten worden toegevoegd aan alle uitgaande e-mails die zijn gekoppeld aan een adres binnen dit domein. <br> De volgende variabelen kunnen worden gebruikt voor de voettekst:",
"admin": "Bewerk administrator",
"app_passwd_protocols": "Toegestane protocollen voor app wachtwoord",
"created_on": "Gemaakt op",
"acl": "ACL (Toestemming)",
"domain_footer": "Domeinbreede footer",
"domain_footer_html": "HTML footer",
"mailbox_relayhost_info": "Wordt alleen toegepast op de mailbox en directe aliassen, maar heft een domein relayhost op."
}, },
"footer": { "footer": {
"cancel": "Annuleren", "cancel": "Annuleren",
@@ -1082,5 +1142,29 @@
"quota_exceeded_scope": "Domeinquota overschreden: Voor dit domein kunnen uitsluitend onbeperkte mailboxen aangemaakt worden.", "quota_exceeded_scope": "Domeinquota overschreden: Voor dit domein kunnen uitsluitend onbeperkte mailboxen aangemaakt worden.",
"session_token": "Token ongeldig: komt niet overeen", "session_token": "Token ongeldig: komt niet overeen",
"session_ua": "Token ongeldig: gebruikersagentvalidatie mislukt" "session_ua": "Token ongeldig: gebruikersagentvalidatie mislukt"
},
"datatables": {
"emptyTable": "Geen data beschikbaar in tabel",
"expand_all": "Alles uitbreiden",
"paginate": {
"last": "Laatste",
"next": "Volgende",
"previous": "Vorige",
"first": "Eerste"
},
"aria": {
"sortAscending": ": activeer om kolommen oplopend te sorteren",
"sortDescending": ": activeer om kolommen aflopend te sorteren"
},
"collapse_all": "Alle samenvoegen",
"decimal": ".",
"info": "_START_ tot _END_ van _TOTAL_ worden getoond",
"infoEmpty": "0 tot 0 van 0 items worden getoond",
"thousands": ",",
"lengthMenu": "Toon _MENU_ items",
"loadingRecords": "Laden...",
"processing": "Wachten alstublieft..",
"search": "Zoeken:",
"zeroRecords": "Geen overeenkomsten gevonden"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -106,7 +106,8 @@
"timeout2": "Timeout pentru conectarea la gazda locală", "timeout2": "Timeout pentru conectarea la gazda locală",
"username": "Nume de utilizator", "username": "Nume de utilizator",
"validate": "Validează", "validate": "Validează",
"validation_success": "Validat cu succes" "validation_success": "Validat cu succes",
"tags": "Etichete"
}, },
"admin": { "admin": {
"access": "Acces", "access": "Acces",
@@ -334,7 +335,15 @@
"username": "Nume de utilizator", "username": "Nume de utilizator",
"validate_license_now": "Validează GUID cu serverul de licență", "validate_license_now": "Validează GUID cu serverul de licență",
"verify": "Verifică", "verify": "Verifică",
"yes": "&#10003;" "yes": "&#10003;",
"cors_settings": "Setări CORS",
"f2b_ban_time_increment": "Timpul de blocare creşte cu fiecare blocare",
"f2b_max_ban_time": "Max. timp de blocare (s)",
"ip_check": "Verificaţie IP",
"ip_check_disabled": "Verificarea IP este dezactivată. Puteţi activa la<br> <strong>Sistem > Configuraţie > Opţiuni > Personalizează</strong>",
"ip_check_opt_in": "Alegeţi să folosiţi servicile <strong>ipv4.mailcow.email</strong> şi <strong>ipv6.mailcow.email</strong> să rezolvaţi addrese IP externale.",
"options": "Opţiuni",
"queue_unban": "retractează interzicere"
}, },
"danger": { "danger": {
"access_denied": "Accesul a fost respins sau datele formularului sunt invalide", "access_denied": "Accesul a fost respins sau datele formularului sunt invalide",
@@ -453,7 +462,16 @@
"username_invalid": "Numele de utilizator %s nu poate fi utilizat", "username_invalid": "Numele de utilizator %s nu poate fi utilizat",
"validity_missing": "Atribuie o perioadă de valabilitate", "validity_missing": "Atribuie o perioadă de valabilitate",
"value_missing": "Furnizează toate valorile", "value_missing": "Furnizează toate valorile",
"yotp_verification_failed": "Verificarea Yubico OTP a eșuat: %s" "yotp_verification_failed": "Verificarea Yubico OTP a eșuat: %s",
"cors_invalid_method": "Aveţi specificaţi 'Allow-Method' invalid",
"webauthn_authenticator_failed": "Authentificator selectat nu a fost găsit",
"webauthn_publickey_failed": "Nici-o cheie publică a fost salvată pentru authenticatorul selectat",
"webauthn_username_failed": "Authenticatorul selectat aparţine la alt cont",
"demo_mode_enabled": "Mod de demonstraţie este activ",
"extended_sender_acl_denied": "lipseşte ACL pentru setarea adrese externe",
"template_exists": "Şablon %s deja există",
"template_id_invalid": "Şablon ID %s este invalid",
"template_name_invalid": "Nume de şablon este invalid"
}, },
"debug": { "debug": {
"chart_this_server": "Grafic (acest server)", "chart_this_server": "Grafic (acest server)",
@@ -486,7 +504,7 @@
"cname_from_a": "Valoare derivată din înregistrarea A/AAAA. Acest lucru este acceptat atâta timp cât înregistrarea indică resursele corecte.", "cname_from_a": "Valoare derivată din înregistrarea A/AAAA. Acest lucru este acceptat atâta timp cât înregistrarea indică resursele corecte.",
"dns_records": "Înregistrări DNS", "dns_records": "Înregistrări DNS",
"dns_records_24hours": "Rețineți că modificările aduse DNS-ului pot dura până la 24 de ore pentru a reflecta corect starea lor curentă pe această pagină. Acest mecanism este conceput ca o modalitate să vezi ușor cum să îți configurezi înregistrările DNS și să verifici dacă toate înregistrările sunt stocate corect în DNS.", "dns_records_24hours": "Rețineți că modificările aduse DNS-ului pot dura până la 24 de ore pentru a reflecta corect starea lor curentă pe această pagină. Acest mecanism este conceput ca o modalitate să vezi ușor cum să îți configurezi înregistrările DNS și să verifici dacă toate înregistrările sunt stocate corect în DNS.",
"dns_records_docs": "Vă rugăm să consultați și <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">documentația</a>.", "dns_records_docs": "Vă rugăm să consultați și <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">documentația</a>.",
"dns_records_data": "Date corecte", "dns_records_data": "Date corecte",
"dns_records_name": "Nume", "dns_records_name": "Nume",
"dns_records_status": "Stare curentă", "dns_records_status": "Stare curentă",
@@ -641,7 +659,7 @@
"header": { "header": {
"administration": "Configurație și detalii", "administration": "Configurație și detalii",
"apps": "Aplicații", "apps": "Aplicații",
"debug": "Informații Sistem", "debug": "Informaţie",
"email": "E-Mail", "email": "E-Mail",
"mailcow_config": "Configurație", "mailcow_config": "Configurație",
"quarantine": "Carantină", "quarantine": "Carantină",
@@ -1038,7 +1056,7 @@
"apple_connection_profile_mailonly": "Acest profil de conexiune include parametrii de configurare IMAP și SMTP pentru dispozitivele Apple.", "apple_connection_profile_mailonly": "Acest profil de conexiune include parametrii de configurare IMAP și SMTP pentru dispozitivele Apple.",
"apple_connection_profile_with_app_password": "O nouă parolă pentru aplicație este generată și adăugată la profil, astfel încât să nu fie necesară introducerea unei parole la configurarea dispozitivului. Vă rugăm să nu partajați fișierul, deoarece oferă acces deplin la căsuța dvs. poștală.", "apple_connection_profile_with_app_password": "O nouă parolă pentru aplicație este generată și adăugată la profil, astfel încât să nu fie necesară introducerea unei parole la configurarea dispozitivului. Vă rugăm să nu partajați fișierul, deoarece oferă acces deplin la căsuța dvs. poștală.",
"change_password": "Schimbă parola", "change_password": "Schimbă parola",
"change_password_hint_app_passwords": "Contul dvs. are {{number_of_app_passwords}} parole de aplicație care nu vor fi modificate. Pentru a le gestiona, accesați secțiunea Parole aplicație.", "change_password_hint_app_passwords": "Contul dvs. are %d parole de aplicație care nu vor fi modificate. Pentru a le gestiona, accesați secțiunea Parole aplicație.",
"clear_recent_successful_connections": "Ștergeți conexiunile reușite văzute", "clear_recent_successful_connections": "Ștergeți conexiunile reușite văzute",
"client_configuration": "Afișează ghidurile de configurare pentru clienții de email și smartphone-uri", "client_configuration": "Afișează ghidurile de configurare pentru clienții de email și smartphone-uri",
"create_app_passwd": "Crează parola aplicației", "create_app_passwd": "Crează parola aplicației",
@@ -1187,5 +1205,9 @@
"quota_exceeded_scope": "Cota de spațiu a domeniului depășită: Numai căsuțe poștale nelimitate pot fi create pe acest domeniu.", "quota_exceeded_scope": "Cota de spațiu a domeniului depășită: Numai căsuțe poștale nelimitate pot fi create pe acest domeniu.",
"session_token": "Token formular invalid: Nepotrivire token", "session_token": "Token formular invalid: Nepotrivire token",
"session_ua": "Token formular invalid: Eroare validare utilizator-agent" "session_ua": "Token formular invalid: Eroare validare utilizator-agent"
},
"datatables": {
"expand_all": "Expandează tot",
"decimal": ","
} }
} }

View File

@@ -107,7 +107,8 @@
"validate": "Проверить", "validate": "Проверить",
"validation_success": "Проверка прошла успешно", "validation_success": "Проверка прошла успешно",
"tags": "Теги", "tags": "Теги",
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения" "app_passwd_protocols": "Разрешенные протоколы для пароля приложения",
"dry": "Имитировать синхронизацию"
}, },
"admin": { "admin": {
"access": "Настройки доступа", "access": "Настройки доступа",
@@ -338,7 +339,13 @@
"yes": "&#10003;", "yes": "&#10003;",
"queue_unban": "разблокировать", "queue_unban": "разблокировать",
"f2b_ban_time_increment": "Время бана увеличивается с каждым баном", "f2b_ban_time_increment": "Время бана увеличивается с каждым баном",
"f2b_max_ban_time": "Максимальное время блокировки" "f2b_max_ban_time": "Максимальное время блокировки",
"allowed_origins": "Access-Control-Allow-Origin",
"cors_settings": "Настройки CORS",
"allowed_methods": "Access-Control-Allow-Methods",
"ip_check": "Проверить IP",
"ip_check_disabled": "Проверка IP отключена. Вы можете включить его в разделе <br> <strong>Система > Конфигурация > Параметры > Настроить</strong>.",
"ip_check_opt_in": "Согласие на использование сторонних служб <strong>ipv4.mailcow.email</strong> и <strong>ipv6.mailcow.email</strong> для разрешения внешних IP-адресов."
}, },
"danger": { "danger": {
"access_denied": "Доступ запрещён, или указаны неверные данные", "access_denied": "Доступ запрещён, или указаны неверные данные",
@@ -457,7 +464,10 @@
"username_invalid": "Имя пользователя %s нельзя использовать", "username_invalid": "Имя пользователя %s нельзя использовать",
"validity_missing": "Пожалуйста, назначьте срок действия", "validity_missing": "Пожалуйста, назначьте срок действия",
"value_missing": "Пожалуйста заполните все поля", "value_missing": "Пожалуйста заполните все поля",
"yotp_verification_failed": "Ошибка валидации Yubico OTP: %s" "yotp_verification_failed": "Ошибка валидации Yubico OTP: %s",
"cors_invalid_method": "Указан недопустимый метод разрешения",
"demo_mode_enabled": "Демонстрационный режим включен",
"cors_invalid_origin": "Указан неверный Allow-Origin"
}, },
"debug": { "debug": {
"chart_this_server": "Диаграмма (текущий сервер)", "chart_this_server": "Диаграмма (текущий сервер)",
@@ -491,7 +501,7 @@
"dns_records": "Записи DNS", "dns_records": "Записи DNS",
"dns_records_24hours": "Обратите внимание, что для внесения изменений в DNS может потребоваться до 24 часов, чтобы правильно отобразить их текущее состояние на этой странице. Эта страница предназначен для того, чтобы вы могли легко увидеть, как настроить записи DNS и проверить, все ли записи правильно занесены в DNS.", "dns_records_24hours": "Обратите внимание, что для внесения изменений в DNS может потребоваться до 24 часов, чтобы правильно отобразить их текущее состояние на этой странице. Эта страница предназначен для того, чтобы вы могли легко увидеть, как настроить записи DNS и проверить, все ли записи правильно занесены в DNS.",
"dns_records_data": "Значение", "dns_records_data": "Значение",
"dns_records_docs": "Пожалуйста, ознакомьтесь с <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">документацией</a>.", "dns_records_docs": "Пожалуйста, ознакомьтесь с <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">документацией</a>.",
"dns_records_name": "Название", "dns_records_name": "Название",
"dns_records_status": "Статус", "dns_records_status": "Статус",
"dns_records_type": "Тип", "dns_records_type": "Тип",
@@ -542,7 +552,7 @@
"inactive": "Неактивный", "inactive": "Неактивный",
"kind": "Тип", "kind": "Тип",
"last_modified": "Последние изменения", "last_modified": "Последние изменения",
"lookup_mx": "Назначение на основе резовинга MX записи по регулярному выражению (<code>.*\\.example\\.com$</code> для маршрутизации всей почты через этот хост, если MX заканчивающийся на example.com)", "lookup_mx": "Назначение на основе резолвинга MX записи по регулярному выражению (<code>.*\\.example\\.com$</code> для маршрутизации всей почты через этот хост, если MX заканчивающийся на example.com)",
"mailbox": "Изменение почтового аккаунта", "mailbox": "Изменение почтового аккаунта",
"mailbox_quota_def": "Квота по умолчанию", "mailbox_quota_def": "Квота по умолчанию",
"mailbox_relayhost_info": "Применяется только к почтовому ящику и личным псевдонимам, вне зависимости от настроек маршрутизации на уровне домена.", "mailbox_relayhost_info": "Применяется только к почтовому ящику и личным псевдонимам, вне зависимости от настроек маршрутизации на уровне домена.",
@@ -607,7 +617,23 @@
"title": "Изменение объекта", "title": "Изменение объекта",
"unchanged_if_empty": "Если без изменений - оставьте пустым", "unchanged_if_empty": "Если без изменений - оставьте пустым",
"username": "Имя пользователя", "username": "Имя пользователя",
"validate_save": "Подтвердить и сохранить" "validate_save": "Подтвердить и сохранить",
"sogo_access_info": "Единый вход из интерфейса почты продолжает работать. Эта настройка не влияет на доступ ко всем другим службам, а также не удаляет или изменяет существующий профиль пользователя SOGo.",
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения",
"domain_footer_info": "Нижние колонтитулы на уровне домена добавляются ко всем исходящим электронным письмам, связанным с адресом в этом домене. <br> Для нижнего колонтитула можно использовать следующие переменные:",
"domain_footer_info_vars": {
"from_name": "{= from_name =} - Из названия envelope, например, для \"Mailcow &lt;moo@mailcow.tld&gt;\" возвращается \"Mailcow\"",
"auth_user": "{= auth_user =} - Аутентифицированное имя пользователя, указанное MTA",
"from_user": "{= from_user =} - Из пользовательской части envelope, например, для \"moo@mailcow.tld\" возвращается \"moo\"",
"from_addr": "{= from_addr =} - Из адресной части envelope",
"from_domain": "{= from_domain =} - из доменной части envelope",
"custom": "{= foo =} - Если почтовый ящик имеет пользовательский атрибут \"foo\" со значением \"bar\", он возвращает \"bar\"."
},
"domain_footer": "Нижний колонтитул домена",
"domain_footer_html": "HTML нижний колонтитул",
"domain_footer_plain": "ПРОСТОЙ нижний колонтитул",
"mbox_exclude": "Исключить почтовые ящики",
"custom_attributes": "Пользовательские атрибуты"
}, },
"fido2": { "fido2": {
"confirm": "Подтвердить", "confirm": "Подтвердить",
@@ -987,7 +1013,9 @@
"verified_fido2_login": "Авторизация FIDO2 пройдена", "verified_fido2_login": "Авторизация FIDO2 пройдена",
"verified_totp_login": "Авторизация TOTP пройдена", "verified_totp_login": "Авторизация TOTP пройдена",
"verified_webauthn_login": "Авторизация WebAuthn пройдена", "verified_webauthn_login": "Авторизация WebAuthn пройдена",
"verified_yotp_login": "Авторизация Yubico OTP пройдена" "verified_yotp_login": "Авторизация Yubico OTP пройдена",
"cors_headers_edited": "Настройки CORS сохранены",
"domain_footer_modified": "Изменения в нижнем колонтитуле домена %s сохранены"
}, },
"tfa": { "tfa": {
"api_register": "%s использует Yubico Cloud API. Пожалуйста, получите ключ API для вашего ключа <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">здесь</a>", "api_register": "%s использует Yubico Cloud API. Пожалуйста, получите ключ API для вашего ключа <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">здесь</a>",
@@ -1169,7 +1197,14 @@
"weekly": "Раз в неделю", "weekly": "Раз в неделю",
"weeks": "недели", "weeks": "недели",
"year": "год", "year": "год",
"years": "лет" "years": "лет",
"allowed_protocols": "Разрешенные протоколы",
"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}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".",
"attribute": "Атрибут",
"value": "Значение"
}, },
"warning": { "warning": {
"cannot_delete_self": "Вы не можете удалить сами себя", "cannot_delete_self": "Вы не можете удалить сами себя",
@@ -1183,5 +1218,8 @@
"quota_exceeded_scope": "Квота домена превышена: могут быть созданы только почтовые ящики без лимита.", "quota_exceeded_scope": "Квота домена превышена: могут быть созданы только почтовые ящики без лимита.",
"session_token": "Неверный токен формы: несоответствие токена", "session_token": "Неверный токен формы: несоответствие токена",
"session_ua": "Неверный токен формы: ошибка проверки User-Agent" "session_ua": "Неверный токен формы: ошибка проверки User-Agent"
},
"datatables": {
"infoPostFix": ""
} }
} }

View File

@@ -0,0 +1,556 @@
{
"acl": {
"app_passwds": "Upravljaj gesla aplikacij",
"bcc_maps": "Preslikave SKP (BCC)",
"delimiter_action": "Dejanje ločila",
"domain_relayhost": "Spremeni gostitelja relay za domeno",
"eas_reset": "Ponastavi EAS naprave",
"filters": "Filtri",
"login_as": "Prijavi se kot uporabnik poštnega predala",
"mailbox_relayhost": "Spremeni gostitelja relay za poštni predal",
"prohibited": "Prepovedano z ACL",
"protocol_access": "Spremeni dostop do protokola",
"pushover": "Pushover",
"quarantine": "Dejanja karantene",
"quarantine_attachments": "Priponke v karanteno",
"quarantine_notification": "Spremeni obvestila o karanteni",
"ratelimit": "Omejitev pošiljanja",
"recipient_maps": "Preslikave prejemnikov",
"smtp_ip_access": "Spremeni dovoljene gostitelje za SMTP",
"sogo_access": "Dovoli upravljanje SOGo dostopov",
"sogo_profile_reset": "Ponastavi SOGo profil",
"spam_alias": "Začasni aliasi",
"spam_policy": "Blacklist/Whitelist",
"spam_score": "Ocena neželene pošte",
"tls_policy": "Politika TLS",
"unlimited_quota": "Neomejena kvota za poštne predale",
"alias_domains": "Dodaj alias domene",
"domain_desc": "Spremeni opis domene",
"extend_sender_acl": "Dovoli razširitev pošiljateljevega ACL z zunanjimi e-poštnimi naslovi",
"quarantine_category": "Spremeni kategorijo obvestil o karanteni",
"syncjobs": "Opravila sinhronizacije"
},
"add": {
"active": "Aktivno",
"add": "Dodaj",
"add_domain_only": "Dodaj samo domeno",
"add_domain_restart": "Dodaj domeno in ponovno zaženi SOGo",
"alias_address": "Alias naslov/i",
"alias_domain": "Alias domena",
"alias_domain_info": "<small>Samo veljavne domene (ločene z vejico).</small>",
"app_name": "Ime aplikacije",
"app_password": "Dodaj geslo aplikacije",
"app_passwd_protocols": "Dovoljeni protokoli za geslo aplikacije",
"automap": "Poskusi samodejno preslikati mape (\"Sent items\", \"Sent\" => \"Poslano\" ipd.)",
"backup_mx_options": "Možnosti posredovanja (relay)",
"comment_info": "Zasebni komentarji niso vidni uporabnikom, javni komentarji pa so prikazani kot tooltip, ko se z miško postavimo nad uporabnika v pregledu",
"custom_params": "Parametri po meri",
"custom_params_hint": "Pravilno: --param=xy, napačno: --param xy",
"delete1": "Izbriši na viru, ko je končano",
"delete2": "Izbriši sporočila na cilju, ki niso na viru",
"delete2duplicates": "Izbriši dvojnike na cilju",
"description": "Opis",
"destination": "Cilj",
"domain": "Domena",
"domain_matches_hostname": "Domena %s se ujema z nazivom gostitelja (hostname)",
"domain_quota_m": "Kvota za celotno domeno (MiB)",
"enc_method": "Metoda kriptiranja",
"exclude": "Izključi objekte (regex)",
"full_name": "Polno ime",
"gal": "Globalni seznam stikov (GAL)",
"generate": "generiraj",
"goto_ham": "Prepoznaj kot <span class=\"text-success\"><b>ham</b></span>",
"goto_null": "Odstrani e-poštno sporočilo brez obvestila",
"goto_spam": "Prepoznaj kot <span class=\"text-danger\"><b>spam</b></span>",
"hostname": "Gostitelj",
"inactive": "Neaktivno",
"kind": "Tip",
"mailbox_quota_def": "Privzeta kvota za poštni predal",
"mailbox_username": "Uporabniško ime (levi del e-poštnega naslova)",
"max_aliases": "Največje število dovoljenih aliasov",
"max_mailboxes": "Največje dovoljeno število poštnih predalov",
"mins_interval": "Interval preverjanja (minute)",
"multiple_bookings": "Več rezervacij",
"nexthop": "Naslednji korak",
"password_repeat": "Potrditev gesla (ponovi)",
"port": "Vrata (port)",
"private_comment": "Zasebni komentar",
"public_comment": "Javni komentar",
"quota_mb": "Kvota (MiB)",
"relay_all": "Posreduj vse prejemnike (relay)",
"relay_all_info": "↪ Če izberete da <b>ne</b> posredujete vse prejemnike, morate ustvariti (\"slepi\") poštni predal za vsakega prejemnika, za katerega želite posredovati e-pošto.",
"relay_domain": "Posreduj to domeno (relay)",
"relay_unknown_only": "Posreduj samo neobstoječe poštne predale. V obstoječe poštne predale bo e-pošta dostavljena lokalno.",
"relayhost_wrapped_tls_info": "Prosim <b>ne</b> uporabljajte TLS-wrapped vrata (večinoma uporabljeno na vratih 465).<br>\nUporabite katera koli non-wrapped vrata in ustvarite STARTTLS. TLS politika za obvezno uporabo TLS se lahko ustvari pod \"Preslikave TLS politik\"",
"select": "Prosim izberite...",
"select_domain": "Prosim najprej izberite domeno",
"sieve_desc": "Kratek opis",
"sieve_type": "Vrsta filtra",
"skipcrossduplicates": "Preskoči podvojena sporočila po mapah (prvi pride, prvi melje)",
"subscribeall": "Prijavi vse mape",
"syncjob": "Dodaj opravilo sinhronizacije",
"tags": "Oznake",
"target_address": "Goto naslov",
"target_address_info": "<small>Polni e-poštni naslov/i (ločeni z vejico).</small>",
"target_domain": "Ciljna domena",
"timeout1": "Časovna omejitev za povezavo do oddaljenega gostitelja",
"username": "Uporabniško ime",
"validate": "Preveri",
"validation_success": "Uspešno preverjeno",
"activate_filter_warn": "Ko je aktivni izbran, bodo vsi ostali filtri deaktivirani.",
"alias_address_info": "<small>Polni email naslov/i oziroma @example.com za zajem vseh sporočil domene (ločeno z vejico), <b>samo domene mailcow</b>.</small>",
"bcc_dest_format": "BCC naslov mora biti en veljaven e-poštni naslov.<br>Če morate poslati kopijo na več naslov, ustvarite alias in ga uporabite tukaj.",
"disable_login": "Prepovej vpis (vhodna e-pošta je še vedno sprejeta)",
"gal_info": "GAL vsebuje vse objekte domene in ga uporabniki ne morejo urejati. Informacija o zasedenosti v SOGo ni na voljo, če je onemogočena! <b>Ponovno zaženi SOGo za uveljavitev sprememb.</b>",
"mailbox_quota_m": "Najvišja kvota na poštni predal (MiB)",
"password": "Geslo",
"post_domain_add": "SOGo container \"sogo-mailcow\" mora biti ponovno zagnan po dodajanju nove domene!<br><br>Dodatno se mora preveriti DNS konfiguracija domene. Ko je DNS konfiguracija domene odobrena, ponovno zaženite \"acme-mailcow\" za samodejno generiranje certifikatov za novo domeno (autoconfig.&lt;domain&gt;, autodiscover.&lt;domain&gt;).<br>Ta korak je opcijski in se ponovno poskuša vsakih 24 ur.",
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Definirate lahko preslikave transportov za cilj po meri za to domeno. Če ni nastavljena, se ustvari MX poizvedba.",
"syncjob_hint": "Pozor! Gesla se morajo shraniti v plain-text!",
"timeout2": "Časovna omejitev za povezavo do lokalnega gostitelja"
},
"admin": {
"access": "Dostop",
"action": "Dejanje",
"activate_api": "Aktiviraj API",
"activate_send": "Aktiviraj gumb \"Pošlji\"",
"active": "Aktivno",
"active_rspamd_settings_map": "Aktivna preslikava nastavitev",
"add": "Dodaj",
"add_domain_admin": "Dodaj skrbnika domene",
"add_forwarding_host": "Dodaj gostitelja za posredovanje",
"add_relayhost": "Dodaj transport odvisen od pošiljatelja",
"add_row": "Dodaj vrstico",
"add_settings_rule": "Dodaj pravilo nastavitev",
"add_transport": "Dodaj transport",
"add_transports_hint": "Prosimo zavedajte se, da se podatki za avtentikacijo, če obstajajo, shranijo v plain text.",
"additional_rows": " nove vrstice so bile dodane",
"admin_details": "Uredi podrobnosti skrbnika",
"admin_domains": "Dodeljene domene",
"admins": "Skrbniki",
"admins_ldap": "LDAP skrbniki",
"advanced_settings": "Napredne nastavitve",
"api_info": "API je v razvoju. Dokumentacija je na voljo na naslovu <a href=\"/api\">/api</a>",
"api_key": "API ključ",
"api_read_only": "Dostop samo za branje",
"api_read_write": "Dostop za branje in urejanje",
"api_skip_ip_check": "Preskoči preverjanje IP za API",
"app_links": "Povezave aplikacij",
"app_name": "Ime aplikacije",
"arrival_time": "Čas prispetja (strežniški čas)",
"authed_user": "Prij. uporabnik",
"ays": "Ste prepričani, da želite nadaljevati?",
"change_logo": "Zamenjaj logotip",
"configuration": "Konfiguracija",
"convert_html_to_text": "Pretvori HTML v golo besedilo",
"credentials_transport_warning": "<b>Opozorilo</b>: Dodajanje nove preslikave transporta bo posodobilo poverilnice za vse vnose, ki imajo enako vrednost v stolpcu naslednji skok.",
"customer_id": "ID stranke",
"customize": "Prilagodi",
"destination": "Cilj",
"dkim_add_key": "Dodaj ARC/DKIM ključ",
"dkim_domains_selector": "Izbira",
"dkim_domains_wo_keys": "Izberi domene z manjkajočimi ključi",
"dkim_from": "Od",
"dkim_from_title": "Izvorna domena od katere prekopiram podatke",
"dkim_key_missing": "Manjka ključ",
"dkim_key_unused": "Ključ ni v rabi",
"dkim_key_valid": "Veljaven ključ",
"dkim_keys": "ARC/DKIM ključi",
"dkim_overwrite_key": "Prepiši obstoječi DKIM ključ",
"dkim_private_key": "Zasebni ključ",
"dkim_to": "Za",
"domain": "Domena",
"domain_admin": "Skrbnik domene",
"domain_admins": "Skrbniki domene",
"domain_s": "Domena/e",
"duplicate": "Podvoji",
"duplicate_dkim": "Podvoji DKIM zapis",
"edit": "Uredi",
"empty": "Ni rezultatov",
"excludes": "Izključuje te prejemnike",
"f2b_ban_time": "Čas blokade (s)",
"f2b_ban_time_increment": "Čas blokade se poveča z vsako blokado",
"f2b_blacklist": "Mreže/gostitelji na blacklisti",
"f2b_filter": "Regex filtri",
"f2b_max_attempts": "Največ poskusov",
"f2b_max_ban_time": "Maksimalno trajanje blokade (s)",
"f2b_netban_ipv4": "velikost subneta IPv4 za blokiranje (8-32)",
"f2b_netban_ipv6": "Velikost subneta IPv6 za blokiranje (8-128)",
"f2b_parameters": "Fail2ban parametri",
"f2b_regex_info": "Upoštevajo se dnevniki SOGo, Postfix, Dovecot, PHP-FPM.",
"f2b_retry_window": "Upoštevan čas (s) za največ poskusov",
"f2b_whitelist": "Mreže/gostitelji na whitelisti",
"filter_table": "Filtriraj tabelo",
"from": "Od",
"generate": "ustvari",
"guid": "GUID - enolični ID instance",
"guid_and_license": "GUID & licenca",
"hash_remove_info": "Odstranitev hasha za omejitev (če obstaja) bo povsem ponastavilo njen števec.<br>\n Vsak hash je prikazan z individualno barvo.",
"help_text": "Zamenjaj tekst za pomoč pod masko za prijavo (HTML je dovoljen)",
"host": "Gostitelj",
"html": "HTML",
"import": "Uvozi",
"import_private_key": "Uvozi zasebni ključ",
"in_use_by": "V uporabi",
"inactive": "Neaktivno",
"include_exclude": "Vključi/Izključi",
"include_exclude_info": "Privzeto - če ni izbire - so vključeni <b>vsi poštni predali</b>",
"includes": "Vključi te prejemnike",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je onemogočena. Lahko jo omogočite pod <br/> <strong>Sistem > Konfiguracija > Možnosti > Prilagodi</strong>",
"ip_check_opt_in": "Opt-in za uporabo zunanje storitve <strong>ipv4.mailcow.email</strong> in <strong>ipv6.mailcow.email</strong> za razreševanje zunanjih IP.",
"is_mx_based": "Glede na MX",
"last_applied": "Nazadnje aplicirano",
"link": "Povezava",
"loading": "Prosim počakajte...",
"login_time": "Čas prijave",
"logo_info": "Vaša slika bo pomanjšana na velikost 40px za zgornjo navigacijo in največjo velikost 250px za začetno stran. Zelo priporočena je uporaba grafike brez izgube kakovosti ob spremembi velikosti.",
"message": "Sporočilo",
"message_size": "Velikost sporočila",
"nexthop": "Naslednji skok",
"no": "&#10005;",
"no_active_bans": "Ni aktivnih blokad",
"no_new_rows": "Ni dodatnih vrstic",
"no_record": "Ni zapisa",
"oauth2_apps": "OAuth2 aplikacije",
"oauth2_add_client": "Dodaj OAuth2 klienta",
"oauth2_client_id": "ID klienta",
"oauth2_client_secret": "Skrivnost (secret)",
"oauth2_redirect_uri": "URI za preusmeritev",
"oauth2_renew_secret": "Generiraj nov client secret",
"oauth2_revoke_tokens": "Zavrni vse tokene klientov",
"optional": "opcijsko",
"options": "Možnosti",
"password": "Geslo",
"password_length": "Dolžina gesla",
"password_policy": "Politika gesel",
"password_policy_chars": "Mora vsebovati vsaj eno črko",
"password_policy_length": "Minimalna dolžina gesla je %d",
"password_policy_lowerupper": "Mora vsebovati male in velike črke",
"password_policy_numbers": "Mora vsebovati vsaj eno številko",
"password_policy_special_chars": "Mora vsebovati posebne znake",
"password_repeat": "Potrditev gesla (ponovite)",
"priority": "Prioriteta",
"private_key": "Zasebni ključ",
"quarantine": "Karantena",
"quarantine_bcc": "Pošlji kopijo vseh obvestil (BCC) temu prejemniku:<br><small>Pustite prazno za izklop te funkcije. <b>Nepodpisana, nepreverjena pošta. Uporabljalo naj bi se samo za interno dostavo.</b></small>",
"quarantine_exclude_domains": "Izključi domene in alias-domene",
"quarantine_max_age": "Maksimalna starost v dnevnih<br><small>Vrednost mora biti večja ali enaka 1 dnevu</small>",
"quarantine_max_score": "Opusti obvestilo, če je ocena spama večja od te vrednosti:<br><small>Privzeto 9999.0</small>",
"quarantine_max_size": "Največja velikost v MiB (Večji elementi so zavrženi):<br><small>0 <b>ne</b> pomeni neomejeno.</small>",
"quarantine_notification_html": "Predloga sporočila za obvestilo:<br><small>Pustite prazno za obnovitev privzete predloge.</small>",
"quarantine_notification_sender": "Pošiljatelj obvestila",
"quarantine_notification_subject": "Naslov obvestila",
"quarantine_release_format": "Oblika sproščenih elementov",
"quarantine_release_format_att": "Kot priponka",
"quarantine_release_format_raw": "Nespremenjen original",
"quarantine_retention_size": "Število zadržanj na poštni predal: <br><small>0 pomeni <b>neaktivno</b>,</small>",
"quota_notification_sender": "Pošiljatelj obvestila",
"quota_notification_subject": "Predmet obvestila",
"quota_notifications": "Obvestila o omejitvi",
"quota_notifications_info": "Obvestila o omejitvi so poslana uporabnikom enkrat, ko presežejo 80% in enkrat ko presežejo 95% zasedenosti.",
"queue_unban": "odblokiraj",
"r_active": "Aktivne omejitve",
"r_inactive": "Neaktivne omejitve",
"rate_name": "Ime omejitve",
"recipients": "Prejemniki",
"refresh": "Osveži",
"regen_api_key": "Ponovno generiraj API ključ",
"regex_maps": "Regex preslikave",
"relay_from": "\"Od:\" naslov",
"relay_rcpt": "\"Za:\" naslov",
"relay_run": "Izvedi test",
"relayhosts": "Transporti glede na pošiljatelja",
"remove": "Odstrani",
"remove_row": "Odstrani vrstico",
"reset_default": "Ponastavi na privzeto",
"reset_limit": "Odstrani hash",
"routing": "Routing",
"rsetting_add_rule": "Dodaj pravilo",
"rsetting_content": "Vsebina pravila",
"rsetting_desc": "Kratek opis",
"rsetting_no_selection": "Prosim izberite pravilo",
"rsetting_none": "Ni pravil na voljo",
"rsettings_insert_preset": "Vstavi prednastavljen primer \"%s\"",
"rsettings_preset_1": "Onemogoči vse razen DKIM in omejitve za prijavljene uporabnike",
"rsettings_preset_2": "Postmasterji želijo spam",
"rsettings_preset_3": "Dovoli samo specifične pošiljatelje za poštni predal (npr. uporaba samo kot interni poštni predal)",
"rsettings_preset_4": "Onemogoči Rspamd za domeno",
"rspamd_com_settings": "Ime nastavitve bo samodejno generirano. Prosim oglejte si primere nastavitev spodaj. Za več informacij si oglejte <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">dokumentacijo Rspamd</a>",
"rspamd_global_filters": "Globalne preslikave filtrov",
"rspamd_global_filters_agree": "Previden bom!",
"rspamd_global_filters_info": "Globalne preslikave filtrov vsebujejo različne vrste globalnih blacklist in whitelist.",
"add_admin": "Dodaj skrbnika",
"add_relayhost_hint": "Prosimo zavedajte se, da se podatki za avtentikacijo, če obstajajo, shranijo v plain text.",
"admin": "Skrbnik",
"api_allow_from": "Dovoli API dostop s teh IP naslovov / CIDR mrežnih zapisov",
"apps_name": "Ime aplikacije v mailcow",
"ban_list_info": "Oglejte si seznam blokiranih IP naslovov spodaj: <b>network (remaining ban time) - [actions]</b>.<br />. IPji v vrsti za odstranitev blokade bodo odstranjeni iz aktivnega seznama blokad v nekaj sekundah.<br />Rdeče oznake prikazujejo trajne blokade z blacklisto.",
"dkim_key_length": "Dolžina DKIM ključa (v bitih)",
"dkim_to_title": "Ciljne domene bodo prepisane",
"f2b_list_info": "Gostitelj ali omrežje na blacklisti bo vedno prevladal zapis na whitelisti. <b>Apliciranje sprememb seznama traja nekaj sekund.</b>",
"forwarding_hosts": "Gostitelji za posredovanje",
"forwarding_hosts_add_hint": "Lahko vpišete IPv4/IPv6 naslove, mreže v CIDR obliki, imena gostiteljev (kateri se prevedejo v IP naslove) ali imena domen (katera se prevedejo v IP naslove glede na poizvedbo po SPF zapisih, v primeru manjkajočih zapisov pa MX zapisih).",
"forwarding_hosts_hint": "Dohodna sporočila so brezpogojno sprejeta od katerih koli gostiteljev v tem seznamu. Ti gostitelji se ne bodo preverjali po DNSBL seznamih in ne bodo dodani v greyliste. Prejeti spam s teh gostiteljev ni nikoli zavrnjen, opcijsko pa se lahko premakne v mapo neželene pošte. Najpogostejša uporaba za to je navedba poštnih strežnikov, iz katerih ste nastavili pravilo za posredovanje pošte na vaš mailcow strežnik.",
"license_info": "Licenca ni zahtevana, a pomaga pri nadaljnjem razvoju. <br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"Naročilo SAL\">Registrirajte svoj GUID tukaj</a> ali <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Naročilo podpora\">Kupite podporo za svojo namestitev Mailcow.</a>",
"lookup_mx": "Cilj je regular expression za ujemanje MX zapisov (<code>.*\\.google\\.com</code> za usmeritev vse pošte na MX, ki se konča z google.com, preko tega skoka)",
"main_name": "Naziv \"mailcow UI\"",
"merged_vars_hint": "Sive vrstice so združene iz <code>vars.(local.)inc.php</code> in jih ni mogoče spremeniti.",
"oauth2_info": "OAuth2 implementacija omogoča grant vrste \"Authorization code\" in izdaja refresh tokene.<br>\nStrežnik prav tako izda nove refresh tokene, ko je bil refresh token uporabljen<br><br>\n&#8226; Privzeti obseg je <i>profile</i>. Samo uporabniki poštnih predalov se lahko prijavijo s pomočjo OAuth2. Če parameter obsega ni vnesen, se nastavi na <i>profile</i>.<br>\n&#8226; Parameter <i>state</i> mora biti poslan s strani klienta kot del zahtevka za avtorizacijo .<br><br>\nPoti za OAuth2 API: <br>\n<ul>\n <li>Endpoint za avtorizacijo: <code>/oauth/authorize</code></li>\n <li>Endpoint za tokene: <code>/oauth/token</code></li>\n <li>Stran vira: <code>/oauth/profile</code></li>\n</ul>\nPonovno generiranje client secret ne bo razveljavilo obstoječih avtorizacijskih kod, ne bodo pa mogle obnoviti svoje tokene.<br><br>\nZavrnitev client tokenov bo povzročilo tekojčno prekinitev aktivnih sej. Vsi klienti se bodo morali ponovno prijaviti.",
"quarantine_redirect": "<b>Preusmeri vsa obvestila</b> k temu prejemniku:<br><small>Pustite prazno, da onemogočite. <b>Nepodpisana, nepreverjena pošta. Uporabljalo bi se naj samo za interno dostavo.</b></small>",
"quota_notification_html": "Predloga sporočila za obvestilo:<br><small>Pustite prazno za obnovitev privzete predloge.</small>",
"quota_notifications_vars": "{{percent}} pomeni trenutna omejitev uporabnika<br>{{username}} je ime poštnega predala",
"r_info": "Sivi/onemogočeni elementi v seznamu aktivnih omejitev niso znane kot veljavne omejitve za mailcow in ne morejo biti premaknjene. Neznane omejitve bodo kljub temu nastavljene po vrstnem redu pojavitve. <br>Nove elemente lahko dodate v <code>inc/vars.local.inc.php</code> da jih lahko vklopite ali izklopite.",
"relayhosts_hint": "Določite transporte glede na pošiljatelja, da jih lahko izberete v konfiguraciji domene.<br>\nTransportni servis je vedno \"smtp:\" in bo poskušal s TLS ko bo na voljo. Wrapped TLS (SMTPS) ni podprto. Upošteva se uporabnikova politika odhodnega TLS.<br>\nVpliva na izbrane domene vključno z alias domenami.",
"transport_dest_format": "Regex ali sintaksa: example.org, .example.org, *, box@example.org (več vrednosti ločite z vejico)",
"transport_test_rcpt_info": "&#8226; Uporabite null@hosted.mailcow.de za testiranje relaya na drugo destinacijo.",
"rspamd_global_filters_regex": "Njihovi nazivi pojasnijo njihov namen. Vsa vsebina mora imeti veljaven regular expression v obliki \"/pattern/options\" (npr. <code>/.+@domain\\.tld/i</code>).<br>\nČeprav se v vsaki vrstici regexa izvedejo osnovni pregledi, je lahko funkcionalnost programa Rspamd motena, če sintaksa ni pravilna.<br>\nRspamd bo poskušal prebrati vsebino preslikave, ko bo spremenjena. Če imate težave, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">ponovno zaženite Rspamd</a>, da prisilite ponovno nalaganje preslikav.<br> Elementi z Blackliste so izključeni iz karantene.",
"rspamd_settings_map": "Preslikava nastavitev Rspamd",
"sal_level": "Moo stopnja",
"save": "Shrani spremembe",
"search_domain_da": "Išči domene",
"send": "Pošlji",
"sender": "Pošiljatelj",
"service": "Servis",
"service_id": "ID servisa",
"source": "Vir",
"spamfilter": "Spam filter",
"subject": "Predmet",
"success": "Uspešno",
"sys_mails": "Sistemska pošta",
"text": "Besedilo",
"time": "Čas",
"title": "Naziv",
"title_name": "Naziv spletnega mesta \"mailcow UI\"",
"to_top": "Nazaj na vrh",
"transport_maps": "Preslikave transportov",
"transports_hint": "&#8226; Vpis preslikave transporta <b>nadredi</b> preslikavo transporta odvisno od pošiljatelja.<br>\n&#8226; Preferenčno se uporabljajo transporti glede na MX zapise.<br>\n&#8226; Izhodne TLS politike na uporabnika so ignorirane in se lahko vsilijo samo s preslikavami TLS politik.<br>\n&#8226; Transportni servis za definirane transporte je vedno \"smtp:\" in bo posledično poskušal TLS ko bo ponujeno. Wrapped TLS (SMTPS) ni podprto.<br>\n&#8226; Naslovi, ki se ujemajo z \"/localhost$/\" bodo vedno preneseni preko \"local:\", in zato destinacija \"*\" ne bo vplivala na te naslove.<br>\n&#8226; Za določitev poverilnic za naslednji skok (npr. \"[host]:25\"), Postfix <b>vedno</b> preveri \"host\" preden išče \"[host]:25\". Zaradi takšnega obnašanja je nemogoče hkrati uporabiti \"host\" in \"[host]:25\".",
"ui_footer": "Noga (HTML dovoljen)",
"ui_header_announcement": "Obvestila",
"ui_header_announcement_active": "Nastavi obvestilo kot aktivno",
"ui_header_announcement_content": "Besedilo (HTML dovoljen)",
"ui_header_announcement_help": "Obvestilo je vidno za vse prijavljene uporabnike in na vmesniku za prijavo.",
"ui_header_announcement_select": "Izberite vrsto obvestila",
"ui_header_announcement_type": "Vrsta",
"ui_header_announcement_type_danger": "Zelo pomembno",
"ui_header_announcement_type_info": "Info",
"ui_header_announcement_type_warning": "Pomembno",
"ui_texts": "Oznake in besedila UI",
"unban_pending": "unban v postopku",
"unchanged_if_empty": "Če je nespremenjeno, pustite prazno",
"upload": "Naloži",
"username": "Uporabniško ime",
"validate_license_now": "Potrdi GUID z licenčnim strežnikom",
"verify": "Preveri",
"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",
"aliasd_targetd_identical": "Alias domena ne sme biti enaka ciljni domeni: %s",
"bcc_exists": "BCC preslikava obstaja za vrsto %s",
"dkim_domain_or_sel_exists": "DKIM ključ za \"%s\" obstaja in ne bo prepisan.",
"domain_quota_m_in_use": "Kvota domene mora biti večja ali enaka %s MiB",
"extra_acl_invalid_domain": "Zunanji pošiljatelj \"%s\" uporablja neveljavno domeno",
"global_map_write_error": "Ni mogoče zapisati ID globalne preslikave %s: %s",
"img_tmp_missing": "Ni mogoče preveriti slikovne datoteke: začasne datoteke ni mogoče najti",
"invalid_nexthop": "Oblika naslednjega skoka ni veljavna",
"invalid_nexthop_authenticated": "Naslednji skok obstaja z drugačnimi poverilnicami. Prosim najprej posodobite obstoječe poverilnice za ta naslednji skok.",
"demo_mode_enabled": "Demo način je omogočen",
"access_denied": "Dostop zavrnjen ali pa so podatki obrazca napačni",
"alias_domain_invalid": "Alias domena %s ni veljavna",
"alias_empty": "Alias naslov ne sme biti prazen",
"alias_invalid": "Alias naslov %s ni veljaven",
"aliases_in_use": "Max. aliasov mora biti večje ali enako %d",
"app_name_empty": "Naziv aplikacije ne more biti prazno",
"app_passwd_id_invalid": "ID gesla aplikacije %s je neveljaven",
"bcc_empty": "BCC cilj ne more biti prazen",
"bcc_must_be_email": "BCC cilj %s ni veljaven e-poštni naslov",
"comment_too_long": "Komentar je predolg, dovoljeno je največ 100 znakov.",
"defquota_empty": "Privzeta kvota na poštni predal ne more biti 0",
"description_invalid": "Opis resursa za %s ni veljaven",
"dkim_domain_or_sel_invalid": "Domena ali izbirnik DKIM ni veljaven: %s",
"domain_cannot_match_hostname": "Domena se ne more ujemati z imenom gostitelja",
"domain_exists": "Domena %s že obstaja",
"domain_invalid": "Manjka ali napačno ime domene",
"domain_not_empty": "Ne morem odstraniti ne-prazno domeno %s",
"domain_not_found": "Domene %s ni bilo mogoče najti",
"extended_sender_acl_denied": "manjka ACL za določitev naslovov zunanjih pošiljateljev",
"extra_acl_invalid": "Naslov zunanjega pošiljatelja \"%s\" ni veljaven",
"fido2_verification_failed": "Preverjanje FIDO2 ni uspelo: %s",
"file_open_error": "Datoteka ne more biti odprta za urejanje",
"filter_type": "Napačna vrsta filtra",
"from_invalid": "Pošiljatelj ne sme biti prazno",
"global_filter_write_error": "Ni mogoče zapisati datoteke filtra: %s",
"global_map_invalid": "ID globalne preslikave %s ni veljaven",
"goto_empty": "Alias naslov mora vsebovati vsaj en veljaven goto naslov",
"goto_invalid": "Goto naslov %s ni veljaven",
"ham_learn_error": "Napaka pri učenju Ham: %s",
"imagick_exception": "Napaka: Imagick napaka pri branju slike",
"img_invalid": "Ni možno preveriti slikovne datoteke",
"invalid_bcc_map_type": "Neveljavna vrsta preslikave BCC",
"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",
"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,6 +41,7 @@
"alias_domain": "Alias doména", "alias_domain": "Alias doména",
"alias_domain_info": "<small>Len platné mená domén (oddelené čiarkou).</small>", "alias_domain_info": "<small>Len platné mená domén (oddelené čiarkou).</small>",
"app_name": "Meno aplikácie", "app_name": "Meno aplikácie",
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
"app_password": "Pridať heslo aplikácie", "app_password": "Pridať heslo aplikácie",
"automap": "Skúsiť automaticky mapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)", "automap": "Skúsiť automaticky mapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti preposielania", "backup_mx_options": "Možnosti preposielania",
@@ -106,8 +107,8 @@
"username": "Používateľské meno", "username": "Používateľské meno",
"validate": "Overiť", "validate": "Overiť",
"validation_success": "Úspešne overené", "validation_success": "Úspešne overené",
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie", "tags": "Štítky",
"tags": "Štítky" "dry": "Simulovať synchronizáciu"
}, },
"admin": { "admin": {
"access": "Prístup", "access": "Prístup",
@@ -147,6 +148,8 @@
"ays": "Naozaj chcete pokračovať?", "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.", "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", "change_logo": "Zmeniť logo",
"logo_normal_label": "Normálne",
"logo_dark_label": "Inverzné pre tmavý režim",
"configuration": "Konfigurácia", "configuration": "Konfigurácia",
"convert_html_to_text": "Konvertovať HTML do obyčajného textu", "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.", "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.",
@@ -206,6 +209,9 @@
"include_exclude": "Zahrnúť/Vylúčiť", "include_exclude": "Zahrnúť/Vylúčiť",
"include_exclude_info": "Ak nič nevyberiete tak bude adresované <b>všetkým schránkam</b>", "include_exclude_info": "Ak nič nevyberiete tak bude adresované <b>všetkým schránkam</b>",
"includes": "Zahrnúť týchto príjemcov", "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", "is_mx_based": "Na základe MX",
"last_applied": "Naposledy aplikované", "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>", "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>",
@@ -232,6 +238,7 @@
"oauth2_renew_secret": "Vygenerovať nový tajný kľuč", "oauth2_renew_secret": "Vygenerovať nový tajný kľuč",
"oauth2_revoke_tokens": "Odobrať všetky tokeny klienta", "oauth2_revoke_tokens": "Odobrať všetky tokeny klienta",
"optional": "voliteľné", "optional": "voliteľné",
"options": "Možnosti",
"password": "Heslo", "password": "Heslo",
"password_length": "Dĺžka hesla", "password_length": "Dĺžka hesla",
"password_policy": "Politika hesiel", "password_policy": "Politika hesiel",
@@ -439,6 +446,9 @@
"target_domain_invalid": "Cieľová doména %s je neplatná", "target_domain_invalid": "Cieľová doména %s je neplatná",
"targetd_not_found": "Cieľová doména %s sa nenašla", "targetd_not_found": "Cieľová doména %s sa nenašla",
"targetd_relay_domain": "Cieľová doména %s je posielaná ďalej (relay)", "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", "temp_error": "Dočasná chyba",
"text_empty": "Text nemôže byť prázdny", "text_empty": "Text nemôže byť prázdny",
"tfa_token_invalid": "Neplatný TFA token", "tfa_token_invalid": "Neplatný TFA token",
@@ -456,6 +466,31 @@
"value_missing": "Prosím poskytnite všetky hodnoty", "value_missing": "Prosím poskytnite všetky hodnoty",
"yotp_verification_failed": "Overenie cez OTP Yubico zlyhalo: %s" "yotp_verification_failed": "Overenie cez OTP Yubico zlyhalo: %s"
}, },
"datatables": {
"info": "Záznamy _START_ až _END_ z celkom _TOTAL_",
"infoEmpty": "Záznamy 0 až 0 z celkom 0 ",
"infoFiltered": "(vyfiltrované spomedzi _MAX_ záznamov)",
"lengthMenu": "Zobraz _MENU_ záznamov",
"loadingRecords": "Načítavam...",
"processing": "Spracúvam...",
"search": "Hľadať:",
"zeroRecords": "Nenašli sa žiadne vyhovujúce záznamy",
"paginate": {
"first": "Prvá",
"last": "Posledná",
"next": "Nasledujúca",
"previous": "Predchádzajúca"
},
"aria": {
"sortAscending": ": aktivujte na zoradenie stĺpca vzostupne",
"sortDescending": ": aktivujte na zoradenie stĺpca zostupne"
},
"emptyTable": "Nie sú k dispozícii žiadne dáta.",
"decimal": ",",
"thousands": " ",
"collapse_all": "Zbaliť všetko",
"expand_all": "Rozbaliť všetko"
},
"debug": { "debug": {
"chart_this_server": "Graf (tento server)", "chart_this_server": "Graf (tento server)",
"containers_info": "Informácie o kontajneroch", "containers_info": "Informácie o kontajneroch",
@@ -488,7 +523,7 @@
"dns_records": "DNS záznamy", "dns_records": "DNS záznamy",
"dns_records_24hours": "Berte prosím do úvahy, že zmeny v DNS môžu trvať až 24 hodín, aby sa zmeny prejavili na tejto stránke. Pre jednoduchosť DNS konfigurácie môžete použiť údaje uvedené nižšie, prípadne skontrolovať tak správnosť záznamov v DNS.", "dns_records_24hours": "Berte prosím do úvahy, že zmeny v DNS môžu trvať až 24 hodín, aby sa zmeny prejavili na tejto stránke. Pre jednoduchosť DNS konfigurácie môžete použiť údaje uvedené nižšie, prípadne skontrolovať tak správnosť záznamov v DNS.",
"dns_records_data": "Správne dáta", "dns_records_data": "Správne dáta",
"dns_records_docs": "Pozrite si prosím <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">dokumentáciu</a>.", "dns_records_docs": "Pozrite si prosím <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentáciu</a>.",
"dns_records_name": "Meno", "dns_records_name": "Meno",
"dns_records_status": "Súčasný stav", "dns_records_status": "Súčasný stav",
"dns_records_type": "Typ", "dns_records_type": "Typ",
@@ -505,6 +540,7 @@
"allowed_protocols": "Povolené protokoly", "allowed_protocols": "Povolené protokoly",
"app_name": "Meno aplikácie", "app_name": "Meno aplikácie",
"app_passwd": "Heslo aplikácie", "app_passwd": "Heslo aplikácie",
"app_passwd_protocols": "Povolené protokoly",
"automap": "Skúsiť automapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)", "automap": "Skúsiť automapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
"backup_mx_options": "Možnosti preposielania", "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.", "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.",
@@ -589,6 +625,8 @@
"sieve_desc": "Krátky popis", "sieve_desc": "Krátky popis",
"sieve_type": "Typ filtru", "sieve_type": "Typ filtru",
"skipcrossduplicates": "Preskočiť duplikované správy naprieč priečinkami (akceptuje sa prvý nález)", "skipcrossduplicates": "Preskočiť duplikované správy naprieč priečinkami (akceptuje sa prvý nález)",
"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": "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.", "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", "spam_alias": "Vytvoriť alebo zmeniť časovo limitované alias adresy",
@@ -605,9 +643,17 @@
"unchanged_if_empty": "Ak nemeníte, nechajte prázdne", "unchanged_if_empty": "Ak nemeníte, nechajte prázdne",
"username": "Používateľské meno", "username": "Používateľské meno",
"validate_save": "Validovať a uložiť", "validate_save": "Validovať a uložiť",
"sogo_access": "Prideliť priame prihlásenie do SOGo", "domain_footer_info_vars": {
"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.", "from_addr": "{= from_addr =} - E-mailová adresa odosielateľa",
"app_passwd_protocols": "Povolené protokoly" "from_domain": "{= from_domain =} - Doména odosielateľa",
"auth_user": "{= auth_user =} - Prihlasovacie meno odosielateľa",
"from_user": "{= from_user =} - Používateľská časť e-mailovej adresy odosielateľa, napr. pre \"moo@mailcow.tld\" vráti \"moo\"",
"from_name": "{= from_name =} - Meno odosielateľa, napr. pre \"Mailcow &lt;moo@mailcow.tld&gt;\" vráti \"Mailcow\""
},
"domain_footer": "Pätička pre celú doménu",
"domain_footer_html": "HTML text",
"domain_footer_info": "Pätička pre celú doménu sa pridáva do všetkých odchádzajúcich e-mailov spojených s adresou v rámci tejto domény. <br> Pre pätičku je možné použiť nasledujúce premenné:",
"domain_footer_plain": "Obyčajný text"
}, },
"fido2": { "fido2": {
"confirm": "Potvrdiť", "confirm": "Potvrdiť",
@@ -644,6 +690,7 @@
"apps": "Aplikácie", "apps": "Aplikácie",
"debug": "Systémové informácie", "debug": "Systémové informácie",
"email": "E-Mail", "email": "E-Mail",
"mailcow_system": "Systém",
"mailcow_config": "Konfigurácia", "mailcow_config": "Konfigurácia",
"quarantine": "Karanténa", "quarantine": "Karanténa",
"restart_netfilter": "Reštartovať netfilter", "restart_netfilter": "Reštartovať netfilter",
@@ -679,6 +726,7 @@
"add_mailbox": "Pridať mailovú schránku", "add_mailbox": "Pridať mailovú schránku",
"add_recipient_map_entry": "Pridať mapu príjemcu", "add_recipient_map_entry": "Pridať mapu príjemcu",
"add_resource": "Pridať zdroj", "add_resource": "Pridať zdroj",
"add_template": "Pridať šablónu",
"add_tls_policy_map": "Pridať TLS mapu pravidiel", "add_tls_policy_map": "Pridať TLS mapu pravidiel",
"address_rewriting": "Prepisovanie adries", "address_rewriting": "Prepisovanie adries",
"alias": "Alias", "alias": "Alias",
@@ -721,6 +769,7 @@
"domain": "Doména", "domain": "Doména",
"domain_admins": "Administrátori domény", "domain_admins": "Administrátori domény",
"domain_aliases": "Alias domény", "domain_aliases": "Alias domény",
"domain_templates": "Šablóny domén",
"domain_quota": "Kvóta", "domain_quota": "Kvóta",
"domain_quota_total": "Celkové kvóta domény", "domain_quota_total": "Celkové kvóta domény",
"domains": "Domény", "domains": "Domény",
@@ -749,6 +798,7 @@
"mailbox_defaults": "Predvolené nastavenia", "mailbox_defaults": "Predvolené nastavenia",
"mailbox_defaults_info": "Definuje predvolené nastavenia pre nové schránky.", "mailbox_defaults_info": "Definuje predvolené nastavenia pre nové schránky.",
"mailbox_defquota": "Predvolená veľkosť schránky", "mailbox_defquota": "Predvolená veľkosť schránky",
"mailbox_templates": "Šablóny schránok",
"mailbox_quota": "Max. veľkosť schránky", "mailbox_quota": "Max. veľkosť schránky",
"mailboxes": "Mailové schránky", "mailboxes": "Mailové schránky",
"max_aliases": "Max. počet aliasov", "max_aliases": "Max. počet aliasov",
@@ -816,6 +866,8 @@
"table_size_show_n": "Zobraziť %s položiek", "table_size_show_n": "Zobraziť %s položiek",
"target_address": "Cieľová adresa", "target_address": "Cieľová adresa",
"target_domain": "Cieľová doména", "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_in": "Vynútiť TLS pre prichádzajúcu poštu",
"tls_enforce_out": "Vynútiť TLS pre odchádzajúcu poštu", "tls_enforce_out": "Vynútiť TLS pre odchádzajúcu poštu",
"tls_map_dest": "Cieľ", "tls_map_dest": "Cieľ",
@@ -896,7 +948,19 @@
"type": "Typ" "type": "Typ"
}, },
"queue": { "queue": {
"queue_manager": "Správca fronty" "queue_manager": "Správca fronty",
"delete": "Vymazať všetko",
"flush": "Vyprázdnit frontu",
"info": "Poštová fronta obsahuje všetky e-maily, ktoré čakajú na doručenie. Ak e-mail uviazne v poštovej fronte na dlhší čas, systém ho automaticky vymaže.<br>Chybové hlásenie príslušného e-mailu poskytuje informácie o tom, prečo sa e-mail nepodarilo doručiť.",
"legend": "Možnosti akcií nad poštovou frontou:",
"ays": "Potvrďte, že chcete naozaj odstrániť všetky položky z aktuálnej fronty.",
"deliver_mail": "Doručiť",
"deliver_mail_legend": "Pokus o opätovné doručenie vybraných e-mailov.",
"show_message": "Zobraziť správu",
"unhold_mail": "Uvoľniť",
"unhold_mail_legend": "Uvoľniť vybrané e-maily na doručenie. (Len v prípade predchádzajúceho podržania)",
"hold_mail": "Podržať",
"hold_mail_legend": "Podržať vybrané e-maily. (Zabráni ďalším pokusom o doručenie)"
}, },
"ratelimit": { "ratelimit": {
"disabled": "Vypnuté", "disabled": "Vypnuté",
@@ -980,6 +1044,9 @@
"settings_map_added": "Pridaná mapa nastavení", "settings_map_added": "Pridaná mapa nastavení",
"settings_map_removed": "Odstránená mapa nastavení ID %s", "settings_map_removed": "Odstránená mapa nastavení ID %s",
"sogo_profile_reset": "SOGo profil pre používateľa %s resetovaný", "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_deleted": "Položka mapy TLS pravidiel %s vymazaná",
"tls_policy_map_entry_saved": "Položka mapy TLS pravidiel \"%s\" uložená", "tls_policy_map_entry_saved": "Položka mapy TLS pravidiel \"%s\" uložená",
"ui_texts": "Zmeny v UI textoch uložené", "ui_texts": "Zmeny v UI textoch uložené",
@@ -987,7 +1054,8 @@
"verified_fido2_login": "Overené FIDO2 prihlásenie", "verified_fido2_login": "Overené FIDO2 prihlásenie",
"verified_totp_login": "Overené TOTP prihlásenie", "verified_totp_login": "Overené TOTP prihlásenie",
"verified_webauthn_login": "Overené WebAuthn prihlásenie", "verified_webauthn_login": "Overené WebAuthn prihlásenie",
"verified_yotp_login": "Overené Yubico OTP prihlásenie" "verified_yotp_login": "Overené Yubico OTP prihlásenie",
"domain_footer_modified": "Zmeny v pätičke domény %s boli uložené"
}, },
"tfa": { "tfa": {
"api_register": "%s využíva Yubico Cloud API. Prosím, zaobstarajte si API kľúč pre váš kľúč <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">tu</a>", "api_register": "%s využíva Yubico Cloud API. Prosím, zaobstarajte si API kľúč pre váš kľúč <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">tu</a>",
@@ -1031,13 +1099,16 @@
"alias_valid_until": "Platné do", "alias_valid_until": "Platné do",
"aliases_also_send_as": "Môže odosielať ako používateľ", "aliases_also_send_as": "Môže odosielať ako používateľ",
"aliases_send_as_all": "Nekontrolovať prístup odosielateľa pre nasledujúcu doménu/y a jej alias domény", "aliases_send_as_all": "Nekontrolovať prístup odosielateľa pre nasledujúcu doménu/y a jej alias domény",
"allowed_protocols": "Povolené protokoly",
"app_hint": "Heslá aplikácií sú alternatívne heslá pre vaše IMAP, SMTP, CalDAV, CardDAV a EAS prihlásenie. Používateľské meno zostáva nezmenené.<br>SOGo nie je momentálne podporovaný.", "app_hint": "Heslá aplikácií sú alternatívne heslá pre vaše IMAP, SMTP, CalDAV, CardDAV a EAS prihlásenie. Používateľské meno zostáva nezmenené.<br>SOGo nie je momentálne podporovaný.",
"app_name": "Meno aplikácie", "app_name": "Meno aplikácie",
"app_passwds": "Heslá aplikácií", "app_passwds": "Heslá aplikácií",
"apple_connection_profile": "Apple konfiguračný profil", "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_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_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 mail schránke.",
"change_password": "Zmeniť heslo", "change_password": "Zmeniť heslo",
"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", "clear_recent_successful_connections": "Vymazať nedávne úspešné prihlásenia",
"client_configuration": "Zobraziť konfiguračné pokyny pre emailových klientov a smartfóny", "client_configuration": "Zobraziť konfiguračné pokyny pre emailových klientov a smartfóny",
"create_app_passwd": "Vytvoriť heslo aplikácie", "create_app_passwd": "Vytvoriť heslo aplikácie",
@@ -1048,6 +1119,7 @@
"delete_ays": "Potvrďte zmazanie.", "delete_ays": "Potvrďte zmazanie.",
"direct_aliases": "Priame alias adresy", "direct_aliases": "Priame alias adresy",
"direct_aliases_desc": "Priame aliasy sú ovplyvnené spam filtrom a nastavením TLS pravidiel.", "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 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": "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_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", "eas_reset_now": "Reset ActiveSync",
@@ -1141,15 +1213,15 @@
"spamfilter_yellow": "Žltá: Táto správa môže byť spam, bude označená ako spam a presunutá do priečinku nevyžiadanej pošty", "spamfilter_yellow": "Žltá: Táto správa môže byť spam, bude označená ako spam a presunutá do priečinku nevyžiadanej pošty",
"status": "Status", "status": "Status",
"sync_jobs": "Synchronizačné úlohy", "sync_jobs": "Synchronizačné úlohy",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Problém s autentifikáciou",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Chybné uživateľské meno alebo heslo",
"syncjob_EXIT_CONNECTION_FAILURE": "Problém so spojením",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Nedá sa pripojiť k vzdialenému serveru",
"syncjob_EXIT_OVERQUOTA": "Cieľová schránka je plná",
"syncjob_EXIT_TLS_FAILURE": "Problém so šifrovaným spojením",
"syncjob_EX_OK": "Úspech",
"syncjob_check_log": "Skontrolujte záznam", "syncjob_check_log": "Skontrolujte záznam",
"syncjob_last_run_result": "Výsledok posledného spustenia", "syncjob_last_run_result": "Výsledok posledného spustenia",
"syncjob_EX_OK": "Úspech",
"syncjob_EXIT_CONNECTION_FAILURE": "Problém so spojením",
"syncjob_EXIT_TLS_FAILURE": "Problém so šifrovaným spojením",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Problém s autentifikáciou",
"syncjob_EXIT_OVERQUOTA": "Cieľová schránka je plná",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Nedá sa pripojiť k vzdialenému serveru",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Chybné uživateľské meno alebo heslo",
"tag_handling": "Zaobchádzanie s označenou poštou", "tag_handling": "Zaobchádzanie s označenou poštou",
"tag_help_example": "Príklad tagu e-mailovej adresy: me<b>+Facebook</b>@example.org", "tag_help_example": "Príklad tagu e-mailovej adresy: me<b>+Facebook</b>@example.org",
"tag_help_explain": "V podadresári: nový podadresár s menom tag-u bude vytvorený nižšie INBOX (\"INBOX/Facebook\").<br>\r\nIn subject: meno štítka bude pridané pred predmet pošty, napríklad: \"[Facebook] My News\".", "tag_help_explain": "V podadresári: nový podadresár s menom tag-u bude vytvorený nižšie INBOX (\"INBOX/Facebook\").<br>\r\nIn subject: meno štítka bude pridané pred predmet pošty, napríklad: \"[Facebook] My News\".",
@@ -1169,11 +1241,9 @@
"week": "týždeň", "week": "týždeň",
"weekly": "Týždenne", "weekly": "Týždenne",
"weeks": "týždne", "weeks": "týždne",
"with_app_password": "s heslom aplikácie",
"year": "rok", "year": "rok",
"years": "rokov", "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á {{number_of_app_passwords}} 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."
}, },
"warning": { "warning": {
"cannot_delete_self": "Nemožno vymazať prihláseného používateľa", "cannot_delete_self": "Nemožno vymazať prihláseného používateľa",

View File

@@ -473,7 +473,7 @@
"cname_from_a": "Värde härstammar från A/AAAA-uppslaget. Detta stöds så länge som uppslaget pekar mot rätt resurs.", "cname_from_a": "Värde härstammar från A/AAAA-uppslaget. Detta stöds så länge som uppslaget pekar mot rätt resurs.",
"dns_records": "DNS-uppslag", "dns_records": "DNS-uppslag",
"dns_records_24hours": "Observera att ändringar gjorda i DNS kan ta upp till 24 timmar innan det visas korrekt på denna sida. Syftet med sidan är att enkelt se hur DNS-uppslagen är konfigurerade. Det är lätt att kontrollera att DNS-uppslagen är korrekt uppsatta.", "dns_records_24hours": "Observera att ändringar gjorda i DNS kan ta upp till 24 timmar innan det visas korrekt på denna sida. Syftet med sidan är att enkelt se hur DNS-uppslagen är konfigurerade. Det är lätt att kontrollera att DNS-uppslagen är korrekt uppsatta.",
"dns_records_docs": "Se även <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">dokumentationen</a>.", "dns_records_docs": "Se även <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentationen</a>.",
"dns_records_data": "Korrektdata", "dns_records_data": "Korrektdata",
"dns_records_name": "Namn", "dns_records_name": "Namn",
"dns_records_status": "Nuvarande status", "dns_records_status": "Nuvarande status",

View File

@@ -1,61 +1,113 @@
{ {
"acl": { "acl": {
"alias_domains": "Takma alan adı ekle", "alias_domains": "Alias domain ekle",
"app_passwds": "Uygulama şifrelerini yönet", "app_passwds": "Uygulama şifrelerini yönet",
"delimiter_action": "Sınırlama işlemi", "bcc_maps": "BCC haritası",
"domain_relayhost": "Bir alan adı için relayhost sunucusunu değiştir", "delimiter_action": "Sınırlandırma işlemi",
"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",
"domain_desc": "Alan adııklamasını değiştir", "domain_desc": "Alan adııklamasını değiştir",
"extend_sender_acl": "Gönderenin acl'sini harici adreslere göre genişletmeye izin ver", "domain_relayhost": "Alan adı için relayhost sunucusunu değiştir",
"spam_policy": "Engellenenler / İzin verilenler", "eas_reset": "EAS cihazlarını sıfırla",
"filters": "Fitreler" "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": { "add": {
"activate_filter_warn": "Aktif edilirse diğer tüm filtreler devre dışı bırakılacak.", "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", "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": "Takma alan adı",
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayırın).</small>", "alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayrılmış).</small>",
"backup_mx_options": "İletme ayarları", "app_name": "Uygulama adı",
"delete2": "Kaynakta olmayan hedefteki mesajları sil", "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", "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": "Alan adı",
"domain_matches_hostname": "Alan adı %s ana bilgisayar adıyla eşleşiyor", "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)", "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", "generate": "oluştur",
"goto_ham": "Ham olarak<span class=\"text-success\"><b>işaretle</b></span>", "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>", "goto_spam": "Spam olarak<span class=\"text-danger\"><b>işaretle</b></span>",
"hostname": "Ana sunucu", "hostname": "Ana sunucu",
"inactive": "İnaktif",
"kind": "Tür", "kind": "Tür",
"mailbox_quota_m": "Posta kutusu başına maksimum kota (MiB)", "mailbox_quota_def": "Varsayılan e-posta kotası",
"max_aliases": "Maksimum olası takma adı", "mailbox_quota_m": "E-posta başına maksimum kota (MiB)",
"max_mailboxes": "Maksimum olası posta kutusu", "mailbox_username": "Kullanıcı adı (e-posta adresinin sol kısmı)",
"nexthop": "Sonraki atlama", "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", "port": "Port",
"public_comment": "Genel yorum", "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.",
"relay_all": "Tüm alıcılara ilet", "private_comment": "Özel not",
"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.", "public_comment": "Herkese açık not",
"relay_domain": "Bu alan adını ilet", "quota_mb": "Kota (MiB)",
"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_all": "Tüm alıcıları aktar",
"relay_unknown_only": "Yalnızca mevcut olmayan posta kutularını ilet. Mevcut posta kutuları yerel olarak teslim edilecektir.", "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.",
"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.", "relay_domain": "Bu etki alanını aktarın",
"skipcrossduplicates": "Klasörler arasında yinelenen mesajları atlayın (ilk mesaj seçilir)", "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": "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ı", "target_domain": "Hedef alan adı",
"timeout1": "Uzak ana bilgisayara bağlantısı zaman aşımına uğradı", "timeout1": "Uzak ana bilgisayara bağlantı için zaman aşımı",
"timeout2": "Yerel ana bilgisayara bağlantı zaman aşımına uğradı" "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": { "admin": {
"action": "İşlem", "action": "İşlem",
@@ -82,5 +134,18 @@
"f2b_ban_time": "Yasaklama süresi (saniye)", "f2b_ban_time": "Yasaklama süresi (saniye)",
"f2b_max_attempts": "Maksimum giriş denemesi", "f2b_max_attempts": "Maksimum giriş denemesi",
"f2b_retry_window": "Maksimum girişim için deneme pencere(leri)" "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": "Вид", "kind": "Вид",
"delete1": "Видалити з джерела після завершення", "delete1": "Видалити з джерела після завершення",
"delete2duplicates": "Видалити дублікати на місці призначення", "delete2duplicates": "Видалити дублікати на місці призначення",
"domain_quota_m": "Загальна квота домену (МіБ)" "domain_quota_m": "Загальна квота домену (МіБ)",
"dry": "Імітувати синхронізацію"
}, },
"admin": { "admin": {
"access": "Налаштування доступу", "access": "Налаштування доступу",
@@ -335,7 +336,17 @@
"transport_test_rcpt_info": "&#8226; Використовуйте null@hosted.mailcow.de для перевірки пересилання на зовнішній пункт призначення.", "transport_test_rcpt_info": "&#8226; Використовуйте null@hosted.mailcow.de для перевірки пересилання на зовнішній пункт призначення.",
"transports_hint": "&#8226; Глобальні правила маршрутизації <b>переважають</b> над маршрутами на основі відправника.<br>\n&#8226; Переважно використовувати транспорти на основі резолвінгу MX.<br>\n&#8226; Користувацькі політики TLS для вихідної пошти будуть проігноровані та використовуватимуть політику TLS, налаштовану тут.<br>\n&#8226; Протокол для доставки завжди \"smtp:\" і тому намагатиметься використовувати TLS, якщо наступний хост підтримує його. SMTPS (TLS, найчастіше порту 465) не підтримується.<br>\n&#8226; Адреси відповідні \"/localhost$/\" завжди будуть доставлені \"local:\", отже політика \"*\" не поширюється на них.<br>\n&#8226; Щоб визначити облікові дані для наступного вузла \"[host]:25\", Postfix <b>завжди</b> шукає дані для \"host\" перед тим як шукати \"[host]:25\". Така поведінка унеможливлює використання \"host\" and \"[host]:25\" одночасно.", "transports_hint": "&#8226; Глобальні правила маршрутизації <b>переважають</b> над маршрутами на основі відправника.<br>\n&#8226; Переважно використовувати транспорти на основі резолвінгу MX.<br>\n&#8226; Користувацькі політики TLS для вихідної пошти будуть проігноровані та використовуватимуть політику TLS, налаштовану тут.<br>\n&#8226; Протокол для доставки завжди \"smtp:\" і тому намагатиметься використовувати TLS, якщо наступний хост підтримує його. SMTPS (TLS, найчастіше порту 465) не підтримується.<br>\n&#8226; Адреси відповідні \"/localhost$/\" завжди будуть доставлені \"local:\", отже політика \"*\" не поширюється на них.<br>\n&#8226; Щоб визначити облікові дані для наступного вузла \"[host]:25\", Postfix <b>завжди</b> шукає дані для \"host\" перед тим як шукати \"[host]:25\". Така поведінка унеможливлює використання \"host\" and \"[host]:25\" одночасно.",
"ui_header_announcement_help": "Оголошення видно на екрані входу в mailcow UI і всім користувачам, що ввійшли в систему.", "ui_header_announcement_help": "Оголошення видно на екрані входу в mailcow UI і всім користувачам, що ввійшли в систему.",
"unchanged_if_empty": "Якщо не змінено, залиште порожнім" "unchanged_if_empty": "Якщо не змінено, залиште порожнім",
"allowed_methods": "Access-Control-Allow-Methods",
"f2b_max_ban_time": "Максимальний час блокування (с)",
"f2b_ban_time_increment": "Час бану збільшується з кожним баном",
"allowed_origins": "Access-Control-Allow-Origin",
"cors_settings": "Налаштування CORS",
"ip_check": "Перевірка IP",
"ip_check_disabled": "Перевірка IP вимкнена. Ви можете ввімкнути його в меню<br> <strong>Система > Конфігурація > Параметри > Налаштувати</strong>",
"ip_check_opt_in": "Згода на використання сторонніх служб <strong>ipv4.mailcow.email</strong> і <strong>ipv6.mailcow.email</strong> для визначення зовнішніх IP-адрес.",
"options": "Параметри",
"queue_unban": "розблокувати"
}, },
"danger": { "danger": {
"alias_domain_invalid": "Неприпустимий псевдонім домену: %s", "alias_domain_invalid": "Неприпустимий псевдонім домену: %s",
@@ -454,7 +465,17 @@
"tls_policy_map_dest_invalid": "Неприпустиме значення призначення політики", "tls_policy_map_dest_invalid": "Неприпустиме значення призначення політики",
"tls_policy_map_parameter_invalid": "Неприпустиме значення параметра політики", "tls_policy_map_parameter_invalid": "Неприпустиме значення параметра політики",
"unlimited_quota_acl": "Необмежена квота заборонена політикою доступу", "unlimited_quota_acl": "Необмежена квота заборонена політикою доступу",
"yotp_verification_failed": "Помилка валідації Yubico OTP: %s" "yotp_verification_failed": "Помилка валідації Yubico OTP: %s",
"cors_invalid_method": "Вказано недійсний Allow-Method",
"webauthn_authenticator_failed": "Обраний автентифікатор не знайдено",
"webauthn_publickey_failed": "Для вибраного автентифікатора не було збережено відкритого ключа",
"webauthn_username_failed": "Обраний автентифікатор належить іншому акаунту",
"cors_invalid_origin": "Вказано недійсний Allow-Origin",
"demo_mode_enabled": "Демонстраційний режим увімкнено",
"extended_sender_acl_denied": "відсутній ACL для встановлення зовнішніх адрес відправників",
"template_exists": "Шаблон %s вже існує",
"template_id_invalid": "Ідентифікатор шаблону %s недійсний",
"template_name_invalid": "Ім'я шаблону невірне"
}, },
"debug": { "debug": {
"chart_this_server": "Діаграма (цей сервер)", "chart_this_server": "Діаграма (цей сервер)",
@@ -481,13 +502,27 @@
"username": "Ім'я користувача", "username": "Ім'я користувача",
"external_logs": "Зовнішні журнали", "external_logs": "Зовнішні журнали",
"jvm_memory_solr": "Використання оперативної пам'яті JVM", "jvm_memory_solr": "Використання оперативної пам'яті JVM",
"log_info": "<p><b>Журнали контейнерів</b> mailcow зберігаються в Redis, і раз на хвилину рядки журналу за межами <code>LOG_LINES (%d)</code> видаляються, щоб зменшити навантаження на сервер.\n <br>Самі журнали контейнерів не зберігаються після перезавантаження контейнера. Усі контейнери додатково пишуть логи у службу Docker, і, отже, використовують драйвер логування за промовчанням. Журнали контейнерів призначені лише для налагодження дрібних проблем. Для інших завдань, будь ласка, настройте драйвер логування Docker самостійно.</p>\n <p><b>Зовнішні журнали</b> збираються через API програм.</p>\n <p><b>Статичні журнали</b> &ndash; це в основному журнали активності, які не записуються в Dockerd, але все одно повинні бути постійними (за винятком журналів API).</p>" "log_info": "<p><b>Журнали контейнерів</b> mailcow зберігаються в Redis, і раз на хвилину рядки журналу за межами <code>LOG_LINES (%d)</code> видаляються, щоб зменшити навантаження на сервер.\n <br>Самі журнали контейнерів не зберігаються після перезавантаження контейнера. Усі контейнери додатково пишуть логи у службу Docker, і, отже, використовують драйвер логування за промовчанням. Журнали контейнерів призначені лише для налагодження дрібних проблем. Для інших завдань, будь ласка, настройте драйвер логування Docker самостійно.</p>\n <p><b>Зовнішні журнали</b> збираються через API програм.</p>\n <p><b>Статичні журнали</b> &ndash; це в основному журнали активності, які не записуються в Dockerd, але все одно повинні бути постійними (за винятком журналів API).</p>",
"error_show_ip": "Не вдалося розпізнати публічні IP-адреси",
"no_update_available": "Система працює на останній версії",
"architecture": "Архітектура",
"container_running": "Працює",
"container_disabled": "Контейнер зупинено або вимкнено",
"container_stopped": "Зупинено",
"cores": "Ядра",
"current_time": "Системний час",
"memory": "Пам'ять",
"show_ip": "Показати загальнодоступну IP-адресу",
"timezone": "Часовий пояс",
"update_available": "Доступне оновлення",
"update_failed": "Не вдалося перевірити наявність оновлень",
"wip": "Наразі робота триває"
}, },
"diagnostics": { "diagnostics": {
"cname_from_a": "Значення, отримане із запису A/AAAA. Це підтримується, поки запис вказує на правильний ресурс.", "cname_from_a": "Значення, отримане із запису A/AAAA. Це підтримується, поки запис вказує на правильний ресурс.",
"dns_records": "Записи DNS", "dns_records": "Записи DNS",
"dns_records_data": "Значення", "dns_records_data": "Значення",
"dns_records_docs": "Також перегляньте <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">документацію</a>.", "dns_records_docs": "Також перегляньте <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">документацію</a>.",
"dns_records_name": "Назва", "dns_records_name": "Назва",
"dns_records_status": "Статус", "dns_records_status": "Статус",
"optional": "Цей запис необов'язковий.", "optional": "Цей запис необов'язковий.",
@@ -607,7 +642,22 @@
"sender_acl_info": "Врахуйте, що якщо користувачеві поштового облікового запису А дозволено відправляти від імені користувача Б, то адреса користувача Б не з'явиться автоматично у списку \"Відправник\" при написанні листів у SOGo.<br>\n Користувач поштового облікового запису Б повинен створити делегування в SOGo, щоб користувач поштового облікового запису А міг вибрати його адресу як відправника. Делегування знаходиться в меню (три крапки) праворуч від імені поштового облікового запису у вікні пошти SOGo. Ця поведінка не відноситься до псевдонімів.", "sender_acl_info": "Врахуйте, що якщо користувачеві поштового облікового запису А дозволено відправляти від імені користувача Б, то адреса користувача Б не з'явиться автоматично у списку \"Відправник\" при написанні листів у SOGo.<br>\n Користувач поштового облікового запису Б повинен створити делегування в SOGo, щоб користувач поштового облікового запису А міг вибрати його адресу як відправника. Делегування знаходиться в меню (три крапки) праворуч від імені поштового облікового запису у вікні пошти SOGo. Ця поведінка не відноситься до псевдонімів.",
"sogo_visible_info": "Впливає лише на об'єкти, які можуть відображатися в SOGo (персональні або загальні псевдоніми, що вказують щонайменше на один локальний поштовий обліковий запис). Зверніть увагу, що якщо функцію вимкнено, користувач не зможе вибрати адресу псевдоніма як відправника в SOGo.", "sogo_visible_info": "Впливає лише на об'єкти, які можуть відображатися в SOGo (персональні або загальні псевдоніми, що вказують щонайменше на один локальний поштовий обліковий запис). Зверніть увагу, що якщо функцію вимкнено, користувач не зможе вибрати адресу псевдоніма як відправника в SOGo.",
"target_address": "Власники псевдоніма, <small>(розділені комами)</small>", "target_address": "Власники псевдоніма, <small>(розділені комами)</small>",
"timeout2": "Тайм-аут для підключення до локального хоста" "timeout2": "Тайм-аут для підключення до локального хоста",
"pushover_sound": "Звук",
"domain_footer": "Нижній колонтитул домену",
"domain_footer_info": "Нижні колонтитули для всього домену додаються до всіх вихідних електронних листів, пов’язаних з адресою в цьому домені. <br> Наступні змінні можна використовувати для нижнього колонтитула:",
"domain_footer_info_vars": {
"from_name": "{= from_name =} - З назви envelope, наприклад, для \"Mailcow &lt;moo@mailcow.tld&gt;\" повертає \"Mailcow\"",
"auth_user": "{= auth_user =} - Аутентифіковане ім'я користувача, вказане MTA",
"from_user": "{= from_user =} - З користувацької частини envelope, наприклад, для \"moo@mailcow.tld\" повертає \"moo\"",
"from_addr": "{= from_addr =} - З адресної частини envelope",
"from_domain": "{= from_domain =} - З доменної частини envelope",
"custom": "{= foo =} - Якщо поштова скринька має кастомний атрибут \"foo\" зі значенням \"bar\", то повертається \"bar\""
},
"domain_footer_html": "Нижній колонтитул HTML",
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул",
"custom_attributes": "Користувацькі атрибути",
"mbox_exclude": "Виключити поштові скриньки"
}, },
"fido2": { "fido2": {
"confirm": "Підтвердити", "confirm": "Підтвердити",
@@ -648,7 +698,8 @@
"restart_netfilter": "Перезапустити netfilter", "restart_netfilter": "Перезапустити netfilter",
"restart_sogo": "Перезапустити SOGo", "restart_sogo": "Перезапустити SOGo",
"user_settings": "Налаштування користувача", "user_settings": "Налаштування користувача",
"mailcow_config": "Конфігурація" "mailcow_config": "Конфігурація",
"mailcow_system": "Система"
}, },
"info": { "info": {
"no_action": "Дій не передбачено", "no_action": "Дій не передбачено",
@@ -831,7 +882,13 @@
"target_address": "Власники псевдоніму", "target_address": "Власники псевдоніму",
"tls_map_dest_info": "Приклади: example.org, .example.org, [mail.example.org]:25", "tls_map_dest_info": "Приклади: example.org, .example.org, [mail.example.org]:25",
"tls_map_parameters_info": "Залиште поле порожнім або вкажіть параметри, наприклад: protocols=!SSLv2 ciphers=medium exclude=3DES", "tls_map_parameters_info": "Залиште поле порожнім або вкажіть параметри, наприклад: protocols=!SSLv2 ciphers=medium exclude=3DES",
"tls_policy_maps_enforced_tls": "Для вихідних повідомлень від користувачів із включеною примусовою політикою шифрування вихідних з'єднань не описані глобальною політикою, будуть застосовані значення за замовчуванням, зазначені в <code>smtp_tls_mandatory_protocols</code> та <code>smtp_tls_mandatory_ciphers</code>." "tls_policy_maps_enforced_tls": "Для вихідних повідомлень від користувачів із включеною примусовою політикою шифрування вихідних з'єднань не описані глобальною політикою, будуть застосовані значення за замовчуванням, зазначені в <code>smtp_tls_mandatory_protocols</code> та <code>smtp_tls_mandatory_ciphers</code>.",
"add_template": "Додати шаблон",
"domain_templates": "Шаблони доменів",
"relay_unknown": "Ретрансляція невідомих поштових скриньок",
"mailbox_templates": "Шаблони поштових скриньок",
"templates": "Шаблони",
"template": "Шаблон"
}, },
"oauth2": { "oauth2": {
"authorize_app": "Авторизація додатка", "authorize_app": "Авторизація додатка",
@@ -896,7 +953,20 @@
"table_size_show_n": "Відображати %s полів" "table_size_show_n": "Відображати %s полів"
}, },
"queue": { "queue": {
"queue_manager": "Черга на відправлення" "queue_manager": "Черга на відправлення",
"delete": "Видалити все",
"info": "Поштова черга містить усі електронні листи, які очікують на доставку. Якщо електронний лист застряг у черзі на тривалий час, він автоматично видаляється системою.<br>Повідомлення про помилку відповідного листа містить інформацію про те, чому лист не може бути доставлено.",
"unhold_mail": "Зняти утримування",
"unhold_mail_legend": "Звільняє вибрані листи для доставки. (Потрібне попереднє утримання)",
"flush": "Очистити чергу",
"legend": "Функції дій з поштовими чергами:",
"ays": "Підтвердьте, що ви хочете видалити всі елементи з поточної черги.",
"deliver_mail": "Доставити",
"deliver_mail_legend": "проби повторної доставки вибраних листів.",
"hold_mail": "Утримати",
"hold_mail_legend": "Утримує вибрані листи. (Запобігає подальшим спробам доставки)",
"show_message": "Показати повідомлення",
"unban": "розблокувати чергу"
}, },
"ratelimit": { "ratelimit": {
"disabled": "Вимкнено", "disabled": "Вимкнено",
@@ -988,7 +1058,12 @@
"settings_map_added": "Правило додано", "settings_map_added": "Правило додано",
"tls_policy_map_entry_deleted": "Політику TLS ID %s видалено", "tls_policy_map_entry_deleted": "Політику TLS ID %s видалено",
"verified_totp_login": "Авторизацію TOTP пройдено", "verified_totp_login": "Авторизацію TOTP пройдено",
"domain_add_dkim_available": "Ключ DKIM вже існує" "domain_add_dkim_available": "Ключ DKIM вже існує",
"template_added": "Додано шаблон %s",
"template_modified": "Зміни до шаблону %s збережено",
"cors_headers_edited": "Налаштування CORS збережено",
"ip_check_opt_in_modified": "Перевірка IP-адреси успішно збережено",
"template_removed": "Шаблону із ID %s видалено"
}, },
"tfa": { "tfa": {
"confirm": "Підтвердьте", "confirm": "Підтвердьте",
@@ -1037,7 +1112,7 @@
"apple_connection_profile_complete": "Цей профіль включає налаштування IMAP та SMTP, а також CalDAV (календарів) та CardDAV (контактів) для пристрою Apple.", "apple_connection_profile_complete": "Цей профіль включає налаштування IMAP та SMTP, а також CalDAV (календарів) та CardDAV (контактів) для пристрою Apple.",
"apple_connection_profile_mailonly": "Цей профіль включає лише налаштування IMAP та SMTP для пристрою Apple.", "apple_connection_profile_mailonly": "Цей профіль включає лише налаштування IMAP та SMTP для пристрою Apple.",
"change_password": "Змінити пароль", "change_password": "Змінити пароль",
"change_password_hint_app_passwords": "Ваш обліковий запис містить {{number_of_app_passwords}} паролів додатків, які не змінюватимуться. Щоб керувати ними, перейдіть на вкладку \"Паролі додатків\".", "change_password_hint_app_passwords": "Ваш обліковий запис містить %d паролів додатків, які не змінюватимуться. Щоб керувати ними, перейдіть на вкладку \"Паролі додатків\".",
"clear_recent_successful_connections": "Очистити історію успішних підключень", "clear_recent_successful_connections": "Очистити історію успішних підключень",
"create_app_passwd": "Створити новий пароль", "create_app_passwd": "Створити новий пароль",
"create_syncjob": "Створити нове завдання синхронізації", "create_syncjob": "Створити нове завдання синхронізації",
@@ -1176,7 +1251,10 @@
"tag_help_explain": "Перемістити до підпапки: буде створено нову підпапку в INBOX з ім'ям тега, наприклад: \"INBOX/Facebook\".<br>\n Додати до теми листа: ім'я тега буде додано до теми листа, наприклад: \"[Facebook] My News\".", "tag_help_explain": "Перемістити до підпапки: буде створено нову підпапку в INBOX з ім'ям тега, наприклад: \"INBOX/Facebook\".<br>\n Додати до теми листа: ім'я тега буде додано до теми листа, наприклад: \"[Facebook] My News\".",
"tls_policy_warning": "<strong>Попередження:</strong> якщо ви увімкнете примусове шифрування пошти, ви можете зіткнутися з втратою листів.<br>Повідомлення, які не відповідають політиці, будуть відкидатися з повідомленням поштовим сервером про серйозний збій.<br>Цей параметр застосовується до вашої основної адреси електронної пошти (логіну), усім особистим псевдонімам та псевдонімам доменів. Маються на увазі лише псевдоніми <b>з однією поштовою скринькою</b>, як одержувач.", "tls_policy_warning": "<strong>Попередження:</strong> якщо ви увімкнете примусове шифрування пошти, ви можете зіткнутися з втратою листів.<br>Повідомлення, які не відповідають політиці, будуть відкидатися з повідомленням поштовим сервером про серйозний збій.<br>Цей параметр застосовується до вашої основної адреси електронної пошти (логіну), усім особистим псевдонімам та псевдонімам доменів. Маються на увазі лише псевдоніми <b>з однією поштовою скринькою</b>, як одержувач.",
"year": "рік", "year": "рік",
"years": "років" "years": "років",
"pushover_sound": "Звук",
"value": "Значення",
"attribute": "Атрибут"
}, },
"warning": { "warning": {
"domain_added_sogo_failed": "Домен був доданий, але перезавантажити SOGo не вдалося, будь ласка, перевірте журнали сервера.", "domain_added_sogo_failed": "Домен був доданий, але перезавантажити SOGo не вдалося, будь ласка, перевірте журнали сервера.",
@@ -1190,5 +1268,31 @@
"hash_not_found": "Хеш не знайдено або вже видалено", "hash_not_found": "Хеш не знайдено або вже видалено",
"ip_invalid": "Пропущено недійсний IP: %s", "ip_invalid": "Пропущено недійсний IP: %s",
"is_not_primary_alias": "Пропущено неосновний псевдонім %s" "is_not_primary_alias": "Пропущено неосновний псевдонім %s"
},
"datatables": {
"decimal": ".",
"infoPostFix": "",
"zeroRecords": "Відповідних записів не знайдено",
"aria": {
"sortAscending": ": активувати для сортування стовпців за зростанням",
"sortDescending": ": активувати для сортування стовпців за спаданням"
},
"emptyTable": "У таблиці немає даних",
"expand_all": "Розгорнути всі",
"info": "Показано від _START_ до _END_ of _TOTAL_ записів",
"infoEmpty": "Показано від 0 до 0 із 0 записів",
"infoFiltered": "(відфільтровано з _MAX_ всіх записів)",
"thousands": ",",
"lengthMenu": "Показати записи _MENU_",
"loadingRecords": "Завантаження...",
"processing": "Будь ласка, зачекайте...",
"search": "Пошук:",
"paginate": {
"first": "Перший",
"last": "Останній",
"next": "Наступний",
"previous": "Попередній"
},
"collapse_all": "Згорнути все"
} }
} }

View File

@@ -494,7 +494,7 @@
"dns_records": "DNS 记录", "dns_records": "DNS 记录",
"dns_records_24hours": "请注意 DNS 记录的更改可能需要24小时才可以使此页面的当前状态显示正确。此页面为你提供了一个可以便捷查询如何配置 DNS 记录以及检查你的 DNS 记录是否正确的方式。", "dns_records_24hours": "请注意 DNS 记录的更改可能需要24小时才可以使此页面的当前状态显示正确。此页面为你提供了一个可以便捷查询如何配置 DNS 记录以及检查你的 DNS 记录是否正确的方式。",
"dns_records_data": "正确数据", "dns_records_data": "正确数据",
"dns_records_docs": "请同时也参考这个<a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">文档</a>.", "dns_records_docs": "请同时也参考这个<a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">文档</a>.",
"dns_records_name": "名称", "dns_records_name": "名称",
"dns_records_status": "当前状态", "dns_records_status": "当前状态",
"dns_records_type": "类型", "dns_records_type": "类型",
@@ -1042,7 +1042,7 @@
"apple_connection_profile_mailonly": "此连接描述文件包括提供给 Apple 设备的 IMAP 和 SMTP 配置参数。", "apple_connection_profile_mailonly": "此连接描述文件包括提供给 Apple 设备的 IMAP 和 SMTP 配置参数。",
"apple_connection_profile_with_app_password": "一个新的应用程序密码将会被生成并添加到该配置文件中,因此在设备设置时不需要输入密码。请不要随意分享该文件,因为它包含你的邮箱的完全访问权限。", "apple_connection_profile_with_app_password": "一个新的应用程序密码将会被生成并添加到该配置文件中,因此在设备设置时不需要输入密码。请不要随意分享该文件,因为它包含你的邮箱的完全访问权限。",
"change_password": "更改密码", "change_password": "更改密码",
"change_password_hint_app_passwords": "你的账户有 {{number_of_app_passwords}} 个应用密码,这些密码将不会被更改。如果需要管理这些密码,请访问应用密码标签。", "change_password_hint_app_passwords": "你的账户有 %d 个应用密码,这些密码将不会被更改。如果需要管理这些密码,请访问应用密码标签。",
"clear_recent_successful_connections": "清除成功匹配的连接", "clear_recent_successful_connections": "清除成功匹配的连接",
"client_configuration": "显示邮箱客户端和智能手机的配置指南", "client_configuration": "显示邮箱客户端和智能手机的配置指南",
"create_app_passwd": "添加应用密码", "create_app_passwd": "添加应用密码",

View File

@@ -488,7 +488,7 @@
"dns_records": "DNS 紀錄", "dns_records": "DNS 紀錄",
"dns_records_24hours": "請注意 DNS 紀錄的更改可能需要 24 小時才能正確顯示於此頁面。此頁面的目的是為了讓你可以輕鬆的了解如何設定 DNS 紀錄並檢查 DNS 是否設定正確。", "dns_records_24hours": "請注意 DNS 紀錄的更改可能需要 24 小時才能正確顯示於此頁面。此頁面的目的是為了讓你可以輕鬆的了解如何設定 DNS 紀錄並檢查 DNS 是否設定正確。",
"dns_records_data": "正確值", "dns_records_data": "正確值",
"dns_records_docs": "請同時另外查看 <a target=\"_blank\" href=\"https://mailcow.github.io/mailcow-dockerized-docs/prerequisite/prerequisite-dns/\">文件</a>.", "dns_records_docs": "請同時另外查看 <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">文件</a>.",
"dns_records_name": "名稱", "dns_records_name": "名稱",
"dns_records_status": "目前狀態", "dns_records_status": "目前狀態",
"dns_records_type": "類型", "dns_records_type": "類型",
@@ -1036,7 +1036,7 @@
"apple_connection_profile_mailonly": "此連接描述檔案包括提供給 Apple 裝置的 IMAP 和 SMTP 組態參數。", "apple_connection_profile_mailonly": "此連接描述檔案包括提供給 Apple 裝置的 IMAP 和 SMTP 組態參數。",
"apple_connection_profile_with_app_password": "應用程式密碼已產生並加入到連接描述檔案中,因此裝置在設定時不需要輸入密碼。請勿分享這個檔案,因為它擁有存取信箱的所有權限。", "apple_connection_profile_with_app_password": "應用程式密碼已產生並加入到連接描述檔案中,因此裝置在設定時不需要輸入密碼。請勿分享這個檔案,因為它擁有存取信箱的所有權限。",
"change_password": "更改密碼", "change_password": "更改密碼",
"change_password_hint_app_passwords": "你的帳號有 {{number_of_app_passwords}} 個應用程式密碼不會被更動。要管理這些密碼,請至應用程式密碼分頁。", "change_password_hint_app_passwords": "你的帳號有 %d 個應用程式密碼不會被更動。要管理這些密碼,請至應用程式密碼分頁。",
"clear_recent_successful_connections": "中斷成功的連線", "clear_recent_successful_connections": "中斷成功的連線",
"client_configuration": "顯示電子信箱程式和智慧型手機的設定指南", "client_configuration": "顯示電子信箱程式和智慧型手機的設定指南",
"create_app_passwd": "新增應用程式密碼", "create_app_passwd": "新增應用程式密碼",

View File

@@ -0,0 +1,9 @@
<div class="thumbnail mb-4">
<img class="img-thumbnail mb-4{% if dark %} bg-black{% endif %}" src="{{ logo }}" alt="mailcow logo">
<div class="caption">
<span class="badge fs-5 bg-info">{{ logo_specs.geometry.width }}x{{ logo_specs.geometry.height }} px</span>
<span class="badge fs-5 bg-info">{{ logo_specs.mimetype }}</span>
<span class="badge fs-5 bg-info">{{ logo_specs.fileSize }}</span>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<div role="tabpanel" class="tab-pane fade show active" id="tab-config-admins" role="tabpanel" aria-labelledby="tab-config-admins"> <div class="tab-pane fade show active" id="tab-config-admins" role="tabpanel" aria-labelledby="tab-config-admins">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header bg-danger text-white d-flex fs-5"> <div class="card-header bg-danger text-white d-flex fs-5">
<button class="btn d-md-none text-white flex-grow-1 text-start" data-bs-target="#collapse-tab-config-admins" data-bs-toggle="collapse" aria-controls="collapse-tab-config-admins"> <button class="btn d-md-none text-white flex-grow-1 text-start" data-bs-target="#collapse-tab-config-admins" data-bs-toggle="collapse" aria-controls="collapse-tab-config-admins">
@@ -122,8 +122,8 @@
</div> </div>
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<div class="offset-sm-2 col-sm-9"> <div class="offset-sm-2 col-sm-9 d-grid d-sm-block">
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-success" data-item="cors" data-api-url="edit/cors" data-id="editcors" data-action="edit_selected" href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button> <button class="btn btn-sm btn-xs-lg btn-success" data-item="cors" data-api-url="edit/cors" data-id="editcors" data-action="edit_selected" href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div> </div>
</div> </div>
</form> </form>
@@ -146,7 +146,7 @@
<div class="row mb-2"> <div class="row mb-2">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<label> <label>
<input type="checkbox" name="skip_ip_check" id="skip_ip_check_ro" {% if api.ro.skip_ip_check %}checked{% endif %}> {{ lang.admin.api_skip_ip_check }} <input type="checkbox" class="form-check-input" name="skip_ip_check" id="skip_ip_check_ro" {% if api.ro.skip_ip_check %}checked{% endif %}> {{ lang.admin.api_skip_ip_check }}
</label> </label>
</div> </div>
</div> </div>
@@ -159,15 +159,15 @@
<div class="row mb-2"> <div class="row mb-2">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<label> <label>
<input type="checkbox" name="active" {% if api.ro.active %}checked{% endif %}> {{ lang.admin.activate_api }} <input type="checkbox" class="form-check-input" name="active" {% if api.ro.active %}checked{% endif %}> {{ lang.admin.activate_api }}
</label> </label>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" name="admin_api[ro]" type="submit" href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button> <button class="btn btn-sm btn-xs-lg btn-xs-half d-block d-sm-inline btn-success" name="admin_api[ro]" type="submit" 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 admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#" {% if not api.ro.api_key %}disabled{% endif %}>{{ lang.admin.regen_api_key }}</button> <button class="btn btn-sm btn-xs-lg btn-xs-half d-block d-sm-inline btn-secondary admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#" {% if not api.ro.api_key %}disabled{% endif %}>{{ lang.admin.regen_api_key }}</button>
</div> </div>
</div> </div>
</div> </div>
@@ -191,7 +191,7 @@
<div class="row mb-2"> <div class="row mb-2">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<label> <label>
<input type="checkbox" name="skip_ip_check" id="skip_ip_check_rw" {% if api.rw.skip_ip_check %}checked{% endif %}> {{ lang.admin.api_skip_ip_check }} <input type="checkbox" class="form-check-input" name="skip_ip_check" id="skip_ip_check_rw" {% if api.rw.skip_ip_check %}checked{% endif %}> {{ lang.admin.api_skip_ip_check }}
</label> </label>
</div> </div>
</div> </div>
@@ -204,7 +204,7 @@
<div class="row mb-2"> <div class="row mb-2">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<label> <label>
<input type="checkbox" name="active" {% if api.rw.active %}checked{% endif %}> {{ lang.admin.activate_api }} <input type="checkbox" class="form-check-input" name="active" {% if api.rw.active %}checked{% endif %}> {{ lang.admin.activate_api }}
</label> </label>
</div> </div>
</div> </div>
@@ -227,7 +227,7 @@
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex fs-5"> <div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-dadmins" data-bs-toggle="collapse" aria-controls="ollapse-tab-config-dadmins"> <button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-dadmins" data-bs-toggle="collapse" aria-controls="collapse-tab-config-dadmins">
{{ lang.admin.domain_admins }} {{ lang.admin.domain_admins }}
</button> </button>
<span class="d-none d-md-block">{{ lang.admin.domain_admins }}</span> <span class="d-none d-md-block">{{ lang.admin.domain_admins }}</span>

View File

@@ -1,7 +1,7 @@
<div role="tabpanel" class="tab-pane fade" id="tab-config-customize" role="tabpanel" aria-labelledby="tab-config-customize"> <div class="tab-pane fade" id="tab-config-customize" role="tabpanel" aria-labelledby="tab-config-customize">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex fs-5"> <div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-customize" data-bs-toggle="collapse" aria-controls="ollapse-tab-config-customize"> <button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-customize" data-bs-toggle="collapse" aria-controls="collapse-tab-config-customize">
{{ lang.admin.customize }} {{ lang.admin.customize }}
</button> </button>
<span class="d-none d-md-block">{{ lang.admin.customize }}</span> <span class="d-none d-md-block">{{ lang.admin.customize }}</span>
@@ -10,22 +10,26 @@
<legend><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr /> <legend><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr />
<p class="text-muted">{{ lang.admin.logo_info }}</p> <p class="text-muted">{{ lang.admin.logo_info }}</p>
<form class="form-inline" role="form" method="post" enctype="multipart/form-data"> <form class="form-inline" role="form" method="post" enctype="multipart/form-data">
<p> <div class="mb-4">
<input class="mb-4" type="file" name="main_logo" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml"><br> <label for="main_logo_input" class="form-label">{{ lang.admin.logo_normal_label }}</label>
<button name="submit_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary"><i class="bi bi-upload"></i> {{ lang.admin.upload }}</button> <input class="form-control" id="main_logo_input" type="file" name="main_logo" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml">
</p> </div>
<div class="mb-4">
<label for="main_logo_dark_input" class="form-label">{{ lang.admin.logo_dark_label }}</label>
<input class="form-control" id="main_logo_dark_input" type="file" name="main_logo_dark" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml">
</div>
<button name="submit_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary"><i class="bi bi-upload"></i> {{ lang.admin.upload }}</button>
</form> </form>
{% if logo %} {% if logo or logo_dark %}
<div class="row"> <div class="row mt-4">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="thumbnail"> {% if logo %}
<img class="img-thumbnail" src="{{ logo }}" alt="mailcow logo"> {% include 'admin/customize/logo.twig' %}
<div class="caption"> {% endif %}
<span class="badge fs-5 bg-info">{{ logo_specs.geometry.width }}x{{ logo_specs.geometry.height }} px</span> {% if logo_dark %}
<span class="badge fs-5 bg-info">{{ logo_specs.mimetype }}</span> {% include 'admin/customize/logo.twig' with {'logo': logo_dark, 'logo_specs': logo_dark_specs, 'dark': 1} %}
<span class="badge fs-5 bg-info">{{ logo_specs.fileSize }}</span> {% endif %}
</div>
</div>
<hr> <hr>
<form class="form-inline" role="form" method="post"> <form class="form-inline" role="form" method="post">
<p><button name="reset_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary">{{ lang.admin.reset_default }}</button></p> <p><button name="reset_main_logo" type="submit" class="btn btn-sm d-block d-sm-inline btn-secondary">{{ lang.admin.reset_default }}</button></p>
@@ -111,7 +115,7 @@
</div> </div>
</div> </div>
<p><textarea class="form-control" id="ui_announcement_text" name="ui_announcement_text" rows="7">{{ ui_texts.ui_announcement_text }}</textarea></p> <p><textarea class="form-control" id="ui_announcement_text" name="ui_announcement_text" rows="7">{{ ui_texts.ui_announcement_text }}</textarea></p>
<div class="checkbox"> <div class="form-check">
<label> <label>
<input type="checkbox" name="ui_announcement_active" class="form-check-input" {% if ui_texts.ui_announcement_active == 1 %}checked{% endif %}> {{ lang.admin.ui_header_announcement_active }} <input type="checkbox" name="ui_announcement_active" class="form-check-input" {% if ui_texts.ui_announcement_active == 1 %}checked{% endif %}> {{ lang.admin.ui_header_announcement_active }}
</label> </label>

View File

@@ -1,7 +1,7 @@
<div role="tabpanel" class="tab-pane fade" id="tab-config-dkim" role="tabpanel" aria-labelledby="tab-config-dkim"> <div class="tab-pane fade" id="tab-config-dkim" role="tabpanel" aria-labelledby="tab-config-dkim">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex fs-5"> <div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-dkim" data-bs-toggle="collapse" aria-controls="ollapse-tab-config-dkim"> <button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-dkim" data-bs-toggle="collapse" aria-controls="collapse-tab-config-dkim">
{{ lang.admin.dkim_keys }} {{ lang.admin.dkim_keys }}
</button> </button>
<span class="d-none d-md-block">{{ lang.admin.dkim_keys }}</span> <span class="d-none d-md-block">{{ lang.admin.dkim_keys }}</span>
@@ -20,7 +20,7 @@
{% for domain, domain_data in dkim_domains %} {% for domain, domain_data in dkim_domains %}
{% if domain_data.dkim %} {% if domain_data.dkim %}
<div class="row collapse show dkim_key_valid"> <div class="row collapse show dkim_key_valid">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="{{ domain }}"></div> <div class="col-md-1"><input type="checkbox" class="form-check-input" data-id="dkim" name="multi_select" value="{{ domain }}"></div>
<div class="col-md-3"> <div class="col-md-3">
<p>{{ lang.admin.domain }}: <strong>{{ domain }}</strong> <p>{{ lang.admin.domain }}: <strong>{{ domain }}</strong>
<p class="dkim-label"><span class="badge fs-6 bg-success">{{ lang.admin.dkim_key_valid }}</span></p> <p class="dkim-label"><span class="badge fs-6 bg-success">{{ lang.admin.dkim_key_valid }}</span></p>
@@ -31,7 +31,7 @@
<div class="col-md-8"> <div class="col-md-8">
<textarea class="form-control" rows="6" readonly>{{ domain_data.dkim.dkim_txt }}</textarea> <textarea class="form-control" rows="6" readonly>{{ domain_data.dkim.dkim_txt }}</textarea>
<small> <small>
<i class="bi bi-arrow-return-right"></i> <i class="bi bi-arrow-return-right"></i>
<a href="#" data-bs-toggle="modal" data-bs-target="#showDKIMprivKey" id="dkim_priv" data-priv-key="{{ domain_data.dkim.privkey }}"> {{ lang.admin.dkim_private_key }}</a> <a href="#" data-bs-toggle="modal" data-bs-target="#showDKIMprivKey" id="dkim_priv" data-priv-key="{{ domain_data.dkim.privkey }}"> {{ lang.admin.dkim_private_key }}</a>
</small> </small>
</div> </div>
@@ -50,7 +50,7 @@
{% for alias_domain, alias_domain_data in domain_data.alias_domains %} {% for alias_domain, alias_domain_data in domain_data.alias_domains %}
{% if alias_domain_data.dkim %} {% if alias_domain_data.dkim %}
<div class="row collapse in dkim_key_valid"> <div class="row collapse in dkim_key_valid">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="{{ alias_domain }}"></div> <div class="col-md-1"><input type="checkbox" class="form-check-input" data-id="dkim" name="multi_select" value="{{ alias_domain }}"></div>
<div class="col-md-2 offset-md-1"> <div class="col-md-2 offset-md-1">
<p><small>↳ Alias-Domain: <strong>{{ alias_domain }}</strong></small> <p><small>↳ Alias-Domain: <strong>{{ alias_domain }}</strong></small>
<p class="dkim-label"><span class="badge fs-6 bg-success">{{ lang.admin.dkim_key_valid }}</span></p> <p class="dkim-label"><span class="badge fs-6 bg-success">{{ lang.admin.dkim_key_valid }}</span></p>
@@ -78,7 +78,7 @@
{% endfor %} {% endfor %}
{% for blind, data in dkim_blind_domains|filter(data => data.dkim is not null) %} {% for blind, data in dkim_blind_domains|filter(data => data.dkim is not null) %}
<div class="row collapse in dkim_key_unused"> <div class="row collapse in dkim_key_unused">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="{{ blind }}"></div> <div class="col-md-1"><input type="checkbox" class="form-check-input" data-id="dkim" name="multi_select" value="{{ blind }}"></div>
<div class="col-md-3"> <div class="col-md-3">
<p>{{ lang.admin.domain }}: <strong>{{ blind }}</strong> <p>{{ lang.admin.domain }}: <strong>{{ blind }}</strong>
<p class="dkim-label"><span class="badge fs-6 bg-warning">{{ lang.admin.dkim_key_unused }}</span></p> <p class="dkim-label"><span class="badge fs-6 bg-warning">{{ lang.admin.dkim_key_unused }}</span></p>
@@ -114,7 +114,7 @@
</div> </div>
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12 col-md-6 col-lg-4 col-xl-3"> <div class="col-12 col-md-6 col-lg-4 col-xl-3">
<select data-style="btn btn-secondary btn-sm" class="form-control" id="key_size" name="key_size" title="{{ lang.admin.dkim_key_length }}" required> <select data-style="btn btn-light btn-sm" class="form-control" id="key_size" name="key_size" title="{{ lang.admin.dkim_key_length }}" required>
<option data-subtext="bits">1024</option> <option data-subtext="bits">1024</option>
<option data-subtext="bits">2048</option> <option data-subtext="bits">2048</option>
</select> </select>
@@ -143,7 +143,7 @@
</div> </div>
<div class="mb-2"> <div class="mb-2">
<label> <label>
<input type="checkbox" name="overwrite_existing" value="1"> {{ lang.admin.dkim_overwrite_key }} <input type="checkbox" class="form-check-input" name="overwrite_existing" value="1"> {{ lang.admin.dkim_overwrite_key }}
</label> </label>
</div> </div>
<button class="btn btn-sm d-block d-sm-inline btn-secondary" data-action="add_item" data-id="dkim_import" data-api-url='add/dkim_import' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> {{ lang.admin.import }}</button> <button class="btn btn-sm d-block d-sm-inline btn-secondary" data-action="add_item" data-id="dkim_import" data-api-url='add/dkim_import' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> {{ lang.admin.import }}</button>
@@ -159,7 +159,7 @@
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-sm-2 text-sm-end" for="from_domain">{{ lang.admin.dkim_from }}:</label> <label class="control-label col-sm-2 text-sm-end" for="from_domain">{{ lang.admin.dkim_from }}:</label>
<div class="col-sm-10 col-md-6 col-lg-4 col-xl-3"> <div class="col-sm-10 col-md-6 col-lg-4 col-xl-3">
<select data-style="btn btn-secondary btn-sm" <select data-style="btn btn-light btn-sm"
data-live-search="true" data-live-search="true"
data-id="dkim_duplicate" data-id="dkim_duplicate"
title="{{ lang.admin.dkim_from_title }}" title="{{ lang.admin.dkim_from_title }}"
@@ -175,7 +175,7 @@
<div class="col-sm-10 col-md-6 col-lg-4 col-xl-3"> <div class="col-sm-10 col-md-6 col-lg-4 col-xl-3">
<select <select
data-live-search="true" data-live-search="true"
data-style="btn btn-secondary btn-sm" data-style="btn btn-light btn-sm"
data-id="dkim_duplicate" data-id="dkim_duplicate"
title="{{ lang.admin.dkim_to_title }}" title="{{ lang.admin.dkim_to_title }}"
name="to_domain" id="to_domain" class="full-width-select form-control" multiple required> name="to_domain" id="to_domain" class="full-width-select form-control" multiple required>

View File

@@ -1,7 +1,7 @@
<div role="tabpanel" class="tab-pane fade" id="tab-config-f2b" role="tabpanel" aria-labelledby="tab-config-f2b"> <div class="tab-pane fade" id="tab-config-f2b" role="tabpanel" aria-labelledby="tab-config-f2b">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex fs-5"> <div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-f2b" data-bs-toggle="collapse" aria-controls="ollapse-tab-config-f2b"> <button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-f2b" data-bs-toggle="collapse" aria-controls="collapse-tab-config-f2b">
{{ lang.admin.f2b_parameters }} {{ lang.admin.f2b_parameters }}
</button> </button>
<span class="d-none d-md-block">{{ lang.admin.f2b_parameters }}</span> <span class="d-none d-md-block">{{ lang.admin.f2b_parameters }}</span>
@@ -108,12 +108,14 @@
</form> </form>
{% for active_ban in f2b_data.active_bans %} {% for active_ban in f2b_data.active_bans %}
<p> <p>
<span class="badge fs-5 bg-info" style="padding:4px;font-size:85%;"> <span class="badge fs-7 bg-info d-block d-sm-inline-block">
<i class="bi bi-funnel-fill"></i> <i class="bi bi-funnel-fill"></i>
<a href="https://bgp.he.net/ip/{{ active_ban.ip }}" target="_blank" style="color:white"> <a href="https://bgp.he.net/ip/{{ active_ban.ip }}" target="_blank">
{{ active_ban.network }} {{ active_ban.network }}
</a> </a>
({{ active_ban.banned_until }}) - ({{ active_ban.banned_until }})
</span>
<span class="d-none d-sm-inline"> - </span>
{% if active_ban.queued_for_unban == 0 %} {% if active_ban.queued_for_unban == 0 %}
<a data-action="edit_selected" data-item="{{ active_ban.network }}" data-id="f2b-quick" data-api-url='edit/fail2ban' data-api-attr='{"action":"unban"}' href="#">[{{ lang.admin.queue_unban }}]</a> <a data-action="edit_selected" data-item="{{ active_ban.network }}" data-id="f2b-quick" data-api-url='edit/fail2ban' data-api-attr='{"action":"unban"}' href="#">[{{ lang.admin.queue_unban }}]</a>
<a data-action="edit_selected" data-item="{{ active_ban.network }}" data-id="f2b-quick" data-api-url='edit/fail2ban' data-api-attr='{"action":"whitelist"}' href="#">[whitelist]</a> <a data-action="edit_selected" data-item="{{ active_ban.network }}" data-id="f2b-quick" data-api-url='edit/fail2ban' data-api-attr='{"action":"whitelist"}' href="#">[whitelist]</a>
@@ -121,15 +123,14 @@
{% else %} {% else %}
<i>{{ lang.admin.unban_pending }}</i> <i>{{ lang.admin.unban_pending }}</i>
{% endif %} {% endif %}
</span>
</p> </p>
{% endfor %} {% endfor %}
<hr> <hr>
{% for perm_ban in f2b_data.perm_bans %} {% for perm_ban in f2b_data.perm_bans %}
<p> <p>
<span class="badge fs-5 bg-danger" style="padding: 0.1em 0.4em 0.1em;"> <span class="badge fs-7 bg-danger d-block d-sm-inline-block">
<i class="bi bi-funnel-fill"></i> <i class="bi bi-funnel-fill"></i>
<a href="https://bgp.he.net/ip/{{ perm_ban.ip }}" target="_blank" style="color:white"> <a href="https://bgp.he.net/ip/{{ perm_ban.ip }}" target="_blank">
{{ perm_ban.network }} {{ perm_ban.network }}
</a> </a>
</span> </span>

View File

@@ -1,7 +1,7 @@
<div role="tabpanel" class="tab-pane fade" id="tab-config-fwdhosts" role="tabpanel" aria-labelledby="tab-config-fwdhosts"> <div class="tab-pane fade" id="tab-config-fwdhosts" role="tabpanel" aria-labelledby="tab-config-fwdhosts">
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex fs-5"> <div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-fwdhosts" data-bs-toggle="collapse" aria-controls="ollapse-tab-config-fwdhosts"> <button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-config-fwdhosts" data-bs-toggle="collapse" aria-controls="collapse-tab-config-fwdhosts">
{{ lang.admin.forwarding_hosts }} {{ lang.admin.forwarding_hosts }}
</button> </button>
<span class="d-none d-md-block">{{ lang.admin.forwarding_hosts }}</span> <span class="d-none d-md-block">{{ lang.admin.forwarding_hosts }}</span>
@@ -9,7 +9,7 @@
<div id="collapse-tab-config-fwdhosts" class="card-body collapse" data-bs-parent="#admin-content"> <div id="collapse-tab-config-fwdhosts" class="card-body collapse" data-bs-parent="#admin-content">
<p style="margin-bottom:40px">{{ lang.admin.forwarding_hosts_hint }}</p> <p style="margin-bottom:40px">{{ lang.admin.forwarding_hosts_hint }}</p>
<table id="forwardinghoststable" class="table table-striped dt-responsive w-100"></table> <table id="forwardinghoststable" class="table table-striped dt-responsive w-100"></table>
<div class="mass-actions-admin"> <div class="mass-actions-admin mb-4">
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<button type="button" id="toggle_multi_select_all" data-id="fwdhosts" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary">{{ lang.mailbox.toggle_all }}</button> <button type="button" id="toggle_multi_select_all" data-id="fwdhosts" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary">{{ lang.mailbox.toggle_all }}</button>
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a> <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a>

Some files were not shown because too many files have changed in this diff Show More