mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-10-28 13:05:42 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5590dac96 |
No files matched your search
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||
uses: actions/stale@v11.0.0
|
||||
uses: actions/stale@v10.2.0
|
||||
with:
|
||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||
days-before-stale: 60
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
- "watchdog-mailcow"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup Docker
|
||||
run: |
|
||||
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
||||
|
||||
@@ -8,11 +8,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run the Action
|
||||
uses: devops-infra/action-pull-request@v1.4.0
|
||||
uses: devops-infra/action-pull-request@v1.0.2
|
||||
with:
|
||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Generate postscreen_access.cidr
|
||||
run: |
|
||||
|
||||
@@ -64,8 +64,6 @@ data/web/templates/cache/*
|
||||
!data/web/templates/cache/.gitkeep
|
||||
data/web/.well-known/acme-challenge
|
||||
data/web/css/build/0081-custom-mailcow.css
|
||||
data/web/css/themes/*/
|
||||
!data/web/css/themes/servercow/
|
||||
data/web/inc/vars.local.inc.php
|
||||
data/web/inc/app_info.inc.php
|
||||
data/web/nextcloud*/
|
||||
|
||||
@@ -23,15 +23,10 @@ A big thank you to everyone supporting us on GitHub Sponsors—your contribution
|
||||
<a href="https://www.maehdros.com/" target=_blank><img
|
||||
src="https://avatars.githubusercontent.com/u/173894712" height="58"
|
||||
/></a>
|
||||
<a href="https://garske-systems.de/" target=_blank><img
|
||||
src="https://garske-systems.de/img/logo_min.png" height="58"
|
||||
/></a>
|
||||
|
||||
### 50$/Month Sponsors
|
||||
<a href="https://github.com/vnukhr" target=_blank><img
|
||||
src="https://avatars.githubusercontent.com/u/7805987?s=52&v=4" height="58"
|
||||
/></a><a href="https://github.com/humiico" target=_blank><img
|
||||
src="https://avatars.githubusercontent.com/u/110134705?s=52&v=4" height="58"
|
||||
/></a>
|
||||
|
||||
## Info, documentation and support
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.23
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -249,45 +249,14 @@ while true; do
|
||||
done <<< "${SQL_DOMAINS}"
|
||||
|
||||
if [[ ${ONLY_MAILCOW_HOSTNAME} != "y" ]]; then
|
||||
# Fetch all domains with an active MTA-STS policy once.
|
||||
unset MTA_STS_ACTIVE_DOMAINS
|
||||
declare -A MTA_STS_ACTIVE_DOMAINS
|
||||
if [[ ${AUTODISCOVER_SAN} == "y" ]]; then
|
||||
SQL_MTA_STS_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM mta_sts WHERE active = 1" -Bs)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
while read mta_sts_domain; do
|
||||
if [[ -z "${mta_sts_domain}" ]]; then
|
||||
# ignore empty lines
|
||||
continue
|
||||
fi
|
||||
MTA_STS_ACTIVE_DOMAINS["${mta_sts_domain}"]=1
|
||||
done <<< "${SQL_MTA_STS_DOMAINS}"
|
||||
fi
|
||||
fi
|
||||
for SQL_DOMAIN in "${SQL_DOMAIN_ARR[@]}"; do
|
||||
unset VALIDATED_CONFIG_DOMAINS_SUBDOMAINS
|
||||
declare -a VALIDATED_CONFIG_DOMAINS_SUBDOMAINS
|
||||
for SUBDOMAIN in "${ADDITIONAL_WC_ARR[@]}"; do
|
||||
FULL_SUBDOMAIN="${SUBDOMAIN}.${SQL_DOMAIN}"
|
||||
|
||||
# Skip mta-sts subdomain unless MTA-STS is enabled (active) for this domain
|
||||
if [[ "${SUBDOMAIN}" == "mta-sts" && -z "${MTA_STS_ACTIVE_DOMAINS[${SQL_DOMAIN}]}" ]]; then
|
||||
log_f "MTA-STS is not enabled for ${SQL_DOMAIN} - skipping mta-sts subdomain certificate"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Skip if subdomain matches MAILCOW_HOSTNAME
|
||||
if [[ "${FULL_SUBDOMAIN}" == "${MAILCOW_HOSTNAME}" ]]; then
|
||||
continue
|
||||
fi
|
||||
# Skip if subdomain is covered by a wildcard in ADDITIONAL_SAN
|
||||
if is_covered_by_wildcard "${FULL_SUBDOMAIN}"; then
|
||||
log_f "Subdomain '${FULL_SUBDOMAIN}' is covered by wildcard - skipping explicit subdomain"
|
||||
continue
|
||||
fi
|
||||
# Validate and add subdomain
|
||||
if check_domain "${FULL_SUBDOMAIN}"; then
|
||||
VALIDATED_CONFIG_DOMAINS_SUBDOMAINS+=("${FULL_SUBDOMAIN}")
|
||||
if [[ "${SUBDOMAIN}.${SQL_DOMAIN}" != "${MAILCOW_HOSTNAME}" ]]; then
|
||||
if check_domain "${SUBDOMAIN}.${SQL_DOMAIN}"; then
|
||||
VALIDATED_CONFIG_DOMAINS_SUBDOMAINS+=("${SUBDOMAIN}.${SQL_DOMAIN}")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
VALIDATED_CONFIG_DOMAINS+=("${VALIDATED_CONFIG_DOMAINS_SUBDOMAINS[*]}")
|
||||
@@ -304,10 +273,7 @@ while true; do
|
||||
fi
|
||||
# Only add mta-sts subdomain for alias domains
|
||||
if [[ "mta-sts.${alias_domain}" != "${MAILCOW_HOSTNAME}" ]]; then
|
||||
# Skip if mta-sts subdomain is covered by a wildcard
|
||||
if is_covered_by_wildcard "mta-sts.${alias_domain}"; then
|
||||
log_f "Alias domain mta-sts subdomain 'mta-sts.${alias_domain}' is covered by wildcard - skipping"
|
||||
elif check_domain "mta-sts.${alias_domain}"; then
|
||||
if check_domain "mta-sts.${alias_domain}"; then
|
||||
VALIDATED_CONFIG_DOMAINS+=("mta-sts.${alias_domain}")
|
||||
fi
|
||||
fi
|
||||
@@ -342,31 +308,13 @@ while true; do
|
||||
done
|
||||
fi
|
||||
|
||||
# Check if MAILCOW_HOSTNAME is covered by a wildcard in ADDITIONAL_SAN
|
||||
MAILCOW_HOSTNAME_COVERED=0
|
||||
if [[ ! -z ${VALIDATED_MAILCOW_HOSTNAME} ]]; then
|
||||
if is_covered_by_wildcard "${VALIDATED_MAILCOW_HOSTNAME}"; then
|
||||
MAILCOW_PARENT_DOMAIN=$(echo ${VALIDATED_MAILCOW_HOSTNAME} | cut -d. -f2-)
|
||||
log_f "MAILCOW_HOSTNAME '${VALIDATED_MAILCOW_HOSTNAME}' is covered by wildcard '*.${MAILCOW_PARENT_DOMAIN}' - skipping explicit hostname"
|
||||
MAILCOW_HOSTNAME_COVERED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Unique domains for server certificate
|
||||
if [[ ${ENABLE_SSL_SNI} == "y" ]]; then
|
||||
# create certificate for server name and fqdn SANs only
|
||||
if [[ ${MAILCOW_HOSTNAME_COVERED} == "1" ]]; then
|
||||
SERVER_SAN_VALIDATED=($(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
else
|
||||
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
fi
|
||||
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
else
|
||||
# create certificate for all domains, including all subdomains from other domains [*]
|
||||
if [[ ${MAILCOW_HOSTNAME_COVERED} == "1" ]]; then
|
||||
SERVER_SAN_VALIDATED=($(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
else
|
||||
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
fi
|
||||
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
|
||||
fi
|
||||
if [[ ! -z ${SERVER_SAN_VALIDATED[*]} ]]; then
|
||||
CERT_NAME=${SERVER_SAN_VALIDATED[0]}
|
||||
|
||||
@@ -135,32 +135,3 @@ verify_challenge_path(){
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if a domain is covered by a wildcard (*.example.com) in ADDITIONAL_SAN
|
||||
# Usage: is_covered_by_wildcard "subdomain.example.com"
|
||||
# Returns: 0 if covered, 1 if not covered
|
||||
# Note: Only returns 0 (covered) when DNS-01 challenge is enabled,
|
||||
# as wildcards cannot be validated with HTTP-01 challenge
|
||||
is_covered_by_wildcard() {
|
||||
local DOMAIN=$1
|
||||
|
||||
# Only skip if DNS challenge is enabled (wildcards require DNS-01)
|
||||
if [[ ${ACME_DNS_CHALLENGE} != "y" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Return early if no ADDITIONAL_SAN is set
|
||||
if [[ -z ${ADDITIONAL_SAN} ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract parent domain (e.g., mail.example.com -> example.com)
|
||||
local PARENT_DOMAIN=$(echo ${DOMAIN} | cut -d. -f2-)
|
||||
|
||||
# Check if ADDITIONAL_SAN contains a wildcard for this parent domain
|
||||
if [[ "${ADDITIONAL_SAN}" == *"*.${PARENT_DOMAIN}"* ]]; then
|
||||
return 0 # Covered by wildcard
|
||||
fi
|
||||
|
||||
return 1 # Not covered
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM alpine:3.24 AS builder
|
||||
FROM alpine:3.21 AS builder
|
||||
|
||||
WORKDIR /src
|
||||
ENV CLAMD_VERSION=1.4.6
|
||||
ENV CLAMD_VERSION=1.4.2
|
||||
|
||||
RUN apk upgrade --no-cache \
|
||||
&& apk add --update --no-cache \
|
||||
@@ -68,7 +68,7 @@ RUN wget -P /src https://www.clamav.net/downloads/production/clamav-${CLAMD_VERS
|
||||
"/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || exit 1
|
||||
|
||||
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.21
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.23
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,36 +1,4 @@
|
||||
# Alpine >= 3.22 only ships Dovecot 2.4, Dovecot 2.3 is built from the Alpine 3.21 packaging
|
||||
FROM alpine:3.24 AS builder
|
||||
|
||||
# Last commit of the aports 3.21-stable branch
|
||||
ARG APORTS_COMMIT=55126a11a4b9dcd14de3068e3348e9951d144ca6
|
||||
|
||||
RUN apk add --no-cache alpine-sdk git \
|
||||
&& git init -q /aports \
|
||||
&& cd /aports \
|
||||
&& git remote add origin https://github.com/alpinelinux/aports.git \
|
||||
&& git sparse-checkout set main/dovecot community/dovecot-fts-flatcurve \
|
||||
&& git fetch -q --depth 1 --filter=blob:none origin ${APORTS_COMMIT} \
|
||||
&& git checkout -q FETCH_HEAD \
|
||||
&& abuild-keygen -a -n \
|
||||
&& cp /root/.abuild/*.rsa.pub /etc/apk/keys/ \
|
||||
&& apk update \
|
||||
&& cd /aports/main/dovecot \
|
||||
# newer gettext does not overwrite the shipped config.rpath without -f
|
||||
&& sed -i 's/autoreconf -vi/autoreconf -fvi/' APKBUILD \
|
||||
&& abuild -F -r \
|
||||
# flatcurve must be built against the Dovecot 2.3 headers
|
||||
&& apk add --no-cache /root/packages/main/*/dovecot-2*.apk /root/packages/main/*/dovecot-dev-*.apk \
|
||||
&& cd /aports/community/dovecot-fts-flatcurve \
|
||||
&& abuild -F -r \
|
||||
&& mkdir /packages \
|
||||
&& cp /root/.abuild/*.rsa.pub /packages/ \
|
||||
&& for pkg in dovecot dovecot-dev dovecot-lmtpd dovecot-lua dovecot-ldap dovecot-mysql dovecot-sql \
|
||||
dovecot-submissiond dovecot-pigeonhole-plugin dovecot-pop3d dovecot-fts-flatcurve; do \
|
||||
cp /root/packages/*/*/${pkg}-[0-9]*.apk /packages/; \
|
||||
done
|
||||
|
||||
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.21
|
||||
|
||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
@@ -39,10 +7,6 @@ ARG GOSU_VERSION=1.19
|
||||
|
||||
ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
# MariaDB Connector/C >= 3.4 refuses DB servers without TLS, this covers Perl DBI, client.cnf covers Dovecot
|
||||
ENV MARIADB_TLS_DISABLE_PEER_VERIFICATION=1
|
||||
|
||||
COPY --from=builder /packages /tmp/packages
|
||||
|
||||
# Add groups and users before installing Dovecot to not break compatibility
|
||||
RUN addgroup -g 5000 vmail \
|
||||
@@ -54,7 +18,6 @@ RUN addgroup -g 5000 vmail \
|
||||
&& adduser -D -u 5000 -G vmail -h /var/vmail vmail \
|
||||
&& adduser -D -G dovecot -u 401 -h /dev/null -s /sbin/nologin dovecot \
|
||||
&& adduser -D -G dovenull -u 402 -h /dev/null -s /sbin/nologin dovenull \
|
||||
&& cp /tmp/packages/*.rsa.pub /etc/apk/keys/ \
|
||||
&& apk add --no-cache --update \
|
||||
bash \
|
||||
bind-tools \
|
||||
@@ -136,15 +99,22 @@ RUN addgroup -g 5000 vmail \
|
||||
supervisor \
|
||||
tzdata \
|
||||
wget \
|
||||
/tmp/packages/*.apk \
|
||||
&& rm -rf /tmp/packages \
|
||||
dovecot \
|
||||
dovecot-dev \
|
||||
dovecot-lmtpd \
|
||||
dovecot-lua \
|
||||
dovecot-ldap \
|
||||
dovecot-mysql \
|
||||
dovecot-sql \
|
||||
dovecot-submissiond \
|
||||
dovecot-pigeonhole-plugin \
|
||||
dovecot-pop3d \
|
||||
dovecot-fts-flatcurve \
|
||||
&& arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
|
||||
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$arch" \
|
||||
&& chmod +x /usr/local/bin/gosu \
|
||||
&& gosu nobody true
|
||||
|
||||
# Dovecot's MySQL driver reads [client] from /etc/my.cnf, Connector/C ignores the !includedir there
|
||||
COPY client.cnf /etc/my.cnf
|
||||
COPY trim_logs.sh /usr/local/bin/trim_logs.sh
|
||||
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
|
||||
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[client]
|
||||
ssl = false
|
||||
ssl-verify-server-cert = false
|
||||
@@ -47,7 +47,7 @@ try:
|
||||
if max_score == "":
|
||||
max_score = 9999.0
|
||||
|
||||
def query_mysql(query, params = None, headers = True, update = False):
|
||||
def query_mysql(query, headers = True, update = False):
|
||||
while True:
|
||||
try:
|
||||
cnx = MySQLdb.connect(user=os.environ.get('DBUSER'), password=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci")
|
||||
@@ -57,10 +57,7 @@ try:
|
||||
else:
|
||||
break
|
||||
cur = cnx.cursor()
|
||||
if params:
|
||||
cur.execute(query, params)
|
||||
else:
|
||||
cur.execute(query)
|
||||
cur.execute(query)
|
||||
if not update:
|
||||
result = []
|
||||
columns = tuple( [d[0] for d in cur.description] )
|
||||
@@ -79,7 +76,7 @@ try:
|
||||
|
||||
def notify_rcpt(rcpt, msg_count, quarantine_acl, category):
|
||||
if category == "add_header": category = "add header"
|
||||
meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = %s AND score < %s AND (action = %s OR "all" = %s)', (rcpt, max_score, category, category))
|
||||
meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category))
|
||||
print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count))
|
||||
if len(meta_query) == 0:
|
||||
return
|
||||
@@ -133,7 +130,7 @@ try:
|
||||
server.sendmail(msg['From'], [str(redirect)] + [str(bcc)], text)
|
||||
server.quit()
|
||||
for res in meta_query:
|
||||
query_mysql('UPDATE quarantine SET notified = 1 WHERE id = %s', (res['id'],), update = True)
|
||||
query_mysql('UPDATE quarantine SET notified = 1 WHERE id = "%d"' % (res['id']), update = True)
|
||||
r.hset('Q_LAST_NOTIFIED', record['rcpt'], time_now)
|
||||
break
|
||||
except Exception as ex:
|
||||
@@ -141,7 +138,7 @@ try:
|
||||
print('%s' % (ex))
|
||||
time.sleep(3)
|
||||
|
||||
records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %s AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt', (max_score,))
|
||||
records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %f AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt' % (max_score))
|
||||
|
||||
for record in records:
|
||||
attrs = ''
|
||||
@@ -159,7 +156,7 @@ try:
|
||||
except Exception as ex:
|
||||
print('Could not determine last notification for %s, assuming never' % (record['rcpt']))
|
||||
last_notification = 0
|
||||
attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = %s', (record['rcpt'],))
|
||||
attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = "%s"' % (record['rcpt']))
|
||||
attrs = attrs_json[0]['attributes']
|
||||
if isinstance(attrs, str):
|
||||
# if attr is str then just load it
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.23
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM nginx:1.30.6-alpine
|
||||
FROM nginx:alpine
|
||||
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ENV PIP_BREAK_SYSTEM_PACKAGES=1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.21
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ logger.debug('olefy listen address string: {} (type {})'.format(olefy_listen_add
|
||||
if not olefy_listen_addr_string:
|
||||
olefy_listen_addr = ""
|
||||
else:
|
||||
addr_re = re.compile(r'[\[" \]]')
|
||||
addr_re = re.compile('[\[" \]]')
|
||||
olefy_listen_addr = addr_re.sub('', olefy_listen_addr_string.replace("'", "")).split(',')
|
||||
|
||||
# log runtime variables
|
||||
@@ -206,9 +206,8 @@ class AIO(asyncio.Protocol):
|
||||
self.transport.close()
|
||||
|
||||
|
||||
# start the listeners, Python >= 3.14 no longer creates a loop in get_event_loop()
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
# start the listeners
|
||||
loop = asyncio.get_event_loop()
|
||||
# each client connection will create a new protocol instance
|
||||
coro = loop.create_server(AIO, olefy_listen_addr, olefy_listen_port)
|
||||
server = loop.run_until_complete(coro)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM php:8.2-fpm-alpine3.24
|
||||
FROM php:8.2-fpm-alpine3.21
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
@@ -7,13 +7,13 @@ ARG APCU_PECL_VERSION=5.1.28
|
||||
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG IMAGICK_PECL_VERSION=3.8.1
|
||||
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
ARG MAILPARSE_PECL_VERSION=3.2.0
|
||||
ARG MAILPARSE_PECL_VERSION=3.1.9
|
||||
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
ARG MEMCACHED_PECL_VERSION=3.4.0
|
||||
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG REDIS_PECL_VERSION=6.3.0
|
||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG COMPOSER_VERSION=2.10.2
|
||||
ARG COMPOSER_VERSION=2.9.5
|
||||
|
||||
RUN apk add -U --no-cache autoconf \
|
||||
aspell-dev \
|
||||
@@ -47,7 +47,6 @@ RUN apk add -U --no-cache autoconf \
|
||||
libressl-dev \
|
||||
librsvg \
|
||||
libtool \
|
||||
libwebp \
|
||||
libwebp-dev \
|
||||
libxml2-dev \
|
||||
libxpm \
|
||||
@@ -64,16 +63,13 @@ RUN apk add -U --no-cache autoconf \
|
||||
samba-client \
|
||||
zlib-dev \
|
||||
tzdata \
|
||||
&& docker-php-source extract \
|
||||
# PECL extensions from their GitHub tags (pecl.php.net is not reliable enough for builds)
|
||||
&& mkdir -p /usr/src/php/ext/apcu /usr/src/php/ext/imagick /usr/src/php/ext/mailparse /usr/src/php/ext/memcached /usr/src/php/ext/redis \
|
||||
&& curl -fsSL https://github.com/krakjoe/apcu/archive/refs/tags/v${APCU_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/apcu \
|
||||
&& curl -fsSL https://github.com/Imagick/imagick/archive/refs/tags/${IMAGICK_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/imagick \
|
||||
&& sed -i "s/@PACKAGE_VERSION@/${IMAGICK_PECL_VERSION}/" /usr/src/php/ext/imagick/php_imagick.h \
|
||||
&& curl -fsSL https://github.com/php/pecl-mail-mailparse/archive/refs/tags/v${MAILPARSE_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/mailparse \
|
||||
&& curl -fsSL https://github.com/php-memcached-dev/php-memcached/archive/refs/tags/v${MEMCACHED_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/memcached \
|
||||
&& curl -fsSL https://github.com/phpredis/phpredis/archive/refs/tags/${REDIS_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/redis \
|
||||
&& docker-php-ext-install -j 4 apcu imagick mailparse memcached redis \
|
||||
&& pecl install APCu-${APCU_PECL_VERSION} \
|
||||
&& pecl install imagick-${IMAGICK_PECL_VERSION} \
|
||||
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
||||
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
||||
&& pecl install redis-${REDIS_PECL_VERSION} \
|
||||
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
||||
&& pecl clear-cache \
|
||||
&& docker-php-ext-configure intl \
|
||||
&& docker-php-ext-configure exif \
|
||||
&& docker-php-ext-configure gd --with-freetype=/usr/include/ \
|
||||
@@ -87,7 +83,6 @@ RUN apk add -U --no-cache autoconf \
|
||||
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
|
||||
&& mv composer.phar /usr/local/bin/composer \
|
||||
&& chmod +x /usr/local/bin/composer \
|
||||
&& docker-php-source delete \
|
||||
&& apk del --purge autoconf \
|
||||
aspell-dev \
|
||||
cyrus-sasl-dev \
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
FROM golang:1.26-trixie AS builder
|
||||
FROM golang:1.25-bookworm AS builder
|
||||
WORKDIR /src
|
||||
|
||||
ENV CGO_ENABLED=0 \
|
||||
GO111MODULE=on \
|
||||
NOOPT=1 \
|
||||
VERSION=1.14.0
|
||||
VERSION=1.8.22
|
||||
|
||||
RUN git clone --branch v${VERSION} https://github.com/Zuplu/postfix-tlspol && \
|
||||
cd /src/postfix-tlspol && \
|
||||
scripts/build.sh build-only
|
||||
|
||||
|
||||
FROM debian:trixie-slim
|
||||
FROM debian:bookworm-slim
|
||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@version: 4.8
|
||||
@version: 3.38
|
||||
@include "scl.conf"
|
||||
options {
|
||||
chain_hostnames(off);
|
||||
@@ -7,7 +7,7 @@ options {
|
||||
dns_cache(no);
|
||||
use_fqdn(no);
|
||||
owner("root"); group("adm"); perm(0640);
|
||||
stats(freq(0));
|
||||
stats_freq(0);
|
||||
bad_hostname("^gconfd$");
|
||||
};
|
||||
source s_src {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@version: 4.8
|
||||
@version: 3.38
|
||||
@include "scl.conf"
|
||||
options {
|
||||
chain_hostnames(off);
|
||||
@@ -7,7 +7,7 @@ options {
|
||||
dns_cache(no);
|
||||
use_fqdn(no);
|
||||
owner("root"); group("adm"); perm(0640);
|
||||
stats(freq(0));
|
||||
stats_freq(0);
|
||||
bad_hostname("^gconfd$");
|
||||
};
|
||||
source s_src {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM debian:trixie-slim
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@version: 4.8
|
||||
@version: 3.38
|
||||
@include "scl.conf"
|
||||
options {
|
||||
chain_hostnames(off);
|
||||
@@ -7,7 +7,7 @@ options {
|
||||
dns_cache(no);
|
||||
use_fqdn(no);
|
||||
owner("root"); group("adm"); perm(0640);
|
||||
stats(freq(0));
|
||||
stats_freq(0);
|
||||
bad_hostname("^gconfd$");
|
||||
};
|
||||
source s_src {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@version: 4.8
|
||||
@version: 3.38
|
||||
@include "scl.conf"
|
||||
options {
|
||||
chain_hostnames(off);
|
||||
@@ -7,7 +7,7 @@ options {
|
||||
dns_cache(no);
|
||||
use_fqdn(no);
|
||||
owner("root"); group("adm"); perm(0640);
|
||||
stats(freq(0));
|
||||
stats_freq(0);
|
||||
bad_hostname("^gconfd$");
|
||||
};
|
||||
source s_src {
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM debian:trixie-slim
|
||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG RSPAMD_VER=rspamd_4.2.1-1~b7a16ae
|
||||
ARG RSPAMD_VER=rspamd_3.14.3-1~236eb65
|
||||
ARG CODENAME=trixie
|
||||
ENV LC_ALL=C
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SOGo built from source to enable security patch application
|
||||
# Repository: https://github.com/Alinto/sogo
|
||||
# Version: SOGo-5.12.12
|
||||
# Version: SOGo-5.12.5
|
||||
#
|
||||
# Applied security patches:
|
||||
# -
|
||||
@@ -12,8 +12,8 @@ FROM debian:bookworm
|
||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG SOGO_VERSION=SOGo-5.12.12
|
||||
ARG SOPE_VERSION=SOPE-5.12.12
|
||||
ARG SOGO_VERSION=SOGo-5.12.5
|
||||
ARG SOPE_VERSION=SOPE-5.12.5
|
||||
# Security patches to apply (space-separated commit hashes)
|
||||
ARG SOGO_SECURITY_PATCHES=""
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
|
||||
@@ -161,6 +161,10 @@ RUN ln -s /usr/local/sbin/sogod /usr/sbin/sogod \
|
||||
&& ln -s /usr/local/sbin/sogo-ealarms-notify /usr/sbin/sogo-ealarms-notify \
|
||||
&& ln -s /usr/local/sbin/sogo-slapd-sockd /usr/sbin/sogo-slapd-sockd
|
||||
|
||||
# Create compatibility symlink for old SOGo documentation path
|
||||
# Allows volume mounts using /usr/lib/GNUstep to work with /usr/local/lib/GNUstep
|
||||
RUN ln -sf /usr/local/lib/GNUstep /usr/lib/GNUstep
|
||||
|
||||
# Copy configuration files and scripts
|
||||
COPY ./bootstrap-sogo.sh /bootstrap-sogo.sh
|
||||
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.23
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
# install unbound from alpine:edge to get security patches
|
||||
# minimum version is pinned
|
||||
RUN apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main "unbound>=1.26.2-r0"
|
||||
|
||||
# install other packages from regular alpine stable repo
|
||||
RUN apk add --update --no-cache \
|
||||
curl \
|
||||
bind-tools \
|
||||
coreutils \
|
||||
unbound \
|
||||
bash \
|
||||
openssl \
|
||||
drill \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.24
|
||||
FROM alpine:3.23
|
||||
|
||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
|
||||
@@ -32,17 +32,8 @@ function auth_password_verify(request, password)
|
||||
-- Returning PASSDB_RESULT_PASSWORD_MISMATCH will reset the user's auth cache entry.
|
||||
-- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
|
||||
-- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
|
||||
|
||||
-- On a network-level failure (nginx unreachable, DNS failure, timeout) https.request
|
||||
-- returns nil plus an error string, so c is not a numeric HTTP status code. Treat this
|
||||
-- as a backend outage and keep the cache entry, rather than wiping it as a mismatch.
|
||||
if type(c) ~= "number" then
|
||||
dovecot.i_info("HTTP request to auth backend failed with " .. tostring(c) .. " for user " .. request.user)
|
||||
return dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE, "Upstream unreachable"
|
||||
end
|
||||
|
||||
if c ~= 200 and c ~= 401 then
|
||||
dovecot.i_info("HTTP request failed with " .. tostring(c) .. " for user " .. request.user)
|
||||
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
|
||||
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# global_sieve_before script
|
||||
# global_sieve_before -> user sieve_before (mailcow UI) -> user sieve_after (mailcow UI) -> global_sieve_after
|
||||
|
||||
require ["mailbox", "fileinto"];
|
||||
|
||||
if header :contains ["Chat-Version"] [""] {
|
||||
if mailboxexists "DeltaChat" {
|
||||
fileinto "DeltaChat";
|
||||
} else {
|
||||
fileinto :create "DeltaChat";
|
||||
}
|
||||
stop;
|
||||
}
|
||||
@@ -42,19 +42,12 @@ http {
|
||||
https https;
|
||||
}
|
||||
|
||||
map $server_port $sogo_auth_internal {
|
||||
65510 "1";
|
||||
default "";
|
||||
}
|
||||
|
||||
{% if HTTP_REDIRECT %}
|
||||
# HTTP to HTTPS redirect
|
||||
server {
|
||||
root /web;
|
||||
listen {{ HTTP_PORT }} default_server;
|
||||
{% if ENABLE_IPV6 %}
|
||||
listen [::]:{{ HTTP_PORT }} default_server;
|
||||
{%endif%}
|
||||
|
||||
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* mta-sts.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
|
||||
|
||||
|
||||
@@ -116,9 +116,6 @@ location ~ \.php$ {
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param HTTP_X_REAL_IP $remote_addr;
|
||||
# trusted internal-auth marker; empty for external clients (see nginx.conf map)
|
||||
fastcgi_param SOGO_AUTH_INTERNAL $sogo_auth_internal;
|
||||
fastcgi_read_timeout 3600;
|
||||
fastcgi_send_timeout 3600;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
max_execution_time = 3600
|
||||
max_input_time = 3600
|
||||
memory_limit = 512M
|
||||
; not used by mailcow, disabled as hardening (PHP security release 2026-10-21)
|
||||
session.upload_progress.enabled = Off
|
||||
@@ -37,7 +37,7 @@ postscreen_access_list = permit_mynetworks,
|
||||
cidr:/opt/postfix/conf/postscreen_access.cidr,
|
||||
tcp:127.0.0.1:10027
|
||||
postscreen_bare_newline_enable = no
|
||||
postscreen_denylist_action = drop
|
||||
postscreen_blacklist_action = drop
|
||||
postscreen_cache_cleanup_interval = 24h
|
||||
postscreen_cache_map = proxy:btree:$data_directory/postscreen_cache
|
||||
postscreen_dnsbl_action = enforce
|
||||
@@ -107,6 +107,8 @@ smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch,
|
||||
reject_unknown_sender_domain
|
||||
smtpd_soft_error_limit = 3
|
||||
smtpd_tls_auth_only = yes
|
||||
smtpd_tls_dh1024_param_file = /etc/ssl/mail/dhparams.pem
|
||||
smtpd_tls_eecdh_grade = auto
|
||||
smtpd_tls_exclude_ciphers = ECDHE-RSA-RC4-SHA, RC4, aNULL, DES-CBC3-SHA, ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA
|
||||
smtpd_tls_loglevel = 1
|
||||
|
||||
@@ -150,7 +152,7 @@ smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
||||
smtp_sasl_security_options =
|
||||
smtp_sasl_mechanism_filter = plain, login
|
||||
smtp_tls_policy_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf socketmap:inet:postfix-tlspol:8642:QUERYwithTLSRPT
|
||||
smtp_tls_policy_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf socketmap:inet:postfix-tlspol:8642:QUERY
|
||||
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
|
||||
mail_name = Postcow
|
||||
# local_transport map catches local destinations and prevents routing local dests when the next map would route "*"
|
||||
|
||||
@@ -29,6 +29,7 @@ smtps inet n - n - - smtpd
|
||||
# TLS protocol can be modified by setting submission_smtpd_tls_mandatory_protocols in extra.cf
|
||||
submission inet n - n - - smtpd
|
||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||
-o smtpd_enforce_tls=yes
|
||||
-o smtpd_tls_security_level=encrypt
|
||||
-o smtpd_tls_mandatory_protocols=$submission_smtpd_tls_mandatory_protocols
|
||||
-o tls_preempt_cipherlist=yes
|
||||
@@ -37,6 +38,7 @@ submission inet n - n - - smtpd
|
||||
10587 inet n - n - - smtpd
|
||||
-o smtpd_upstream_proxy_protocol=haproxy
|
||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||
-o smtpd_enforce_tls=yes
|
||||
-o smtpd_tls_security_level=encrypt
|
||||
-o smtpd_tls_mandatory_protocols=$submission_smtpd_tls_mandatory_protocols
|
||||
-o tls_preempt_cipherlist=yes
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# Whitelist generated by Postwhite v3.4 on Thu Oct 1 00:35:20 UTC 2026
|
||||
# Whitelist generated by Postwhite v3.4 on Sun Mar 1 00:29:01 UTC 2026
|
||||
# https://github.com/stevejenkins/postwhite/
|
||||
# 2246 total rules
|
||||
# 2174 total rules
|
||||
2a00:1450:4000::/36 permit
|
||||
2a00:1450:4864::/56 permit
|
||||
2a01:111:f400::/48 permit
|
||||
2a01:111:f403:2800::/53 permit
|
||||
2a01:111:f403:8000::/51 permit
|
||||
@@ -29,15 +28,11 @@
|
||||
2a01:b747:3004:200::/56 permit
|
||||
2a01:b747:3005:200::/56 permit
|
||||
2a01:b747:3006:200::/56 permit
|
||||
2a01:b747:3007::/56 permit
|
||||
2a02:a60:0:5::/64 permit
|
||||
2a0f:f640::/56 permit
|
||||
2c0f:fb50:4000::/36 permit
|
||||
2c0f:fb50:4864::/56 permit
|
||||
2.207.151.32/27 permit
|
||||
2.207.151.53 permit
|
||||
2.207.217.30 permit
|
||||
2.207.223.160/27 permit
|
||||
3.64.237.68 permit
|
||||
3.65.3.180 permit
|
||||
3.70.123.177 permit
|
||||
@@ -58,14 +53,13 @@
|
||||
8.25.196.0/23 permit
|
||||
8.36.116.0/24 permit
|
||||
8.39.54.0/23 permit
|
||||
8.39.54.240/29 permit
|
||||
8.39.54.250/31 permit
|
||||
8.39.144.0/24 permit
|
||||
8.40.222.0/23 permit
|
||||
8.40.222.240/29 permit
|
||||
8.40.222.250/31 permit
|
||||
8.47.10.9 permit
|
||||
12.130.86.238 permit
|
||||
13.107.213.51 permit
|
||||
13.107.246.51 permit
|
||||
13.108.16.0/20 permit
|
||||
13.110.208.0/21 permit
|
||||
13.110.209.0/24 permit
|
||||
@@ -125,6 +119,7 @@
|
||||
23.249.208.0/20 permit
|
||||
23.251.224.0/19 permit
|
||||
23.253.141.0/24 permit
|
||||
23.253.182.0/23 permit
|
||||
23.253.182.103 permit
|
||||
23.253.183.145 permit
|
||||
23.253.183.146 permit
|
||||
@@ -146,7 +141,24 @@
|
||||
27.123.206.80/28 permit
|
||||
31.47.251.17 permit
|
||||
31.186.239.0/24 permit
|
||||
34.2.64.0/19 permit
|
||||
34.2.64.0/22 permit
|
||||
34.2.68.0/23 permit
|
||||
34.2.70.0/23 permit
|
||||
34.2.71.64/26 permit
|
||||
34.2.72.0/22 permit
|
||||
34.2.75.0/26 permit
|
||||
34.2.78.0/23 permit
|
||||
34.2.80.0/23 permit
|
||||
34.2.82.0/23 permit
|
||||
34.2.84.0/24 permit
|
||||
34.2.84.64/26 permit
|
||||
34.2.85.0/24 permit
|
||||
34.2.85.64/26 permit
|
||||
34.2.86.0/23 permit
|
||||
34.2.88.0/23 permit
|
||||
34.2.90.0/23 permit
|
||||
34.2.92.0/23 permit
|
||||
34.2.94.0/23 permit
|
||||
34.70.158.162 permit
|
||||
34.74.74.140 permit
|
||||
34.83.159.189 permit
|
||||
@@ -173,7 +185,6 @@
|
||||
35.205.92.9 permit
|
||||
35.228.216.85 permit
|
||||
35.242.169.159 permit
|
||||
37.187.220.204 permit
|
||||
37.188.97.188 permit
|
||||
37.218.248.47 permit
|
||||
37.218.249.47 permit
|
||||
@@ -247,8 +258,8 @@
|
||||
46.228.39.112/31 permit
|
||||
46.243.88.174 permit
|
||||
46.243.88.175 permit
|
||||
46.243.88.176/30 permit
|
||||
46.243.95.96/27 permit
|
||||
46.243.88.176 permit
|
||||
46.243.88.177 permit
|
||||
46.243.95.179 permit
|
||||
46.243.95.180 permit
|
||||
50.16.246.183 permit
|
||||
@@ -266,8 +277,6 @@
|
||||
50.56.130.221 permit
|
||||
50.56.130.222 permit
|
||||
50.112.246.219 permit
|
||||
50.152.146.25 permit
|
||||
51.83.17.38 permit
|
||||
52.1.14.157 permit
|
||||
52.5.230.59 permit
|
||||
52.6.74.205 permit
|
||||
@@ -358,9 +367,7 @@
|
||||
64.127.115.252 permit
|
||||
64.132.88.0/23 permit
|
||||
64.132.92.0/24 permit
|
||||
64.181.136.241 permit
|
||||
64.181.194.190 permit
|
||||
64.181.213.254 permit
|
||||
64.207.219.7 permit
|
||||
64.207.219.8 permit
|
||||
64.207.219.9 permit
|
||||
@@ -446,11 +453,7 @@
|
||||
66.218.75.252/31 permit
|
||||
66.218.75.254 permit
|
||||
66.220.144.128/25 permit
|
||||
66.220.144.178 permit
|
||||
66.220.144.179 permit
|
||||
66.220.155.0/24 permit
|
||||
66.220.155.178 permit
|
||||
66.220.155.179 permit
|
||||
66.220.157.0/25 permit
|
||||
66.231.80.0/20 permit
|
||||
66.240.227.0/24 permit
|
||||
@@ -515,8 +518,6 @@
|
||||
69.162.98.0/24 permit
|
||||
69.169.224.0/20 permit
|
||||
69.171.232.0/24 permit
|
||||
69.171.232.180 permit
|
||||
69.171.232.181 permit
|
||||
69.171.244.0/23 permit
|
||||
70.42.149.35 permit
|
||||
72.3.185.0/24 permit
|
||||
@@ -638,8 +639,6 @@
|
||||
77.238.189.148/30 permit
|
||||
79.135.106.0/24 permit
|
||||
79.135.107.0/24 permit
|
||||
80.225.160.128/25 permit
|
||||
81.88.38.0/23 permit
|
||||
81.169.146.243 permit
|
||||
81.169.146.245 permit
|
||||
81.169.146.246 permit
|
||||
@@ -658,9 +657,6 @@
|
||||
82.165.159.45 permit
|
||||
82.165.159.130 permit
|
||||
82.165.159.131 permit
|
||||
84.8.68.0/25 permit
|
||||
84.8.192.128/25 permit
|
||||
84.8.224.128/25 permit
|
||||
85.9.206.169 permit
|
||||
85.9.210.45 permit
|
||||
85.158.136.0/21 permit
|
||||
@@ -724,7 +720,6 @@
|
||||
96.43.148.64/28 permit
|
||||
96.43.148.64/31 permit
|
||||
96.43.151.64/28 permit
|
||||
98.77.0.0/16 permit
|
||||
98.97.248.0/21 permit
|
||||
98.136.44.181 permit
|
||||
98.136.44.182/31 permit
|
||||
@@ -1220,6 +1215,8 @@
|
||||
104.43.243.237 permit
|
||||
104.44.112.128/25 permit
|
||||
104.47.0.0/17 permit
|
||||
104.130.96.0/28 permit
|
||||
104.130.122.0/23 permit
|
||||
106.10.144.64/27 permit
|
||||
106.10.144.100/31 permit
|
||||
106.10.144.103 permit
|
||||
@@ -1372,7 +1369,6 @@
|
||||
117.120.16.0/21 permit
|
||||
119.42.242.52/31 permit
|
||||
119.42.242.156 permit
|
||||
121.244.91.31 permit
|
||||
121.244.91.48 permit
|
||||
121.244.91.52 permit
|
||||
122.15.156.182 permit
|
||||
@@ -1412,20 +1408,6 @@
|
||||
129.146.88.28 permit
|
||||
129.146.147.105 permit
|
||||
129.146.236.58 permit
|
||||
129.148.135.0/25 permit
|
||||
129.148.148.0/25 permit
|
||||
129.148.164.0/25 permit
|
||||
129.148.180.0/25 permit
|
||||
129.148.215.0/25 permit
|
||||
129.149.6.0/25 permit
|
||||
129.149.22.0/25 permit
|
||||
129.149.38.0/25 permit
|
||||
129.149.52.0/25 permit
|
||||
129.149.68.0/25 permit
|
||||
129.149.84.0/25 permit
|
||||
129.149.100.0/25 permit
|
||||
129.149.118.0/25 permit
|
||||
129.149.126.0/25 permit
|
||||
129.151.67.221 permit
|
||||
129.153.62.216 permit
|
||||
129.153.104.71 permit
|
||||
@@ -1438,21 +1420,18 @@
|
||||
129.159.22.159 permit
|
||||
129.159.87.137 permit
|
||||
129.213.195.191 permit
|
||||
130.35.116.0/25 permit
|
||||
130.61.9.72 permit
|
||||
130.162.39.83 permit
|
||||
130.162.194.23 permit
|
||||
130.248.172.0/24 permit
|
||||
130.248.173.0/24 permit
|
||||
131.186.12.0/25 permit
|
||||
131.253.30.0/24 permit
|
||||
131.253.121.0/26 permit
|
||||
132.145.13.209 permit
|
||||
132.226.26.225 permit
|
||||
132.226.49.32 permit
|
||||
132.226.56.24 permit
|
||||
134.98.248.128/25 permit
|
||||
134.128.64.0/18 permit
|
||||
134.128.64.0/19 permit
|
||||
134.128.96.0/19 permit
|
||||
134.170.27.8 permit
|
||||
134.170.113.0/26 permit
|
||||
134.170.141.64/26 permit
|
||||
@@ -1481,10 +1460,6 @@
|
||||
136.147.182.0/24 permit
|
||||
136.147.224.0/20 permit
|
||||
136.179.50.206 permit
|
||||
136.248.224.128/25 permit
|
||||
136.248.232.128/25 permit
|
||||
138.1.108.0/25 permit
|
||||
138.1.170.0/24 permit
|
||||
139.60.152.0/22 permit
|
||||
139.138.35.44 permit
|
||||
139.138.46.121 permit
|
||||
@@ -1493,7 +1468,6 @@
|
||||
139.138.57.55 permit
|
||||
139.138.58.119 permit
|
||||
139.167.79.86 permit
|
||||
139.177.108.0/25 permit
|
||||
139.180.17.0/24 permit
|
||||
140.238.148.191 permit
|
||||
141.148.55.217 permit
|
||||
@@ -1524,14 +1498,14 @@
|
||||
146.20.14.105 permit
|
||||
146.20.14.106 permit
|
||||
146.20.14.107 permit
|
||||
146.20.112.0/26 permit
|
||||
146.20.113.0/24 permit
|
||||
146.20.191.0/24 permit
|
||||
146.20.215.0/24 permit
|
||||
146.20.215.182 permit
|
||||
146.88.28.0/24 permit
|
||||
146.148.116.76 permit
|
||||
147.154.32.0/25 permit
|
||||
147.154.63.0/24 permit
|
||||
147.154.126.0/24 permit
|
||||
147.154.191.0/24 permit
|
||||
147.243.1.47 permit
|
||||
147.243.1.48 permit
|
||||
147.243.1.153 permit
|
||||
@@ -1539,23 +1513,16 @@
|
||||
147.243.128.26 permit
|
||||
148.105.0.0/16 permit
|
||||
148.105.8.0/21 permit
|
||||
148.116.32.128/25 permit
|
||||
149.72.0.0/16 permit
|
||||
149.72.234.184 permit
|
||||
149.72.248.236 permit
|
||||
149.97.173.180 permit
|
||||
149.118.160.128/25 permit
|
||||
150.136.21.199 permit
|
||||
150.171.109.178 permit
|
||||
150.230.98.160 permit
|
||||
151.145.38.14 permit
|
||||
152.67.105.195 permit
|
||||
152.69.165.251 permit
|
||||
152.69.200.236 permit
|
||||
152.70.155.126 permit
|
||||
155.248.135.128/25 permit
|
||||
155.248.140.0/25 permit
|
||||
155.248.148.0/25 permit
|
||||
155.248.208.51 permit
|
||||
155.248.220.138 permit
|
||||
155.248.234.149 permit
|
||||
@@ -1564,16 +1531,10 @@
|
||||
157.58.30.128/25 permit
|
||||
157.58.196.96/29 permit
|
||||
157.58.249.3 permit
|
||||
157.137.32.128/25 permit
|
||||
157.137.96.128/25 permit
|
||||
157.151.208.65 permit
|
||||
157.255.1.64/29 permit
|
||||
158.101.211.207 permit
|
||||
158.247.16.0/20 permit
|
||||
158.247.100.0/25 permit
|
||||
159.13.4.0/25 permit
|
||||
159.13.33.181 permit
|
||||
159.26.176.0/20 permit
|
||||
159.92.154.0/24 permit
|
||||
159.92.155.0/24 permit
|
||||
159.92.157.0/24 permit
|
||||
@@ -1601,15 +1562,12 @@
|
||||
159.183.121.182 permit
|
||||
159.183.129.172 permit
|
||||
160.1.62.192 permit
|
||||
161.33.94.110 permit
|
||||
161.33.228.244 permit
|
||||
161.38.192.0/20 permit
|
||||
161.38.204.0/22 permit
|
||||
161.71.32.0/19 permit
|
||||
161.71.64.0/20 permit
|
||||
162.88.4.0/23 permit
|
||||
162.88.8.0/24 permit
|
||||
162.88.24.0/23 permit
|
||||
162.88.24.0/24 permit
|
||||
162.88.25.0/24 permit
|
||||
162.88.36.0/24 permit
|
||||
@@ -1627,13 +1585,11 @@
|
||||
164.152.23.32 permit
|
||||
164.152.25.241 permit
|
||||
164.177.132.168/30 permit
|
||||
165.1.100.0/25 permit
|
||||
165.173.128.0/24 permit
|
||||
165.173.180.0/24 permit
|
||||
165.173.180.1 permit
|
||||
165.173.180.250/31 permit
|
||||
165.173.182.0/24 permit
|
||||
165.173.182.250/31 permit
|
||||
165.173.189.205 permit
|
||||
166.78.68.0/22 permit
|
||||
166.78.68.221 permit
|
||||
166.78.69.169 permit
|
||||
166.78.69.170 permit
|
||||
@@ -1651,12 +1607,9 @@
|
||||
167.89.75.126 permit
|
||||
167.89.75.136 permit
|
||||
167.89.75.164 permit
|
||||
167.89.101.2 permit
|
||||
167.89.101.192/28 permit
|
||||
167.220.67.232/29 permit
|
||||
168.107.248.128/25 permit
|
||||
168.110.160.128/25 permit
|
||||
168.110.248.128/25 permit
|
||||
168.129.184.128/25 permit
|
||||
168.129.248.128/25 permit
|
||||
168.138.5.36 permit
|
||||
168.138.73.51 permit
|
||||
168.138.77.31 permit
|
||||
@@ -1673,7 +1626,6 @@
|
||||
169.148.144.0/25 permit
|
||||
169.148.144.10 permit
|
||||
169.148.146.0/23 permit
|
||||
169.148.174.10 permit
|
||||
169.148.175.3 permit
|
||||
169.148.179.3 permit
|
||||
169.148.188.0/24 permit
|
||||
@@ -1692,7 +1644,6 @@
|
||||
173.203.81.39 permit
|
||||
173.224.161.128/25 permit
|
||||
173.224.165.0/26 permit
|
||||
173.224.166.48/28 permit
|
||||
174.36.84.8/29 permit
|
||||
174.36.84.16/29 permit
|
||||
174.36.84.32/29 permit
|
||||
@@ -1707,8 +1658,6 @@
|
||||
175.41.215.51 permit
|
||||
176.32.105.0/24 permit
|
||||
176.32.127.0/24 permit
|
||||
176.94.34.32/27 permit
|
||||
176.94.34.64/27 permit
|
||||
178.236.10.128/26 permit
|
||||
182.50.76.0/22 permit
|
||||
182.50.78.64/28 permit
|
||||
@@ -1790,36 +1739,26 @@
|
||||
188.125.85.234/31 permit
|
||||
188.125.85.236/31 permit
|
||||
188.125.85.238 permit
|
||||
188.165.51.139 permit
|
||||
188.172.128.0/20 permit
|
||||
192.0.64.0/18 permit
|
||||
192.9.166.184 permit
|
||||
192.18.139.154 permit
|
||||
192.18.145.36 permit
|
||||
192.18.152.58 permit
|
||||
192.22.32.128/25 permit
|
||||
192.22.96.128/25 permit
|
||||
192.22.160.128/25 permit
|
||||
192.22.224.128/25 permit
|
||||
192.28.128.0/18 permit
|
||||
192.29.24.0/25 permit
|
||||
192.29.44.0/25 permit
|
||||
192.29.72.0/25 permit
|
||||
192.29.88.0/25 permit
|
||||
192.29.103.128/25 permit
|
||||
192.29.134.0/25 permit
|
||||
192.29.151.128/25 permit
|
||||
192.29.172.0/25 permit
|
||||
192.29.178.0/25 permit
|
||||
192.29.200.0/25 permit
|
||||
192.29.216.0/25 permit
|
||||
192.29.232.0/25 permit
|
||||
192.29.248.0/25 permit
|
||||
192.30.252.0/22 permit
|
||||
192.161.144.0/20 permit
|
||||
192.162.87.0/24 permit
|
||||
192.237.158.0/23 permit
|
||||
192.237.159.42 permit
|
||||
192.237.159.43 permit
|
||||
192.254.112.0/20 permit
|
||||
192.254.112.60 permit
|
||||
192.254.112.98/31 permit
|
||||
192.254.113.10 permit
|
||||
192.254.113.101 permit
|
||||
192.254.114.176 permit
|
||||
193.109.254.0/23 permit
|
||||
193.122.128.100 permit
|
||||
193.123.56.63 permit
|
||||
@@ -1859,6 +1798,7 @@
|
||||
198.21.0.0/21 permit
|
||||
198.37.144.0/20 permit
|
||||
198.37.152.186 permit
|
||||
198.61.254.0/23 permit
|
||||
198.61.254.21 permit
|
||||
198.61.254.231 permit
|
||||
198.178.234.57 permit
|
||||
@@ -1945,7 +1885,6 @@
|
||||
204.14.232.0/21 permit
|
||||
204.14.232.64/28 permit
|
||||
204.14.234.64/28 permit
|
||||
204.75.18.128/27 permit
|
||||
204.75.142.0/24 permit
|
||||
204.92.114.187 permit
|
||||
204.92.114.203 permit
|
||||
@@ -1953,9 +1892,7 @@
|
||||
204.141.32.0/23 permit
|
||||
204.141.42.0/23 permit
|
||||
204.216.164.202 permit
|
||||
204.220.90.0/23 permit
|
||||
204.220.92.0/22 permit
|
||||
204.220.160.0/20 permit
|
||||
204.220.160.0/21 permit
|
||||
204.220.168.0/21 permit
|
||||
204.220.176.0/20 permit
|
||||
204.220.181.105 permit
|
||||
@@ -1990,9 +1927,6 @@
|
||||
207.211.30.128/25 permit
|
||||
207.211.31.0/25 permit
|
||||
207.211.41.113 permit
|
||||
207.211.132.0/25 permit
|
||||
207.211.147.87 permit
|
||||
207.211.159.110 permit
|
||||
207.218.90.0/24 permit
|
||||
207.218.90.122 permit
|
||||
207.250.68.0/24 permit
|
||||
@@ -2000,8 +1934,6 @@
|
||||
208.43.21.28/30 permit
|
||||
208.43.21.64/29 permit
|
||||
208.43.21.72/30 permit
|
||||
208.56.9.224 permit
|
||||
208.56.13.196 permit
|
||||
208.64.132.0/22 permit
|
||||
208.71.40.63 permit
|
||||
208.71.40.64/31 permit
|
||||
@@ -2028,7 +1960,6 @@
|
||||
208.71.42.214 permit
|
||||
208.72.249.240/29 permit
|
||||
208.75.120.0/22 permit
|
||||
208.76.62.0/23 permit
|
||||
208.76.62.0/24 permit
|
||||
208.76.63.0/24 permit
|
||||
208.82.237.96/29 permit
|
||||
@@ -2042,6 +1973,7 @@
|
||||
209.43.22.0/28 permit
|
||||
209.46.117.168 permit
|
||||
209.46.117.179 permit
|
||||
209.61.151.0/24 permit
|
||||
209.61.151.236 permit
|
||||
209.61.151.249 permit
|
||||
209.61.151.251 permit
|
||||
@@ -2188,7 +2120,6 @@
|
||||
216.136.168.80/28 permit
|
||||
216.139.64.0/19 permit
|
||||
216.145.221.0/24 permit
|
||||
216.146.32.0/23 permit
|
||||
216.146.32.0/24 permit
|
||||
216.146.33.0/24 permit
|
||||
216.198.0.0/18 permit
|
||||
@@ -2209,7 +2140,6 @@
|
||||
223.165.120.0/23 permit
|
||||
2001:0868:0100:0600::/64 permit
|
||||
2001:4860:4000::/36 permit
|
||||
2001:4860:4864::/56 permit
|
||||
2001:748:100:40::2:0/112 permit
|
||||
2001:748:400:1300::3 permit
|
||||
2001:748:400:1300::4 permit
|
||||
@@ -2227,13 +2157,10 @@
|
||||
2001:748:400:3301::3 permit
|
||||
2001:748:400:3301::4 permit
|
||||
2404:6800:4000::/36 permit
|
||||
2404:6800:4864::/56 permit
|
||||
2603:1061:14:1d0::1 permit
|
||||
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
|
||||
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
|
||||
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
|
||||
2607:f8b0:4000::/36 permit
|
||||
2607:f8b0:4864::/56 permit
|
||||
2620:109:c003:104::/64 permit
|
||||
2620:109:c003:104::215 permit
|
||||
2620:109:c006:104::/64 permit
|
||||
@@ -2245,7 +2172,8 @@
|
||||
2620:10d:c09c:400::8:1 permit
|
||||
2620:119:50c0:207::/64 permit
|
||||
2620:119:50c0:207::215 permit
|
||||
2620:1ec:46::51 permit
|
||||
2620:1ec:bdf::51 permit
|
||||
2800:3f0:4000::/36 permit
|
||||
2800:3f0:4864::/56 permit
|
||||
49.12.4.251 permit # checks.mailcow.email
|
||||
2a01:4f8:c17:7906::10 permit # checks.mailcow.email
|
||||
@@ -3,7 +3,8 @@ rules {
|
||||
backend = "http";
|
||||
url = "http://nginx:9081/pipe.php";
|
||||
selector = "reject_no_global_bl";
|
||||
formatter = "multipart";
|
||||
formatter = "default";
|
||||
meta_headers = true;
|
||||
}
|
||||
RLINFO {
|
||||
backend = "http";
|
||||
@@ -15,7 +16,8 @@ rules {
|
||||
backend = "http";
|
||||
url = "http://nginx:9081/pushover.php";
|
||||
selector = "mailcow_rcpt";
|
||||
formatter = "multipart";
|
||||
formatter = "json";
|
||||
meta_headers = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -856,8 +856,6 @@ rspamd_config:register_symbol({
|
||||
|
||||
local seen_cte
|
||||
local newline_s = newline(task)
|
||||
-- use the CTE add_text_footer actually applied
|
||||
local new_cte = rewrite.new_cte or 'quoted-printable'
|
||||
|
||||
local function rewrite_ct_cb(name, hdr)
|
||||
if rewrite.need_rewrite_ct then
|
||||
@@ -879,13 +877,13 @@ rspamd_config:register_symbol({
|
||||
return
|
||||
elseif name:lower() == 'content-transfer-encoding' then
|
||||
out[#out + 1] = string.format('%s: %s',
|
||||
'Content-Transfer-Encoding', new_cte)
|
||||
'Content-Transfer-Encoding', 'quoted-printable')
|
||||
-- update Content-Transfer-Encoding header
|
||||
task:set_milter_reply({
|
||||
remove_headers = {['Content-Transfer-Encoding'] = 0},
|
||||
})
|
||||
task:set_milter_reply({
|
||||
add_headers = {['Content-Transfer-Encoding'] = new_cte}
|
||||
add_headers = {['Content-Transfer-Encoding'] = 'quoted-printable'}
|
||||
})
|
||||
seen_cte = true
|
||||
return
|
||||
@@ -897,7 +895,7 @@ rspamd_config:register_symbol({
|
||||
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', new_cte)
|
||||
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', 'quoted-printable')
|
||||
end
|
||||
|
||||
-- End of headers
|
||||
@@ -916,7 +914,7 @@ rspamd_config:register_symbol({
|
||||
out_parts[#out_parts + 1] = o
|
||||
out_parts[#out_parts + 1] = newline_s
|
||||
else
|
||||
local removePrefix = "--\x0D\x0AContent-"
|
||||
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
|
||||
|
||||
@@ -32,42 +32,47 @@ function parse_email($email) {
|
||||
$a = strrpos($email, '@');
|
||||
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
|
||||
}
|
||||
// rspamd metadata_exporter (multipart formatter):
|
||||
// - $_POST['metadata'] JSON with the rspamd metadata
|
||||
// - $_FILES['message'] raw RFC822 message
|
||||
if (empty($_POST['metadata']) || !isset($_FILES['message']) || $_FILES['message']['error'] !== UPLOAD_ERR_OK) {
|
||||
error_log("QUARANTINE: missing multipart parts from rspamd" . PHP_EOL);
|
||||
http_response_code(400);
|
||||
exit;
|
||||
if (!function_exists('getallheaders')) {
|
||||
function getallheaders() {
|
||||
if (!is_array($_SERVER)) {
|
||||
return array();
|
||||
}
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
|
||||
$meta = json_decode($_POST['metadata'], true);
|
||||
if (!is_array($meta)) {
|
||||
error_log("QUARANTINE: cannot decode metadata JSON" . PHP_EOL);
|
||||
http_response_code(400);
|
||||
exit;
|
||||
}
|
||||
|
||||
$raw_data_content = file_get_contents($_FILES['message']['tmp_name']);
|
||||
$raw_data_content = file_get_contents('php://input');
|
||||
$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
|
||||
$raw_size = (int)$_FILES['message']['size'];
|
||||
$headers = getallheaders();
|
||||
|
||||
$qid = $meta['qid'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$score = $meta['score'] ?? 0;
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$user = $meta['user'] ?? 'unknown';
|
||||
$ip = $meta['ip'] ?? 'unknown';
|
||||
$action = $meta['action'] ?? 'no action';
|
||||
$sender = $meta['from'] ?? '';
|
||||
$symbols = json_encode($meta['symbols'] ?? array());
|
||||
$fuzzy = json_encode(is_array($meta['fuzzy'] ?? null) ? $meta['fuzzy'] : array());
|
||||
$qid = $headers['X-Rspamd-Qid'];
|
||||
$fuzzy = $headers['X-Rspamd-Fuzzy'];
|
||||
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
|
||||
$score = $headers['X-Rspamd-Score'];
|
||||
$rcpts = $headers['X-Rspamd-Rcpt'];
|
||||
$user = $headers['X-Rspamd-User'];
|
||||
$ip = $headers['X-Rspamd-Ip'];
|
||||
$action = $headers['X-Rspamd-Action'];
|
||||
$sender = $headers['X-Rspamd-From'];
|
||||
$symbols = $headers['X-Rspamd-Symbols'];
|
||||
|
||||
$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
|
||||
|
||||
if (empty($sender)) {
|
||||
error_log("QUARANTINE: Unknown sender, assuming empty-env-from@localhost" . PHP_EOL);
|
||||
$sender = 'empty-env-from@localhost';
|
||||
}
|
||||
|
||||
if ($fuzzy == 'unknown') {
|
||||
$fuzzy = '[]';
|
||||
}
|
||||
|
||||
try {
|
||||
$max_size = (int)$redis->Get('Q_MAX_SIZE');
|
||||
if (($max_size * 1048576) < $raw_size) {
|
||||
@@ -89,7 +94,7 @@ catch (RedisException $e) {
|
||||
$rcpt_final_mailboxes = array();
|
||||
|
||||
// Loop through all rcpts
|
||||
foreach ($rcpts as $rcpt) {
|
||||
foreach (json_decode($rcpts, true) as $rcpt) {
|
||||
// Remove tag
|
||||
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
|
||||
|
||||
@@ -153,12 +158,12 @@ foreach ($rcpts as $rcpt) {
|
||||
|
||||
// Loop through all found gotos
|
||||
foreach ($gotos_array as $index => &$goto) {
|
||||
error_log("RCPT RESOLVER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
|
||||
$stmt->execute(array(':goto' => $goto));
|
||||
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
|
||||
if (!empty($username)) {
|
||||
error_log("RCPT RESOLVER: http pipe: mailbox found: " . $username . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
|
||||
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
|
||||
if (!in_array($username, $rcpt_final_mailboxes)) {
|
||||
$rcpt_final_mailboxes[] = $username;
|
||||
@@ -167,21 +172,21 @@ foreach ($rcpts as $rcpt) {
|
||||
else {
|
||||
$parsed_goto = parse_email($goto);
|
||||
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
|
||||
error_log("RCPT RESOLVER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
|
||||
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
|
||||
$stmt->execute(array(':goto' => $goto));
|
||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
|
||||
if ($goto_branch) {
|
||||
error_log("RCPT RESOLVER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||
$goto_branch_array = explode(',', $goto_branch);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
|
||||
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
||||
if ($goto_branch) {
|
||||
error_log("RCPT RESOLVER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
|
||||
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
|
||||
}
|
||||
}
|
||||
@@ -203,11 +208,11 @@ foreach ($rcpts as $rcpt) {
|
||||
// Force exit if loop cannot be solved
|
||||
// Postfix does not allow for alias loops, so this should never happen.
|
||||
$loop_c++;
|
||||
error_log("RCPT RESOLVER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
|
||||
}
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
error_log("RCPT RESOLVER: " . $e->getMessage() . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
|
||||
http_response_code(502);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -32,46 +32,50 @@ function parse_email($email) {
|
||||
$a = strrpos($email, '@');
|
||||
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
|
||||
}
|
||||
// rspamd metadata_exporter (multipart formatter): metadata JSON arrives as $_POST['metadata'].
|
||||
if (empty($_POST['metadata'])) {
|
||||
error_log("NOTIFY: missing metadata part from rspamd" . PHP_EOL);
|
||||
http_response_code(400);
|
||||
exit;
|
||||
if (!function_exists('getallheaders')) {
|
||||
function getallheaders() {
|
||||
if (!is_array($_SERVER)) {
|
||||
return array();
|
||||
}
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
|
||||
$meta = json_decode($_POST['metadata'], true);
|
||||
if (!is_array($meta)) {
|
||||
error_log("NOTIFY: cannot decode metadata JSON" . PHP_EOL);
|
||||
http_response_code(400);
|
||||
exit;
|
||||
}
|
||||
$headers = getallheaders();
|
||||
$json_body = json_decode(file_get_contents('php://input'));
|
||||
|
||||
$qid = $meta['qid'] ?? 'unknown';
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$sender = $meta['from'] ?? '';
|
||||
$ip = $meta['ip'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$messageid= $meta['message_id'] ?? '';
|
||||
$qid = $headers['X-Rspamd-Qid'];
|
||||
$rcpts = $headers['X-Rspamd-Rcpt'];
|
||||
$sender = $headers['X-Rspamd-From'];
|
||||
$ip = $headers['X-Rspamd-Ip'];
|
||||
$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
|
||||
$messageid= $json_body->message_id;
|
||||
$priority = 0;
|
||||
|
||||
$symbols_array = $meta['symbols'] ?? array();
|
||||
$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
|
||||
if (is_array($symbols_array)) {
|
||||
foreach ($symbols_array as $symbol) {
|
||||
if (($symbol['name'] ?? null) == 'HAS_X_PRIO_ONE') {
|
||||
if ($symbol['name'] == 'HAS_X_PRIO_ONE') {
|
||||
$priority = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sender_address = $meta['header_from'][0] ?? '';
|
||||
$sender_address = $json_body->header_from[0];
|
||||
$sender_name = '-';
|
||||
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
|
||||
$sender_address = $matches['address'];
|
||||
$sender_name = trim($matches['name'], '"\' ');
|
||||
}
|
||||
|
||||
$to_address = $meta['header_to'][0] ?? '';
|
||||
$to_address = $json_body->header_to[0];
|
||||
$to_name = '-';
|
||||
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
|
||||
$to_address = $matches['address'];
|
||||
@@ -81,7 +85,7 @@ if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
|
||||
$rcpt_final_mailboxes = array();
|
||||
|
||||
// Loop through all rcpts
|
||||
foreach ($rcpts as $rcpt) {
|
||||
foreach (json_decode($rcpts, true) as $rcpt) {
|
||||
// Remove tag
|
||||
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
|
||||
|
||||
@@ -139,12 +143,12 @@ foreach ($rcpts as $rcpt) {
|
||||
|
||||
// Loop through all found gotos
|
||||
foreach ($gotos_array as $index => &$goto) {
|
||||
error_log("RCPT RESOLVER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
|
||||
$stmt->execute(array(':goto' => $goto));
|
||||
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
|
||||
if (!empty($username)) {
|
||||
error_log("RCPT RESOLVER: http pipe: mailbox found: " . $username . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
|
||||
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
|
||||
if (!in_array($username, $rcpt_final_mailboxes)) {
|
||||
$rcpt_final_mailboxes[] = $username;
|
||||
@@ -153,21 +157,21 @@ foreach ($rcpts as $rcpt) {
|
||||
else {
|
||||
$parsed_goto = parse_email($goto);
|
||||
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
|
||||
error_log("RCPT RESOLVER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
|
||||
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
|
||||
$stmt->execute(array(':goto' => $goto));
|
||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
|
||||
if ($goto_branch) {
|
||||
error_log("RCPT RESOLVER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||
$goto_branch_array = explode(',', $goto_branch);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
|
||||
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
||||
if ($goto_branch) {
|
||||
error_log("RCPT RESOLVER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
|
||||
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
|
||||
}
|
||||
}
|
||||
@@ -189,11 +193,11 @@ foreach ($rcpts as $rcpt) {
|
||||
// Force exit if loop cannot be solved
|
||||
// Postfix does not allow for alias loops, so this should never happen.
|
||||
$loop_c++;
|
||||
error_log("RCPT RESOLVER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
|
||||
}
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
error_log("RCPT RESOLVER: " . $e->getMessage() . PHP_EOL);
|
||||
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
|
||||
http_response_code(502);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -115,8 +115,6 @@ $template_data = [
|
||||
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
|
||||
'ip_check' => customize('get', 'ip_check'),
|
||||
'custom_login' => customize('get', 'custom_login'),
|
||||
'ui_themes' => customize('get', 'ui_themes'),
|
||||
'ui_theme' => customize('get', 'ui_theme'),
|
||||
'password_complexity' => password_complexity('get'),
|
||||
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
||||
'cors_settings' => $cors_settings,
|
||||
|
||||
@@ -185,9 +185,6 @@ paths:
|
||||
example:
|
||||
username: info@domain.tld
|
||||
domain: domain.tld
|
||||
description: my time limited alias
|
||||
validity: 8760
|
||||
permanent: false
|
||||
properties:
|
||||
username:
|
||||
description: 'the mailbox an alias should be created for'
|
||||
@@ -195,15 +192,6 @@ paths:
|
||||
domain:
|
||||
description: "the domain"
|
||||
type: string
|
||||
description:
|
||||
description: "a description for the alias, defaults to an empty string"
|
||||
type: string
|
||||
validity:
|
||||
description: "how many hours the alias stays valid, 1 to 87600, defaults to 8760 (one year)"
|
||||
type: integer
|
||||
permanent:
|
||||
description: "keep the alias after it expired, defaults to false"
|
||||
type: boolean
|
||||
type: object
|
||||
summary: Create time limited alias
|
||||
/api/v1/add/app-passwd:
|
||||
@@ -1084,7 +1072,6 @@ paths:
|
||||
password2: "*"
|
||||
quota: "3072"
|
||||
force_pw_update: "1"
|
||||
force_tfa: "1"
|
||||
tls_enforce_in: "1"
|
||||
tls_enforce_out: "1"
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -1131,7 +1118,6 @@ paths:
|
||||
password2: atedismonsin
|
||||
quota: "3072"
|
||||
force_pw_update: "1"
|
||||
force_tfa: "1"
|
||||
tls_enforce_in: "1"
|
||||
tls_enforce_out: "1"
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -1165,9 +1151,6 @@ paths:
|
||||
force_pw_update:
|
||||
description: forces the user to update its password on first login
|
||||
type: boolean
|
||||
force_tfa:
|
||||
description: force 2FA enrollment at login
|
||||
type: boolean
|
||||
tls_enforce_in:
|
||||
description: force inbound email tls encryption
|
||||
type: boolean
|
||||
@@ -2527,7 +2510,7 @@ paths:
|
||||
description: >-
|
||||
Using this endpoint you can perform actions on quarantine items. It is possible to release
|
||||
emails from quarantine into to the inbox, or learn them as ham to improve Rspamd filtering.
|
||||
You must provide the quarantine item IDs. You can get the IDs using the GET method.
|
||||
You must provide the quarantine item IDs. You can get the IDs using the GET method.
|
||||
operationId: Edit mails in Quarantine
|
||||
requestBody:
|
||||
content:
|
||||
@@ -3431,7 +3414,6 @@ paths:
|
||||
- mailbox
|
||||
- active: "1"
|
||||
force_pw_update: "0"
|
||||
force_tfa: "0"
|
||||
name: Full name
|
||||
password: "*"
|
||||
password2: "*"
|
||||
@@ -3482,7 +3464,6 @@ paths:
|
||||
attr:
|
||||
active: "1"
|
||||
force_pw_update: "0"
|
||||
force_tfa: "0"
|
||||
name: Full name
|
||||
authsource: mailcow
|
||||
password: ""
|
||||
@@ -3506,9 +3487,6 @@ paths:
|
||||
force_pw_update:
|
||||
description: force user to change password on next login
|
||||
type: boolean
|
||||
force_tfa:
|
||||
description: force 2FA enrollment at login
|
||||
type: boolean
|
||||
name:
|
||||
description: Full name of the mailbox user
|
||||
type: string
|
||||
@@ -4903,7 +4881,6 @@ paths:
|
||||
- active: "1"
|
||||
attributes:
|
||||
force_pw_update: "0"
|
||||
force_tfa: "0"
|
||||
mailbox_format: "maildir:"
|
||||
quarantine_notification: never
|
||||
sogo_access: "1"
|
||||
@@ -4920,7 +4897,6 @@ paths:
|
||||
quota: 3221225472
|
||||
quota_used: 0
|
||||
rl: false
|
||||
sender_acl: ["otherbox@doman3.tld", "@aliasdomain.tld"]
|
||||
spam_aliases: 0
|
||||
username: info@doman3.tld
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -5829,7 +5805,6 @@ paths:
|
||||
- active: "1"
|
||||
attributes:
|
||||
force_pw_update: "0"
|
||||
force_tfa: "0"
|
||||
mailbox_format: "maildir:"
|
||||
quarantine_notification: never
|
||||
sogo_access: "1"
|
||||
@@ -5847,7 +5822,6 @@ paths:
|
||||
quota: 3221225472
|
||||
quota_used: 0
|
||||
rl: false
|
||||
sender_acl: ["otherbox@domain3.tld", "@aliasdomain.tld"]
|
||||
spam_aliases: 0
|
||||
username: info@domain3.tld
|
||||
tags: ["tag1", "tag2"]
|
||||
@@ -5870,7 +5844,7 @@ paths:
|
||||
response:
|
||||
value:
|
||||
- type: "success"
|
||||
log: ["cors", "edit", {"allowed_origins": ["*", "https://mail.mailcow.tld"], "allowed_methods": ["POST", "GET", "DELETE", "PUT"]}]
|
||||
log: ["cors", "edit", {"allowed_origins": ["*", "mail.mailcow.tld"], "allowed_methods": ["POST", "GET", "DELETE", "PUT"]}]
|
||||
msg: "cors_headers_edited"
|
||||
description: OK
|
||||
headers: { }
|
||||
@@ -5887,7 +5861,7 @@ paths:
|
||||
schema:
|
||||
example:
|
||||
attr:
|
||||
allowed_origins: ["*", "https://mail.mailcow.tld"]
|
||||
allowed_origins: ["*", "mail.mailcow.tld"]
|
||||
allowed_methods: ["POST", "GET", "DELETE", "PUT"]
|
||||
properties:
|
||||
attr:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.14.0-beta3 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2022 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Bootstrap-select v1.14.0-beta2 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2021 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
@-webkit-keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
@@ -35,7 +35,8 @@ select.selectpicker {
|
||||
display: none !important;
|
||||
}
|
||||
.bootstrap-select {
|
||||
width: 220px;
|
||||
width: 220px \0;
|
||||
/*IE9 and below*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle {
|
||||
|
||||
@@ -17,15 +17,15 @@ table.dataTable>tbody>tr.child ul.dtr-details>li {
|
||||
}
|
||||
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: var(--mc-dtr-hover-bg);
|
||||
background-color: #5e5e5e;
|
||||
}
|
||||
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 {
|
||||
background-color: var(--mc-dtr-bg) !important;
|
||||
border: 1.5px solid var(--mc-dtr-border) !important;
|
||||
background-color: #979797 !important;
|
||||
border: 1.5px solid #616161 !important;
|
||||
border-radius: 2px !important;
|
||||
color: var(--mc-dtr-color);
|
||||
color: #fff;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
line-height: 1.25em;
|
||||
@@ -37,7 +37,7 @@ table.dataTable td.dt-control:before {
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
|
||||
table.dataTable td.dt-control:before {
|
||||
background-color: var(--mc-dtr-active-bg) !important;
|
||||
background-color: #979797 !important;
|
||||
}
|
||||
table.dataTable.table-striped>tbody>tr>td {
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -34,44 +34,11 @@
|
||||
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
|
||||
}
|
||||
|
||||
/* theme tokens, override in css/themes/<name>/theme.css */
|
||||
:root {
|
||||
--mc-font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
--mc-font-size: 10.5pt;
|
||||
--mc-body-bg: #fbfbfb;
|
||||
--mc-primary: #0F688D;
|
||||
--mc-primary-border: #0d526d;
|
||||
--mc-info: #148DBC;
|
||||
--mc-info-border: #127ea8;
|
||||
--mc-border: #dfdfdf;
|
||||
--mc-surface: #fff;
|
||||
--mc-surface-alt: #f7f7f7;
|
||||
--mc-muted: #959595;
|
||||
--mc-hr: #ccc;
|
||||
--mc-progress-bg: #d5d5d5;
|
||||
--mc-placeholder: rgb(137 137 137);
|
||||
--mc-btn-outline-color: #000000;
|
||||
--mc-btn-outline-border: #cfcfcf;
|
||||
--mc-btn-outline-hover-bg: #f0f0f0;
|
||||
--mc-btn-outline-active-bg: #f0f0f0;
|
||||
--mc-btn-light-active-color: #fff;
|
||||
--mc-btn-light-active-bg: #555;
|
||||
--mc-btn-light-active-border: #4d4d4d;
|
||||
--mc-dtr-bg: #979797;
|
||||
--mc-dtr-border: #616161;
|
||||
--mc-dtr-color: #fff;
|
||||
--mc-dtr-hover-bg: #5e5e5e;
|
||||
--mc-dtr-active-bg: #979797;
|
||||
--mc-login-user: #28b62c;
|
||||
--mc-login-domainadmin: #ffc107;
|
||||
--mc-login-admin: #ff4136;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--mc-body-bg);
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
#maxmsgsize { min-width: 80px; }
|
||||
#slider1 .slider-selection {
|
||||
@@ -119,17 +86,17 @@ body {
|
||||
max-height: 1000px
|
||||
}
|
||||
.nav-tabs .nav-link, .nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:hover, .nav-tabs .nav-link.disabled:focus {
|
||||
border-color: var(--mc-border);
|
||||
border-color: #dfdfdf;
|
||||
}
|
||||
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
|
||||
border-color: var(--mc-border);
|
||||
border-bottom: 1px solid var(--mc-surface);
|
||||
border-color: #dfdfdf;
|
||||
border-bottom: 1px solid #ffffff;
|
||||
}
|
||||
.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {
|
||||
border-color: var(--mc-border);
|
||||
border-color: #dfdfdf;
|
||||
}
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid var(--mc-border);
|
||||
border-bottom: 1px solid #dfdfdf;
|
||||
}
|
||||
.bi {
|
||||
display: inline-block;
|
||||
@@ -197,13 +164,13 @@ body.modal-open {
|
||||
padding-right: inherit !important;
|
||||
}
|
||||
body {
|
||||
font-family: var(--mc-font-family);
|
||||
font-size: var(--mc-font-size);
|
||||
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
font-size: 10.5pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
html {
|
||||
font-family: var(--mc-font-family);
|
||||
font-size: var(--mc-font-size);
|
||||
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
font-size: 10.5pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
#mailcow-alert {
|
||||
@@ -239,7 +206,7 @@ legend {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.overlay {
|
||||
background: var(--mc-surface);
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
@@ -248,6 +215,9 @@ legend {
|
||||
.bootstrap-select.btn-group .no-results {
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: rgb(197, 197, 197) !important;
|
||||
}
|
||||
.haveibeenpwned {
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
@@ -269,7 +239,7 @@ legend {
|
||||
.footer {
|
||||
margin-top: 27px;
|
||||
margin-bottom: 20px;
|
||||
color: var(--mc-muted);
|
||||
color: #959595;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -367,7 +337,7 @@ code {
|
||||
overflow-x: auto!important;
|
||||
}
|
||||
.well {
|
||||
border: 1px solid var(--mc-border);
|
||||
border: 1px solid #dfdfdf;
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -386,47 +356,47 @@ button[aria-expanded='true'] > .caret {
|
||||
}
|
||||
|
||||
.list-group-details {
|
||||
background: var(--mc-surface);
|
||||
background: #fff;
|
||||
}
|
||||
.list-group-header {
|
||||
background: var(--mc-surface-alt);
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
|
||||
.bg-primary, .alert-primary, .btn-primary {
|
||||
background-color: var(--mc-primary) !important;
|
||||
border-color: var(--mc-primary-border) !important;
|
||||
background-color: #0F688D !important;
|
||||
border-color: #0d526d !important;
|
||||
}
|
||||
.bg-info, .alert-info, .btn-info {
|
||||
background-color: var(--mc-info) !important;
|
||||
border-color: var(--mc-info-border) !important;
|
||||
background-color: #148DBC !important;
|
||||
border-color: #127ea8 !important;
|
||||
}
|
||||
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: var(--mc-placeholder) !important;
|
||||
color: rgb(137 137 137)!important;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 16px;
|
||||
background-color: var(--mc-progress-bg);
|
||||
background-color: #d5d5d5;
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: var(--mc-btn-outline-hover-bg);
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.btn.btn-outline-secondary {
|
||||
color: var(--mc-btn-outline-color) !important;
|
||||
border-color: var(--mc-btn-outline-border) !important;
|
||||
color: #000000 !important;
|
||||
border-color: #cfcfcf !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 {
|
||||
background-color: var(--mc-btn-outline-active-bg) !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: var(--mc-btn-light-active-color);
|
||||
background-color: var(--mc-btn-light-active-bg);
|
||||
color: #fff;
|
||||
background-color: #555;
|
||||
background-image: none;
|
||||
border-color: var(--mc-btn-light-active-border);
|
||||
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 {
|
||||
@@ -442,26 +412,6 @@ button[aria-expanded='true'] > .caret {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* role marker on login pages, header icons differ too (color blindness) */
|
||||
.login-card::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 5px;
|
||||
background: var(--mc-login-color);
|
||||
border-radius: inherit;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.login-user {
|
||||
--mc-login-color: var(--mc-login-user);
|
||||
}
|
||||
.login-domainadmin {
|
||||
--mc-login-color: var(--mc-login-domainadmin);
|
||||
}
|
||||
.login-admin {
|
||||
--mc-login-color: var(--mc-login-admin);
|
||||
}
|
||||
|
||||
.hr-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -473,7 +423,7 @@ button[aria-expanded='true'] > .caret {
|
||||
.hr-title::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-bottom: 1px solid var(--mc-hr);
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.hr-title:not(:empty)::before {
|
||||
|
||||
+2934
-3749
File diff suppressed because it is too large.
Load diff
@@ -1,103 +1,62 @@
|
||||
/* theme tokens, override in css/themes/<name>/dark.css */
|
||||
:root {
|
||||
--mc-body-bg: #1c1c1e;
|
||||
--mc-body-color: #f2f2f7;
|
||||
--mc-surface: #2c2c2e;
|
||||
--mc-surface-alt: #282828;
|
||||
--mc-text: #ccc;
|
||||
--mc-text-soft: #bbb;
|
||||
--mc-muted: #8e8e93;
|
||||
--mc-border: #555;
|
||||
--mc-tab-border: #444444;
|
||||
--mc-link: #6fc7e9;
|
||||
--mc-link-hover: #3daedb;
|
||||
--mc-btn-bg: #5e5e5e;
|
||||
--mc-btn-border: #4c4c4e;
|
||||
--mc-input-bg: #424242;
|
||||
--mc-input-color: #e2e2e2;
|
||||
--mc-input-border: #999;
|
||||
--mc-dropdown-bg: #424242;
|
||||
--mc-table-bg: #3a3a3a;
|
||||
--mc-table-stripe: #282828;
|
||||
--mc-table-stripe-row: #424242;
|
||||
--mc-table-hover: #343434;
|
||||
--mc-table-border: #5c5c5c;
|
||||
--mc-progress-bg: #555;
|
||||
--mc-placeholder: #d4d4d4;
|
||||
--mc-btn-outline-color: #e0e0e0;
|
||||
--mc-btn-outline-border: #7a7a7a;
|
||||
--mc-btn-outline-hover-bg: #5c5c5c;
|
||||
--mc-btn-outline-active-bg: #7a7a7a;
|
||||
--mc-btn-light-active-color: #f2f2f7;
|
||||
--mc-btn-light-active-bg: #242424;
|
||||
--mc-btn-light-active-border: #1c1c1e;
|
||||
--mc-btn-dark-bg: #242424;
|
||||
--mc-input-disabled-bg: #6e6e6e;
|
||||
--mc-input-disabled-color: #a8a8a8;
|
||||
--mc-list-header-bg: #444;
|
||||
--mc-list-details-bg: #555;
|
||||
--mc-chip-bg: #444;
|
||||
--mc-table-child-bg: #414141;
|
||||
--mc-dtr-bg: #7a7a7a;
|
||||
--mc-dtr-border: #5c5c5c;
|
||||
--mc-dtr-color: #e0e0e0;
|
||||
--mc-dtr-hover-bg: #7a7a7a;
|
||||
--mc-dtr-active-bg: #949494;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--mc-body-bg);
|
||||
color: var(--mc-body-color);
|
||||
background-color: #1c1c1e;
|
||||
color: #f2f2f7;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--mc-surface);
|
||||
background-color: var(--mc-surface);
|
||||
border: 1px solid #2c2c2e;
|
||||
background-color: #2c2c2e;
|
||||
}
|
||||
|
||||
legend {
|
||||
color: var(--mc-body-color);
|
||||
color: #f2f2f7;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
color: var(--mc-muted);
|
||||
background-color: var(--mc-body-bg);
|
||||
color: #8e8e93;
|
||||
background-color: #1c1c1e;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
--bs-card-color: var(--mc-text-soft);
|
||||
--bs-card-color: #bbb;
|
||||
}
|
||||
|
||||
.btn-secondary, .paginate_button, .page-link, .btn-light {
|
||||
color: var(--mc-body-color) !important;
|
||||
background-color: var(--mc-btn-bg) !important;
|
||||
border-color: var(--mc-btn-border) !important;
|
||||
color: #f2f2f7 !important;
|
||||
background-color: #5e5e5e !important;
|
||||
border-color: #4c4c4e !important;
|
||||
}
|
||||
|
||||
.btn-dark {
|
||||
color: var(--mc-body-color) !important;
|
||||
background-color: var(--mc-btn-dark-bg) !important;
|
||||
border-color: var(--mc-body-bg) !important;
|
||||
color: #f2f2f7 !important;
|
||||
background-color: #242424 !important;
|
||||
border-color: #1c1c1e !important;
|
||||
}
|
||||
|
||||
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
|
||||
background-color: #444444;
|
||||
border-color: #4c4c4e !important;
|
||||
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: var(--mc-btn-bg) !important;
|
||||
border-color: #5e5e5e !important;
|
||||
}
|
||||
|
||||
.alert-secondary {
|
||||
color: var(--mc-body-color) !important;
|
||||
background-color: var(--mc-btn-bg) !important;
|
||||
border-color: var(--mc-btn-border) !important;
|
||||
color: #f2f2f7 !important;
|
||||
background-color: #5e5e5e !important;
|
||||
border-color: #4c4c4e !important;
|
||||
}
|
||||
|
||||
.bg-secondary {
|
||||
color: var(--mc-body-color) !important;
|
||||
background-color: var(--mc-btn-bg) !important;
|
||||
color: #f2f2f7 !important;
|
||||
background-color: #5e5e5e !important;
|
||||
}
|
||||
|
||||
.alert-secondary, .alert-secondary a, .alert-secondary .alert-link {
|
||||
color: var(--mc-body-color);
|
||||
color: #f2f2f7;
|
||||
}
|
||||
|
||||
.page-item.active .page-link {
|
||||
@@ -106,18 +65,18 @@ legend {
|
||||
}
|
||||
|
||||
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
|
||||
background-color: var(--mc-btn-bg);
|
||||
border-color: var(--mc-btn-border) !important;
|
||||
color: var(--mc-body-color);
|
||||
background-color: #5e5e5e;
|
||||
border-color: #4c4c4e !important;
|
||||
color: #f2f2f7;
|
||||
}
|
||||
|
||||
.btn-secondary:disabled, .btn-secondary.disabled {
|
||||
border-color: var(--mc-btn-bg) !important;
|
||||
border-color: #5e5e5e !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
--bs-modal-color: var(--mc-text-soft);
|
||||
background-color: var(--mc-surface);
|
||||
--bs-modal-color: #bbb;
|
||||
background-color: #2c2c2e;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
@@ -125,7 +84,7 @@ legend {
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
color: var(--mc-text-soft);
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.modal .btn-close {
|
||||
@@ -133,12 +92,12 @@ legend {
|
||||
}
|
||||
|
||||
.navbar.bg-light {
|
||||
background-color: var(--mc-body-bg) !important;
|
||||
border-color: var(--mc-surface);
|
||||
background-color: #1c1c1e !important;
|
||||
border-color: #2c2c2e;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--mc-muted) !important;
|
||||
color: #8e8e93 !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
|
||||
@@ -146,23 +105,23 @@ legend {
|
||||
}
|
||||
|
||||
.nav-tabs, .nav-tabs .nav-link {
|
||||
border-color: var(--mc-tab-border) !important;
|
||||
border-color: #444444 !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus, .nav-tabs .nav-link.active {
|
||||
border-bottom-color: var(--mc-body-bg) !important;
|
||||
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: var(--mc-surface) !important;
|
||||
border-bottom-color: #2c2c2e !important;
|
||||
}
|
||||
|
||||
.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
|
||||
color: var(--mc-body-color) !important;
|
||||
color: #f2f2f7 !important;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: var(--mc-dropdown-bg);
|
||||
background-color: #424242;
|
||||
border: 1px solid #282828;
|
||||
}
|
||||
.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {
|
||||
@@ -173,79 +132,79 @@ legend {
|
||||
color: #fff;
|
||||
}
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder, .bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: var(--mc-placeholder) !important;
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
tbody tr {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus, .navbar-default .navbar-nav>.active>a:hover {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.list-group-item {
|
||||
background-color: var(--mc-surface-alt);
|
||||
border: 1px solid var(--mc-border);
|
||||
background-color: #282828;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-of-type(odd) {
|
||||
background-color: var(--mc-table-stripe-row);
|
||||
background-color: #424242;
|
||||
}
|
||||
table.dataTable>tbody>tr.child ul.dtr-details>li {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.13);
|
||||
}
|
||||
.label.label-last-login {
|
||||
color: var(--mc-text) !important;
|
||||
color: #ccc !important;
|
||||
background-color: #555 !important;
|
||||
}
|
||||
.progress {
|
||||
background-color: var(--mc-progress-bg);
|
||||
background-color: #555;
|
||||
}
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
div.numberedtextarea-number {
|
||||
color: #999;
|
||||
}
|
||||
.well {
|
||||
border: 1px solid var(--mc-border);
|
||||
background-color: var(--mc-surface-alt);
|
||||
border: 1px solid #555;
|
||||
background-color: #282828;
|
||||
}
|
||||
pre {
|
||||
color: var(--mc-text);
|
||||
background-color: var(--mc-surface-alt);
|
||||
border: 1px solid var(--mc-border);
|
||||
color: #ccc;
|
||||
background-color: #282828;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
.form-control {
|
||||
background-color: transparent;
|
||||
}
|
||||
input.form-control, textarea.form-control {
|
||||
color: var(--mc-input-color) !important;
|
||||
background-color: var(--mc-input-bg) !important;
|
||||
border: 1px solid var(--mc-input-border);
|
||||
color: #e2e2e2 !important;
|
||||
background-color: #424242 !important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
input.form-control:focus, textarea.form-control {
|
||||
background-color: var(--mc-input-bg) !important;
|
||||
background-color: #424242 !important;
|
||||
}
|
||||
input.form-control:disabled, textarea.form-disabled {
|
||||
color: var(--mc-input-disabled-color) !important;
|
||||
background-color: var(--mc-input-disabled-bg) !important;
|
||||
color: #a8a8a8 !important;
|
||||
background-color: #6e6e6e !important;
|
||||
}
|
||||
.input-group-addon {
|
||||
color: var(--mc-text);
|
||||
background-color: var(--mc-input-bg) !important;
|
||||
border: 1px solid var(--mc-input-border);
|
||||
color: #ccc;
|
||||
background-color: #424242 !important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.input-group-text {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.dropdown-item {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.dropdown-item:hover {
|
||||
color: #616161 !important;
|
||||
@@ -255,9 +214,9 @@ input.form-control:disabled, textarea.form-disabled {
|
||||
background-color: #007aff;
|
||||
}
|
||||
.form-select {
|
||||
color: var(--mc-input-color)!important;
|
||||
background-color: var(--mc-input-bg)!important;
|
||||
border: 1px solid var(--mc-input-border);
|
||||
color: #e2e2e2!important;
|
||||
background-color: #424242!important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
.responsive-tabs .card-header button[data-bs-toggle="collapse"] {
|
||||
@@ -281,15 +240,15 @@ input.form-control:disabled, textarea.form-disabled {
|
||||
|
||||
|
||||
a {
|
||||
color: var(--mc-link);
|
||||
color: #6fc7e9;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--mc-link-hover);
|
||||
color: #3daedb;
|
||||
}
|
||||
|
||||
.breadcrumb-item.active {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
|
||||
@@ -314,11 +273,11 @@ a:hover {
|
||||
background-color: #3a3a3a;
|
||||
}
|
||||
.accordion-button {
|
||||
color: var(--mc-text-soft);
|
||||
color: #bbb;
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
.accordion-button:not(.collapsed) {
|
||||
color: var(--mc-link);
|
||||
color: #6fc7e9;
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
.accordion-button::after {
|
||||
@@ -341,15 +300,15 @@ a:hover {
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
background-color: var(--mc-surface-alt);
|
||||
border: 1px solid var(--mc-border);
|
||||
background-color: #282828;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
.tag-input {
|
||||
color: #fff;
|
||||
background-color: var(--mc-surface-alt);
|
||||
background-color: #282828;
|
||||
}
|
||||
.tag-add {
|
||||
color: var(--mc-text);
|
||||
color: #ccc;
|
||||
}
|
||||
.tag-add:hover {
|
||||
color: #d1d1d1;
|
||||
@@ -359,19 +318,19 @@ a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: var(--mc-btn-outline-hover-bg);
|
||||
background-color: #5c5c5c;
|
||||
}
|
||||
.btn.btn-outline-secondary {
|
||||
color: var(--mc-btn-outline-color) !important;
|
||||
border-color: var(--mc-btn-outline-border) !important;
|
||||
color: #e0e0e0 !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 {
|
||||
background-color: var(--mc-btn-outline-active-bg) !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: var(--mc-btn-light-active-color) !important;
|
||||
background-color: var(--mc-btn-light-active-bg) !important;
|
||||
border-color: var(--mc-btn-light-active-border) !important;
|
||||
color: #f2f2f7 !important;
|
||||
background-color: #242424 !important;
|
||||
border-color: #1c1c1e !important;
|
||||
}
|
||||
.btn-input-missing,
|
||||
.btn-input-missing:hover,
|
||||
@@ -389,16 +348,16 @@ a:hover {
|
||||
}
|
||||
|
||||
.list-group-details {
|
||||
background: var(--mc-list-details-bg);
|
||||
background: #555;
|
||||
}
|
||||
.list-group-header {
|
||||
background: var(--mc-list-header-bg);
|
||||
background: #444;
|
||||
}
|
||||
|
||||
span.mail-address-item {
|
||||
background-color: var(--mc-chip-bg);
|
||||
background-color: #444;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--mc-border);
|
||||
border: 1px solid #555;
|
||||
padding: 2px 7px;
|
||||
display: inline-block;
|
||||
margin: 2px 6px 2px 0;
|
||||
@@ -406,52 +365,52 @@ span.mail-address-item {
|
||||
|
||||
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: var(--mc-dtr-hover-bg) !important;
|
||||
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: var(--mc-dtr-bg) !important;
|
||||
border: 1.5px solid var(--mc-dtr-border) !important;
|
||||
color: var(--mc-dtr-color) !important;
|
||||
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: var(--mc-dtr-active-bg);
|
||||
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: var(--mc-table-child-bg);
|
||||
background-color: #414141;
|
||||
}
|
||||
|
||||
table.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
|
||||
color: var(--mc-text) !important;
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.table-secondary {
|
||||
--bs-table-bg: #282828;
|
||||
--bs-table-striped-bg: #343434;
|
||||
--bs-table-striped-color: var(--mc-body-color);
|
||||
--bs-table-striped-color: #f2f2f7;
|
||||
--bs-table-active-bg: #4c4c4c;
|
||||
--bs-table-active-color: var(--mc-body-color);
|
||||
--bs-table-active-color: #f2f2f7;
|
||||
--bs-table-hover-bg: #3a3a3a;
|
||||
--bs-table-hover-color: var(--mc-body-color);
|
||||
color: var(--mc-text);
|
||||
--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: var(--mc-body-color);
|
||||
--bs-table-striped-color: #f2f2f7;
|
||||
--bs-table-active-bg: #5c5c5c;
|
||||
--bs-table-active-color: var(--mc-body-color);
|
||||
--bs-table-active-color: #f2f2f7;
|
||||
--bs-table-hover-bg: #4c4c4c;
|
||||
--bs-table-hover-color: var(--mc-body-color);
|
||||
color: var(--mc-text);
|
||||
--bs-table-hover-color: #f2f2f7;
|
||||
color: #ccc;
|
||||
border-color: #4c4c4c;
|
||||
}
|
||||
|
||||
@@ -471,23 +430,23 @@ table.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
|
||||
|
||||
.table-striped>tbody>tr:nth-of-type(odd)>td,
|
||||
.table-striped>tbody>tr:nth-of-type(odd)>th {
|
||||
background-color: var(--mc-table-stripe);
|
||||
background-color: #282828;
|
||||
}
|
||||
|
||||
.table-hover>tbody>tr:hover {
|
||||
background-color: var(--mc-table-hover);
|
||||
background-color: #343434;
|
||||
}
|
||||
|
||||
.table>:not(caption)>*>* {
|
||||
border-color: var(--mc-table-border);
|
||||
--bs-table-color-state:var(--mc-text-soft);
|
||||
--bs-table-bg: var(--mc-table-bg);
|
||||
border-color: #5c5c5c;
|
||||
--bs-table-color-state:#bbb;
|
||||
--bs-table-bg: #3a3a3a;
|
||||
}
|
||||
.text-muted {
|
||||
--bs-secondary-color: var(--mc-muted);
|
||||
--bs-secondary-color: #8e8e93;
|
||||
}
|
||||
input::placeholder {
|
||||
color: var(--mc-muted) !important;
|
||||
color: #8e8e93 !important;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
/* servercow theme, dark mode, bundled after mailcow core dark mode */
|
||||
|
||||
:root {
|
||||
--sc-brown-hover: #806b55;
|
||||
--sc-brown-active: #8a745c;
|
||||
--sc-brown-soft: rgba(201, 180, 154, .18);
|
||||
--sc-brown-tint: rgba(201, 180, 154, .08);
|
||||
--sc-link: #c9b49a;
|
||||
--sc-danger-text: #ff8a8a;
|
||||
--sc-ink: #ece7e1;
|
||||
--sc-muted: #a89f94;
|
||||
--sc-placeholder: #8a8177;
|
||||
--sc-bg: #171513;
|
||||
--sc-surface: #211e1b;
|
||||
--sc-cap: #2a2622;
|
||||
--sc-addon: #2a2622;
|
||||
--sc-line: #463f38;
|
||||
--sc-divider: #34302b;
|
||||
--sc-cap-line: #3d3732;
|
||||
--sc-stripe: rgba(255, 255, 255, .025);
|
||||
--sc-hover: rgba(201, 180, 154, .08);
|
||||
--sc-focus-border: #8a7864;
|
||||
--sc-focus: rgba(201, 180, 154, .25);
|
||||
--sc-shadow: 0 0 10px 0 rgba(0, 0, 0, .5);
|
||||
--bs-body-color: #ece7e1;
|
||||
--bs-body-bg: #171513;
|
||||
/* core dark tokens, see css/themes/mailcow-darkmode.css */
|
||||
--mc-body-bg: var(--sc-bg);
|
||||
--mc-body-color: var(--sc-ink);
|
||||
--mc-surface: var(--sc-surface);
|
||||
--mc-surface-alt: var(--sc-cap);
|
||||
--mc-text: var(--sc-ink);
|
||||
--mc-text-soft: var(--sc-ink);
|
||||
--mc-muted: var(--sc-muted);
|
||||
--mc-border: var(--sc-divider);
|
||||
--mc-tab-border: var(--sc-divider);
|
||||
--mc-link: var(--sc-link);
|
||||
--mc-link-hover: #e0cfba;
|
||||
--mc-btn-bg: var(--sc-cap);
|
||||
--mc-btn-border: var(--sc-line);
|
||||
--mc-input-bg: var(--sc-bg);
|
||||
--mc-input-color: var(--sc-ink);
|
||||
--mc-input-border: var(--sc-line);
|
||||
--mc-dropdown-bg: var(--sc-surface);
|
||||
--mc-table-bg: transparent;
|
||||
--mc-table-stripe: var(--sc-stripe);
|
||||
--mc-table-stripe-row: transparent;
|
||||
--mc-table-hover: var(--sc-hover);
|
||||
--mc-table-border: var(--sc-divider);
|
||||
--mc-progress-bg: var(--sc-cap);
|
||||
--mc-placeholder: var(--sc-placeholder);
|
||||
--mc-hr: var(--sc-divider);
|
||||
--mc-btn-outline-color: var(--sc-ink);
|
||||
--mc-btn-outline-border: var(--sc-line);
|
||||
--mc-btn-outline-hover-bg: var(--sc-brown-tint);
|
||||
--mc-btn-outline-active-bg: var(--sc-brown);
|
||||
--mc-btn-light-active-color: #fff;
|
||||
--mc-btn-light-active-bg: var(--sc-brown);
|
||||
--mc-btn-light-active-border: var(--sc-brown);
|
||||
--mc-btn-dark-bg: var(--sc-brown);
|
||||
--mc-input-disabled-bg: var(--sc-cap);
|
||||
--mc-input-disabled-color: var(--sc-muted);
|
||||
--mc-list-header-bg: var(--sc-cap);
|
||||
--mc-list-details-bg: var(--sc-surface);
|
||||
--mc-chip-bg: var(--sc-cap);
|
||||
--mc-table-child-bg: var(--sc-surface);
|
||||
--mc-dtr-bg: var(--sc-cap);
|
||||
--mc-dtr-border: var(--sc-line);
|
||||
--mc-dtr-color: var(--sc-link);
|
||||
--mc-dtr-hover-bg: var(--sc-hover);
|
||||
--mc-dtr-active-bg: var(--sc-brown-soft);
|
||||
}
|
||||
|
||||
/* navbar */
|
||||
.navbar.bg-light {
|
||||
background-color: var(--sc-surface) !important;
|
||||
border-bottom-color: var(--sc-green);
|
||||
}
|
||||
.navbar-light .navbar-nav .nav-link {
|
||||
color: var(--sc-ink) !important;
|
||||
}
|
||||
.navbar-light .navbar-nav .nav-link:hover,
|
||||
.navbar-light .navbar-nav .nav-link:focus,
|
||||
.navbar-light .navbar-nav .nav-link.active,
|
||||
.navbar-light .navbar-nav .show > .nav-link {
|
||||
color: var(--sc-link) !important;
|
||||
}
|
||||
.dropdown-menu {
|
||||
border-color: var(--sc-cap-line);
|
||||
}
|
||||
.dropdown-item:hover, .dropdown-item:focus {
|
||||
color: var(--sc-link) !important;
|
||||
}
|
||||
|
||||
/* cards and modals */
|
||||
.card-header {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-cap);
|
||||
border-color: var(--sc-cap-line);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: var(--sc-surface);
|
||||
}
|
||||
.modal-header {
|
||||
border-color: var(--sc-cap-line);
|
||||
}
|
||||
.input-group-text {
|
||||
background-color: var(--sc-addon);
|
||||
}
|
||||
.form-control::file-selector-button {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-cap);
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: var(--sc-link);
|
||||
background-color: var(--sc-line);
|
||||
}
|
||||
.form-check-input:not(:checked) {
|
||||
background-color: var(--sc-bg);
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
kbd {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-cap);
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
.btn-secondary:hover, .btn-secondary:focus, .btn-light:hover, .btn-light:focus, .page-link:hover {
|
||||
color: var(--sc-link) !important;
|
||||
background-color: var(--sc-line) !important;
|
||||
}
|
||||
.btn-check:checked + .btn-secondary, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle,
|
||||
.page-item.active .page-link {
|
||||
color: #fff !important;
|
||||
background-color: var(--sc-brown) !important;
|
||||
border-color: var(--sc-brown) !important;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs .nav-link {
|
||||
color: var(--sc-ink) !important;
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus,
|
||||
.card .nav-tabs .nav-link:not(.disabled):hover, .card .nav-tabs .nav-link:not(.disabled):focus {
|
||||
color: var(--sc-link) !important;
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
.nav-tabs .nav-link.active, .card .nav-tabs .nav-link.active {
|
||||
color: var(--sc-link) !important;
|
||||
border-bottom-color: var(--sc-link) !important;
|
||||
}
|
||||
|
||||
/* semantic text */
|
||||
.text-success {
|
||||
color: var(--sc-green) !important;
|
||||
}
|
||||
.text-warning {
|
||||
color: var(--sc-warning) !important;
|
||||
}
|
||||
|
||||
/* alerts, opaque dark tints (also used for floating notifications) */
|
||||
.alert-primary {
|
||||
color: #e6d9c9 !important;
|
||||
background-color: #3c342b !important;
|
||||
border-color: #5e4f3e !important;
|
||||
}
|
||||
.alert-info {
|
||||
color: #cfe9f6 !important;
|
||||
background-color: #2f3334 !important;
|
||||
border-color: #44535a !important;
|
||||
}
|
||||
.alert-success {
|
||||
color: #cdeea0;
|
||||
background-color: #2e3419;
|
||||
border-color: #405515;
|
||||
}
|
||||
.alert-danger {
|
||||
color: #f9c4c4;
|
||||
background-color: #402a28;
|
||||
border-color: #693a38;
|
||||
}
|
||||
.alert-warning {
|
||||
color: #f5d496;
|
||||
background-color: #3c2f19;
|
||||
border-color: #654815;
|
||||
}
|
||||
.alert-secondary, .alert-light {
|
||||
color: var(--sc-ink) !important;
|
||||
background-color: var(--sc-cap) !important;
|
||||
border-color: var(--sc-divider) !important;
|
||||
}
|
||||
|
||||
.alert .btn-close {
|
||||
filter: invert(1) grayscale(100%) brightness(200%);
|
||||
}
|
||||
|
||||
/* slider */
|
||||
.noUi-target {
|
||||
background: var(--sc-bg);
|
||||
border-color: var(--sc-line);
|
||||
box-shadow: none;
|
||||
}
|
||||
.noUi-handle {
|
||||
background: var(--sc-cap);
|
||||
border-color: var(--sc-line);
|
||||
box-shadow: none;
|
||||
}
|
||||
.noUi-handle::before, .noUi-handle::after {
|
||||
background: var(--sc-line);
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,93 +0,0 @@
|
||||
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -1,584 +0,0 @@
|
||||
/* servercow theme for mailcow, based on the servercow customer portal */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-regular.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-italic.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-italic.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-500.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-500.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-600.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-600.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto Slab';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-regular.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto Slab';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: local(''),
|
||||
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-500.woff2') format('woff2'),
|
||||
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* tokens */
|
||||
:root {
|
||||
--sc-brown: #6F5D49;
|
||||
--sc-brown-hover: #5e4f3e;
|
||||
--sc-brown-active: #594a3a;
|
||||
--sc-brown-soft: rgba(111, 93, 73, .2);
|
||||
--sc-brown-tint: rgba(111, 93, 73, .08);
|
||||
--sc-link: #6F5D49;
|
||||
--sc-green: #8AD608;
|
||||
--sc-blue: #96D0EC;
|
||||
--sc-warning: #e3950b;
|
||||
--sc-danger: #ef6f6f;
|
||||
--sc-danger-hover: #e85e5e;
|
||||
--sc-danger-text: #c43c3c;
|
||||
--sc-ink: #212529;
|
||||
--sc-muted: #6c757d;
|
||||
--sc-placeholder: #6c757d;
|
||||
--sc-bg: #F6F8F8;
|
||||
--sc-surface: #ffffff;
|
||||
--sc-cap: #f7f7f7;
|
||||
--sc-addon: #e9ecef;
|
||||
--sc-line: #ced4da;
|
||||
--sc-divider: #dee2e6;
|
||||
--sc-cap-line: rgba(0, 0, 0, .175);
|
||||
--sc-stripe: rgba(111, 93, 73, .04);
|
||||
--sc-hover: rgba(111, 93, 73, .08);
|
||||
--sc-focus-border: #b7aea4;
|
||||
--sc-focus: rgba(111, 93, 73, .25);
|
||||
--sc-shadow: 0 0 10px 0 rgba(134, 134, 134, .3);
|
||||
--sc-font: 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
--sc-font-head: 'Roboto Slab', Georgia, serif;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-bg: #F6F8F8;
|
||||
--bs-body-font-family: var(--sc-font);
|
||||
/* core tokens, see css/build/014-mailcow.css */
|
||||
--mc-font-family: var(--sc-font);
|
||||
--mc-font-size: 10pt;
|
||||
--mc-body-bg: var(--sc-bg);
|
||||
--mc-primary: var(--sc-brown);
|
||||
--mc-primary-border: var(--sc-brown);
|
||||
--mc-info: var(--sc-blue);
|
||||
--mc-info-border: var(--sc-blue);
|
||||
--mc-border: var(--sc-divider);
|
||||
--mc-surface: var(--sc-surface);
|
||||
--mc-surface-alt: var(--sc-cap);
|
||||
--mc-muted: var(--sc-muted);
|
||||
--mc-hr: var(--sc-divider);
|
||||
--mc-progress-bg: var(--sc-addon);
|
||||
--mc-placeholder: var(--sc-placeholder);
|
||||
--mc-btn-outline-color: var(--sc-ink);
|
||||
--mc-btn-outline-border: var(--sc-line);
|
||||
--mc-btn-outline-hover-bg: var(--sc-brown-tint);
|
||||
--mc-btn-outline-active-bg: var(--sc-brown);
|
||||
--mc-btn-light-active-color: #fff;
|
||||
--mc-btn-light-active-bg: var(--sc-brown);
|
||||
--mc-btn-light-active-border: var(--sc-brown);
|
||||
--mc-dtr-bg: var(--sc-surface);
|
||||
--mc-dtr-border: var(--sc-line);
|
||||
--mc-dtr-color: var(--sc-brown);
|
||||
--mc-dtr-hover-bg: var(--sc-brown-tint);
|
||||
--mc-dtr-active-bg: var(--sc-brown-soft);
|
||||
--mc-login-user: var(--sc-green);
|
||||
--mc-login-domainadmin: #f5c400;
|
||||
--mc-login-admin: var(--sc-danger);
|
||||
}
|
||||
|
||||
/* base */
|
||||
body {
|
||||
color: var(--sc-ink);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6, legend, .modal-title {
|
||||
font-family: var(--sc-font-head);
|
||||
font-weight: 500;
|
||||
color: var(--sc-ink);
|
||||
}
|
||||
a {
|
||||
color: var(--sc-link);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--sc-brown-hover);
|
||||
}
|
||||
.text-muted, .text-secondary {
|
||||
color: var(--sc-muted) !important;
|
||||
}
|
||||
::selection {
|
||||
background: var(--sc-brown-soft);
|
||||
}
|
||||
hr {
|
||||
border-color: var(--sc-divider);
|
||||
opacity: 1;
|
||||
}
|
||||
::placeholder {
|
||||
color: var(--sc-placeholder) !important;
|
||||
}
|
||||
|
||||
/* navbar, white with green line */
|
||||
.navbar.bg-light {
|
||||
background-color: var(--sc-surface) !important;
|
||||
border: 0;
|
||||
border-bottom: 5px solid var(--sc-green);
|
||||
}
|
||||
.navbar-light .navbar-nav .nav-link {
|
||||
color: var(--sc-ink);
|
||||
}
|
||||
.navbar-light .navbar-nav .nav-link:hover,
|
||||
.navbar-light .navbar-nav .nav-link:focus,
|
||||
.navbar-light .navbar-nav .nav-link.active,
|
||||
.navbar-light .navbar-nav .show > .nav-link {
|
||||
color: var(--sc-brown);
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.navbar-nav .nav-link {
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
.dropdown-menu {
|
||||
border: 1px solid var(--sc-cap-line);
|
||||
border-radius: 0;
|
||||
box-shadow: var(--sc-shadow);
|
||||
}
|
||||
.dropdown-item {
|
||||
color: var(--sc-ink);
|
||||
}
|
||||
.dropdown-item:hover, .dropdown-item:focus {
|
||||
color: var(--sc-brown) !important;
|
||||
background-color: var(--sc-brown-soft);
|
||||
}
|
||||
.dropdown-item.active, .dropdown-item:active {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
}
|
||||
|
||||
/* cards, white with soft shadow */
|
||||
.card {
|
||||
border: 0 !important;
|
||||
border-radius: 0;
|
||||
background-color: var(--sc-surface);
|
||||
box-shadow: var(--sc-shadow);
|
||||
}
|
||||
.card-header, .card-header:first-child {
|
||||
font-family: var(--sc-font-head);
|
||||
font-weight: 500;
|
||||
font-size: 1.1rem;
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-cap);
|
||||
border-bottom: 1px solid var(--sc-cap-line);
|
||||
border-radius: 0;
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
.card-header .btn, .card-header .form-control, .card-header .form-select {
|
||||
font-family: var(--sc-font);
|
||||
font-size: .875rem;
|
||||
}
|
||||
.card-footer {
|
||||
background-color: var(--sc-cap);
|
||||
border-top: 1px solid var(--sc-cap-line);
|
||||
border-radius: 0;
|
||||
}
|
||||
.card .card {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--sc-divider) !important;
|
||||
}
|
||||
/* colored headers become a top line */
|
||||
.card:has(> .card-header.bg-danger) {
|
||||
border-top: 4px solid var(--sc-danger) !important;
|
||||
}
|
||||
.card-header.bg-danger, .card-header.bg-success, .card-header.bg-primary, .card-header.bg-info {
|
||||
color: var(--sc-ink) !important;
|
||||
background-color: var(--sc-cap) !important;
|
||||
}
|
||||
.list-group-item {
|
||||
border-color: var(--sc-divider);
|
||||
}
|
||||
|
||||
/* modals */
|
||||
.modal-content {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 8px 40px rgba(0, 0, 0, .2);
|
||||
}
|
||||
.modal-header {
|
||||
background-color: var(--sc-cap);
|
||||
border-bottom: 1px solid var(--sc-cap-line);
|
||||
border-radius: 0;
|
||||
}
|
||||
.modal-footer {
|
||||
border-top: 1px solid var(--sc-divider);
|
||||
}
|
||||
|
||||
/* buttons, flat and square */
|
||||
.btn {
|
||||
border-width: 1px;
|
||||
border-radius: 0;
|
||||
font-weight: 400;
|
||||
transition: none;
|
||||
}
|
||||
.btn:not(.disabled):hover,
|
||||
.btn:not(.disabled):active {
|
||||
margin-top: 0;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
.btn:focus, .btn-check:focus + .btn {
|
||||
box-shadow: 0 0 0 .2rem var(--sc-focus);
|
||||
}
|
||||
.btn-primary, .btn-primary:hover, .btn-primary:focus, .btn-primary:active {
|
||||
color: #fff;
|
||||
}
|
||||
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
|
||||
background-color: var(--sc-brown-hover) !important;
|
||||
border-color: var(--sc-brown-active) !important;
|
||||
}
|
||||
/* main call to action, portal primary */
|
||||
.btn-success, .btn-success:not(.disabled):hover, .btn-success:not(.disabled):active, .btn-success:focus,
|
||||
.btn-success:disabled, .btn-success.disabled {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.btn-success:hover, .btn-success:active, .btn-success:focus {
|
||||
background-color: var(--sc-brown-hover);
|
||||
border-color: var(--sc-brown-active);
|
||||
}
|
||||
.btn-danger, .btn-danger:not(.disabled):hover, .btn-danger:not(.disabled):active, .btn-danger:focus,
|
||||
.btn-danger:disabled, .btn-danger.disabled {
|
||||
color: #000;
|
||||
background-color: var(--sc-danger);
|
||||
border-color: var(--sc-danger);
|
||||
}
|
||||
.btn-danger:hover, .btn-danger:active, .btn-danger:focus {
|
||||
background-color: var(--sc-danger-hover);
|
||||
border-color: var(--sc-danger-hover);
|
||||
}
|
||||
.btn-warning, .btn-warning:not(.disabled):hover, .btn-warning:not(.disabled):active, .btn-warning:focus {
|
||||
color: #000;
|
||||
background-color: var(--sc-warning);
|
||||
border-color: var(--sc-warning);
|
||||
}
|
||||
.bg-info, .alert-info, .btn-info, .badge.bg-info > a {
|
||||
color: #000 !important;
|
||||
}
|
||||
.btn-secondary, .btn-secondary:not(.disabled):hover, .btn-secondary:not(.disabled):active, .btn-secondary:focus,
|
||||
.btn-light, .btn-light:not(.disabled):hover, .btn-light:not(.disabled):active, .btn-light:focus {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-surface);
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
.btn-secondary:hover, .btn-secondary:active, .btn-secondary:focus,
|
||||
.btn-light:hover, .btn-light:focus {
|
||||
color: var(--sc-brown);
|
||||
background-color: var(--sc-brown-tint);
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
.btn-check:checked + .btn-secondary, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.btn-check:checked + .btn-outline-secondary, .btn-outline-secondary.active {
|
||||
color: #fff !important;
|
||||
border-color: var(--sc-brown) !important;
|
||||
}
|
||||
.btn-outline-primary {
|
||||
color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.btn-outline-primary:hover {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
/* active toggle, e.g. tagging options */
|
||||
.btn-dark, .btn-dark:hover, .btn-dark:focus, .btn-dark:active {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.btn-xs, .btn-group-xs > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* forms */
|
||||
.form-control, .form-select {
|
||||
border-color: var(--sc-line);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-surface);
|
||||
}
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--sc-focus-border);
|
||||
box-shadow: 0 0 0 .25rem var(--sc-focus);
|
||||
}
|
||||
.form-control::file-selector-button {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-addon);
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: var(--sc-brown);
|
||||
background-color: var(--sc-brown-soft);
|
||||
}
|
||||
.input-group-text {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-addon);
|
||||
border-color: var(--sc-line);
|
||||
border-radius: 0;
|
||||
}
|
||||
.input-group > :not(:first-child), .input-group > :not(:last-child) {
|
||||
border-radius: 0;
|
||||
}
|
||||
.form-check-input {
|
||||
border-color: var(--sc-line);
|
||||
}
|
||||
.form-check-input:checked {
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.form-check-input:focus {
|
||||
border-color: var(--sc-focus-border);
|
||||
box-shadow: 0 0 0 .25rem var(--sc-focus);
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle {
|
||||
border-radius: 0;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:active {
|
||||
color: var(--mc-placeholder);
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a.active, .bootstrap-select .dropdown-menu li a:active {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
}
|
||||
div.numberedtextarea-number {
|
||||
color: var(--sc-muted);
|
||||
}
|
||||
|
||||
/* nav tabs */
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid var(--sc-divider);
|
||||
}
|
||||
.nav-tabs .nav-link,
|
||||
.nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:hover, .nav-tabs .nav-link.disabled:focus {
|
||||
margin-top: 0;
|
||||
color: var(--sc-ink);
|
||||
border: 0;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
}
|
||||
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus {
|
||||
padding-bottom: .5rem;
|
||||
color: var(--sc-brown);
|
||||
}
|
||||
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link,
|
||||
.nav-tabs .nav-link:not(.disabled).active {
|
||||
padding-bottom: .5rem;
|
||||
color: var(--sc-brown);
|
||||
background: none;
|
||||
border-bottom: 3px solid var(--sc-brown);
|
||||
}
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0;
|
||||
color: var(--sc-ink);
|
||||
}
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
background-color: var(--sc-brown);
|
||||
}
|
||||
|
||||
/* tables */
|
||||
.table {
|
||||
--bs-table-bg: transparent;
|
||||
--bs-table-striped-bg: var(--sc-stripe);
|
||||
--bs-table-striped-color: var(--sc-ink);
|
||||
--bs-table-hover-color: var(--sc-ink);
|
||||
--bs-table-hover-bg: var(--sc-hover);
|
||||
color: var(--sc-ink);
|
||||
border-color: var(--sc-divider);
|
||||
}
|
||||
.table > thead > tr > th, table.dataTable thead th {
|
||||
color: var(--sc-ink);
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--sc-line);
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr.odd > * {
|
||||
box-shadow: inset 0 0 0 9999px var(--sc-stripe);
|
||||
}
|
||||
table.dataTable.table-hover > tbody > tr:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px var(--sc-hover);
|
||||
}
|
||||
.table > :not(:first-child) {
|
||||
border-top: 0;
|
||||
}
|
||||
.dataTables_wrapper .dataTables_filter input,
|
||||
.dataTables_wrapper .dataTables_length select {
|
||||
border: 1px solid var(--sc-line);
|
||||
border-radius: 0;
|
||||
}
|
||||
.pagination > li > a, .pagination > li > span, .pagination .page-link,
|
||||
.pagination > li > a:hover, .pagination > li > a:focus, .pagination > li > a:active {
|
||||
top: 0;
|
||||
color: var(--sc-brown);
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
background-color: var(--sc-surface);
|
||||
border: 1px solid var(--sc-divider);
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.pagination > li > a:hover, .pagination > li > a:focus {
|
||||
background-color: var(--sc-brown-tint);
|
||||
}
|
||||
.pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:active,
|
||||
.page-item.disabled .page-link {
|
||||
color: var(--sc-muted);
|
||||
background-color: var(--sc-surface);
|
||||
border-width: 1px;
|
||||
border-color: var(--sc-divider);
|
||||
}
|
||||
.page-item.active .page-link {
|
||||
color: #fff;
|
||||
background-color: var(--sc-brown);
|
||||
border-color: var(--sc-brown);
|
||||
}
|
||||
.breadcrumb {
|
||||
border: 1px solid var(--sc-divider);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* alerts like the portal */
|
||||
.alert {
|
||||
border: 1px solid;
|
||||
border-radius: 0;
|
||||
}
|
||||
.alert-primary {
|
||||
color: #43382c !important;
|
||||
background-color: #e2dfdb !important;
|
||||
border-color: #d4cec8 !important;
|
||||
}
|
||||
.alert-info {
|
||||
color: #3c535e !important;
|
||||
background-color: #eaf6fb !important;
|
||||
border-color: #e0f1f9 !important;
|
||||
}
|
||||
.alert-success {
|
||||
color: #375603;
|
||||
background-color: #e8f7ce;
|
||||
border-color: #dcf3b5;
|
||||
}
|
||||
.alert-danger {
|
||||
color: #8f4343;
|
||||
background-color: #fce2e2;
|
||||
border-color: #fad4d4;
|
||||
}
|
||||
.alert-warning {
|
||||
color: #885907;
|
||||
background-color: #f9eace;
|
||||
border-color: #f7dfb6;
|
||||
}
|
||||
.alert-secondary, .alert-light {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-cap);
|
||||
border-color: var(--sc-divider);
|
||||
}
|
||||
.alert .alert-link {
|
||||
color: inherit;
|
||||
}
|
||||
.alert .btn-close {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* badges and semantic colors, readable text on top */
|
||||
.badge {
|
||||
font-weight: 500;
|
||||
border-radius: 0;
|
||||
}
|
||||
.bg-success {
|
||||
color: #000 !important;
|
||||
background-color: var(--sc-green) !important;
|
||||
}
|
||||
.bg-warning {
|
||||
color: #000 !important;
|
||||
background-color: var(--sc-warning) !important;
|
||||
}
|
||||
.bg-danger {
|
||||
color: #000 !important;
|
||||
background-color: var(--sc-danger) !important;
|
||||
}
|
||||
.badge.bg-secondary, .badge.bg-light {
|
||||
color: var(--sc-ink);
|
||||
background-color: var(--sc-addon) !important;
|
||||
}
|
||||
.text-success {
|
||||
color: #4f8a0b !important;
|
||||
}
|
||||
.text-danger {
|
||||
color: var(--sc-danger-text) !important;
|
||||
}
|
||||
.text-warning {
|
||||
color: #9a6100 !important;
|
||||
}
|
||||
.progress {
|
||||
border-radius: 0;
|
||||
}
|
||||
.progress-bar {
|
||||
color: #000;
|
||||
background-color: var(--sc-green);
|
||||
}
|
||||
.progress-bar.bg-danger, .progress-bar-danger {
|
||||
color: #000;
|
||||
background-color: var(--sc-danger) !important;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
color: #000;
|
||||
background-color: var(--sc-warning) !important;
|
||||
}
|
||||
|
||||
|
||||
/* footer */
|
||||
.footer, .footer a {
|
||||
color: var(--sc-muted);
|
||||
}
|
||||
.footer hr {
|
||||
border-color: var(--sc-divider);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "servercow",
|
||||
"description": "Based on the servercow theme"
|
||||
}
|
||||
@@ -380,13 +380,13 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
else {
|
||||
$state = state_nomatch;
|
||||
}
|
||||
$state .= '<br />' . htmlspecialchars($current[$data_field[$current['type']]]);
|
||||
$state .= '<br />' . $current[$data_field[$current['type']]];
|
||||
}
|
||||
if ($current['type'] == 'TXT' &&
|
||||
stripos($current['txt'], 'v=dmarc') === 0 &&
|
||||
$record[2] == $dmarc_link) {
|
||||
$current['txt'] = str_replace(' ', '', $current['txt']);
|
||||
$state = htmlspecialchars($current[$data_field[$current['type']]]) . state_optional;
|
||||
$state = $current[$data_field[$current['type']]] . state_optional;
|
||||
}
|
||||
elseif ($current['type'] == 'TXT' &&
|
||||
stripos($current['txt'], 'v=spf') === 0 &&
|
||||
@@ -396,7 +396,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
if (in_array($ip, $rslt) && in_array(expand_ipv6($ip6), $rslt)) {
|
||||
$state = state_good;
|
||||
}
|
||||
$state .= '<br />' . htmlspecialchars($current[$data_field[$current['type']]]) . state_optional;
|
||||
$state .= '<br />' . $current[$data_field[$current['type']]] . state_optional;
|
||||
}
|
||||
elseif ($current['type'] == 'TXT' &&
|
||||
stripos($current['txt'], 'v=dkim') === 0 &&
|
||||
@@ -426,7 +426,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
if ($state == state_nomatch) {
|
||||
$state = array();
|
||||
foreach ($currents as $current) {
|
||||
$state[] = htmlspecialchars($current[$data_field[$current['type']]]);
|
||||
$state[] = $current[$data_field[$current['type']]];
|
||||
}
|
||||
$state = implode('<br />', $state);
|
||||
}
|
||||
@@ -436,22 +436,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
<td>%s</td>
|
||||
<td class="dns-found">%s</td>
|
||||
<td class="dns-recommended">%s</td>
|
||||
</tr>', htmlspecialchars($record[0]), htmlspecialchars($record[1]), $record[2], $state);
|
||||
</tr>', $record[0], $record[1], $record[2], $state);
|
||||
$record[3] = explode('<br />', $state);
|
||||
}
|
||||
|
||||
unset($record);
|
||||
|
||||
// Make a hostname RHS absolute so it is not read relative to $ORIGIN.
|
||||
// Already-absolute names, the SRV root target "." and IP addresses are left alone.
|
||||
$absolutize = function($host) {
|
||||
$host = trim($host);
|
||||
if ($host === '' || $host === '.' || substr($host, -1) === '.' || filter_var($host, FILTER_VALIDATE_IP)) {
|
||||
return $host;
|
||||
}
|
||||
return $host . '.';
|
||||
};
|
||||
|
||||
$dns_data = sprintf("\$ORIGIN %s.\n", $domain);
|
||||
foreach ($records as $record) {
|
||||
if ($domain == substr($record[0], -strlen($domain))) {
|
||||
@@ -472,32 +462,22 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
$val = str_replace(state_optional, '', $val);
|
||||
$val = str_replace(state_good, '', $val);
|
||||
if (strlen($val) > 0) {
|
||||
// these are all TXT values, their RHS is a character string, not a name
|
||||
$vals[] = sprintf("%s\tIN\t%s\t%s\n", $label, $record[1], $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($record[1] == 'MX' || $record[1] == 'CNAME') {
|
||||
$val = $absolutize($val);
|
||||
}
|
||||
elseif ($record[1] == 'SRV') {
|
||||
// format here is "target port"; only the target is a name
|
||||
$parts = explode(' ', $val, 2);
|
||||
$parts[0] = $absolutize($parts[0]);
|
||||
$val = implode(' ', $parts);
|
||||
}
|
||||
$vals[] = sprintf("%s\tIN\t%s\t%s\n", $label, $record[1], $val);
|
||||
}
|
||||
|
||||
foreach ($vals as $val) {
|
||||
$dns_data .= $val;
|
||||
$dns_data .= str_replace($domain, $domain . '.', $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
<a id='download-zonefile' class="btn btn-sm btn-secondary visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline mb-4" style="margin-top:10px" data-zonefile="<?=base64_encode($dns_data);?>" download='<?=htmlspecialchars($_GET['domain']);?>.txt' type='text/csv'>Download</a>
|
||||
<a id='download-zonefile' class="btn btn-sm btn-secondary visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline mb-4" style="margin-top:10px" data-zonefile="<?=base64_encode($dns_data);?>" download='<?=$_GET['domain'];?>.txt' type='text/csv'>Download</a>
|
||||
<script>
|
||||
var zonefile_dl_link = document.getElementById('download-zonefile');
|
||||
var zonefile = atob(zonefile_dl_link.getAttribute('data-zonefile'));
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Admins only
|
||||
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] !== 'admin') {
|
||||
http_response_code(403);
|
||||
echo json_encode(array('status' => 'error', 'message' => 'access denied'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$owner = $GLOBALS['MAILCOW_GIT_OWNER'];
|
||||
$repo = $GLOBALS['MAILCOW_GIT_REPO'];
|
||||
$current = $GLOBALS['MAILCOW_GIT_VERSION'];
|
||||
|
||||
// Cache key is bound to the running version, so an update invalidates it naturally
|
||||
$cache_key = 'MAILCOW_UPDATE_CHECK/' . $current;
|
||||
|
||||
// Serve cached result if present (one GitHub call per TTL, not one per page load)
|
||||
$cached = $redis->get($cache_key);
|
||||
if ($cached !== false) {
|
||||
echo $cached;
|
||||
exit;
|
||||
}
|
||||
|
||||
// GitHub requires a User-Agent and rate-limits unauthenticated requests to 60/h per IP
|
||||
function github_get($url) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, array(
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_USERAGENT => 'mailcow-update-check',
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_HTTPHEADER => array('Accept: application/vnd.github+json'),
|
||||
));
|
||||
$body = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($body === false || $code !== 200) return false;
|
||||
$json = json_decode($body, true);
|
||||
return is_array($json) ? $json : false;
|
||||
}
|
||||
|
||||
$latest = github_get("https://api.github.com/repos/{$owner}/{$repo}/releases/latest");
|
||||
$release = github_get("https://api.github.com/repos/{$owner}/{$repo}/releases/tags/{$current}");
|
||||
|
||||
// Any failure (rate limit, offline, unexpected payload) -> honest error, cached briefly
|
||||
if ($latest === false || $release === false ||
|
||||
empty($latest['tag_name']) || empty($latest['created_at']) || empty($release['created_at'])) {
|
||||
$result = json_encode(array('status' => 'error', 'message' => 'could not reach GitHub API'));
|
||||
$redis->setex($cache_key, 300, $result);
|
||||
echo $result;
|
||||
exit;
|
||||
}
|
||||
|
||||
$date_latest = strtotime($latest['created_at']);
|
||||
$date_current = strtotime($release['created_at']);
|
||||
|
||||
if ($date_latest <= $date_current) {
|
||||
$result = json_encode(array('status' => 'no_update'));
|
||||
} else {
|
||||
$result = json_encode(array('status' => 'update_available', 'latest_tag' => $latest['tag_name']));
|
||||
}
|
||||
|
||||
// Cache the positive result for an hour
|
||||
$redis->setex($cache_key, 3600, $result);
|
||||
echo $result;
|
||||
@@ -287,8 +287,6 @@ function user_login($user, $pass, $extra = null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$row['attributes'] = json_decode($row['attributes'], true);
|
||||
|
||||
// check for tfa authenticators
|
||||
$authenticators = get_tfa($user);
|
||||
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
|
||||
@@ -345,8 +343,6 @@ function user_login($user, $pass, $extra = null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$row['attributes'] = json_decode($row['attributes'], true);
|
||||
|
||||
// check for tfa authenticators
|
||||
$authenticators = get_tfa($user);
|
||||
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
|
||||
|
||||
@@ -233,38 +233,6 @@ function customize($_action, $_item, $_data = null) {
|
||||
'msg' => 'custom_login_modified'
|
||||
);
|
||||
break;
|
||||
case 'ui_theme':
|
||||
$ui_theme = is_array($_data['ui_theme']) ? (string)reset($_data['ui_theme']) : (string)$_data['ui_theme'];
|
||||
if ($ui_theme !== '' && !array_key_exists($ui_theme, customize('get', 'ui_themes'))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => 'ui_theme_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if ($ui_theme === '') {
|
||||
$redis->del('UI_THEME');
|
||||
}
|
||||
else {
|
||||
$redis->set('UI_THEME', $ui_theme);
|
||||
}
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => array('redis_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => 'ui_theme_modified'
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
@@ -434,42 +402,6 @@ function customize($_action, $_item, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'ui_themes':
|
||||
// theme folders in css/themes/<name>/ with a theme.css
|
||||
$themes = array();
|
||||
foreach (glob('/web/css/themes/*/theme.css') as $theme_css) {
|
||||
$theme_dir = dirname($theme_css);
|
||||
$theme = basename($theme_dir);
|
||||
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $theme)) {
|
||||
continue;
|
||||
}
|
||||
$meta = json_decode(@file_get_contents($theme_dir . '/theme.json'), true);
|
||||
$themes[$theme] = array(
|
||||
'name' => (!empty($meta['name'])) ? $meta['name'] : $theme,
|
||||
'description' => (!empty($meta['description'])) ? $meta['description'] : '',
|
||||
'dark' => file_exists($theme_dir . '/dark.css')
|
||||
);
|
||||
}
|
||||
ksort($themes);
|
||||
return $themes;
|
||||
break;
|
||||
case 'ui_theme':
|
||||
try {
|
||||
$ui_theme = $redis->get('UI_THEME');
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => array('redis_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($ui_theme) || !preg_match('/^[a-zA-Z0-9_-]+$/', $ui_theme) || !file_exists('/web/css/themes/' . $ui_theme . '/theme.css')) {
|
||||
return '';
|
||||
}
|
||||
return $ui_theme;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -429,25 +429,14 @@ function domain_admin_sso($_action, $_data) {
|
||||
|
||||
switch ($_action) {
|
||||
case 'check':
|
||||
$token = preg_replace('/[^a-zA-Z0-9-]/', '', $_data);
|
||||
$token = $_data;
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
|
||||
$stmt->execute(array(
|
||||
':token' => $token
|
||||
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
|
||||
));
|
||||
$return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (empty($return['username'])) {
|
||||
return false;
|
||||
}
|
||||
// single-use: consume the token; if a concurrent request already used it, deny
|
||||
$del = $pdo->prepare("DELETE FROM `da_sso` WHERE `token` = :token");
|
||||
$del->execute(array(
|
||||
':token' => $token
|
||||
));
|
||||
if ($del->rowCount() < 1) {
|
||||
return false;
|
||||
}
|
||||
return $return['username'];
|
||||
return empty($return['username']) ? false : $return['username'];
|
||||
case 'issue':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
|
||||
@@ -108,14 +108,6 @@ function fwdhost($_action, $_data = null) {
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$hosts = (array)$_data['forwardinghost'];
|
||||
foreach ($hosts as $host) {
|
||||
try {
|
||||
|
||||
@@ -53,39 +53,6 @@ function valid_network($network) {
|
||||
function valid_hostname($hostname) {
|
||||
return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
|
||||
}
|
||||
// Validates a browser-style Origin: scheme://host[:port], no path/query/fragment/credentials
|
||||
function valid_origin($origin) {
|
||||
$parts = parse_url($origin);
|
||||
if ($parts === false || !isset($parts['scheme']) || !isset($parts['host'])) {
|
||||
return false;
|
||||
}
|
||||
if (!in_array(strtolower($parts['scheme']), array('http', 'https'), true)) {
|
||||
return false;
|
||||
}
|
||||
if (isset($parts['user']) || isset($parts['pass']) || isset($parts['path']) || isset($parts['query']) || isset($parts['fragment'])) {
|
||||
return false;
|
||||
}
|
||||
$host = $parts['host'];
|
||||
$host_without_brackets = trim($host, '[]');
|
||||
if (!valid_hostname($host_without_brackets) && !filter_var($host_without_brackets, FILTER_VALIDATE_IP)) {
|
||||
return false;
|
||||
}
|
||||
// Reject anything that doesn't round-trip to the exact same origin (e.g. stray trailing slash)
|
||||
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
|
||||
$rebuilt = strtolower($parts['scheme']) . '://' . strtolower($host) . $port;
|
||||
return strtolower($origin) === $rebuilt;
|
||||
}
|
||||
// Bring an origin into the exact form a browser sends it (scheme://host[:port], lowercase).
|
||||
function normalize_cors_origin($origin) {
|
||||
$origin = trim($origin);
|
||||
if ($origin === '*') {
|
||||
return '*';
|
||||
}
|
||||
if ($origin !== '' && !preg_match('~^[a-z][a-z0-9+.-]*://~i', $origin)) {
|
||||
$origin = 'https://' . $origin;
|
||||
}
|
||||
return valid_origin($origin) ? strtolower($origin) : false;
|
||||
}
|
||||
// Thanks to https://stackoverflow.com/a/49373789
|
||||
// Validates exact ip matches and ip-in-cidr, ipv4 and ipv6
|
||||
function ip_acl($ip, $networks) {
|
||||
@@ -591,7 +558,7 @@ function logger($_data = false) {
|
||||
$type = $return['type'];
|
||||
$msg = null;
|
||||
if (isset($return['msg'])) {
|
||||
$msg = json_encode($return['msg'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
|
||||
$msg = json_encode($return['msg'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
$call = null;
|
||||
if (isset($return['log'])) {
|
||||
@@ -633,18 +600,6 @@ function logger($_data = false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function is_local_mailcow_domain($domain) {
|
||||
// True if domain is a locally managed, active primary or alias domain
|
||||
global $pdo;
|
||||
$domain = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
|
||||
if (empty($domain)) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT 1 FROM `domain` WHERE `domain` = :d AND `active` = 1
|
||||
UNION SELECT 1 FROM `alias_domain` WHERE `alias_domain` = :d2 AND `active` = 1 LIMIT 1");
|
||||
$stmt->execute(array(':d' => $domain, ':d2' => $domain));
|
||||
return (bool)$stmt->fetchColumn();
|
||||
}
|
||||
function hasDomainAccess($username, $role, $domain) {
|
||||
global $pdo;
|
||||
if (empty($domain) || !is_valid_domain_name($domain)) {
|
||||
@@ -2328,13 +2283,10 @@ function cors($action, $data = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array(getBaseURL());
|
||||
$allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array($_SERVER['SERVER_NAME']);
|
||||
$allowed_origins = !is_array($allowed_origins) ? array_filter(array_map('trim', explode("\n", $allowed_origins))) : $allowed_origins;
|
||||
foreach ($allowed_origins as &$origin) {
|
||||
if ($origin === '*') {
|
||||
continue;
|
||||
}
|
||||
if (!valid_origin($origin)) {
|
||||
foreach ($allowed_origins as $origin) {
|
||||
if (!filter_var($origin, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) && $origin != '*') {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $action, $data),
|
||||
@@ -2342,10 +2294,7 @@ function cors($action, $data = null) {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// browsers always send a lowercase scheme/host in the Origin header, so normalize to match
|
||||
$origin = strtolower($origin);
|
||||
}
|
||||
unset($origin);
|
||||
|
||||
$allowed_methods = isset($data['allowed_methods']) ? $data['allowed_methods'] : array('GET', 'POST', 'PUT', 'DELETE');
|
||||
$allowed_methods = !is_array($allowed_methods) ? array_map('trim', preg_split( "/( |,|;|\n)/", $allowed_methods)) : $allowed_methods;
|
||||
@@ -2393,35 +2342,26 @@ function cors($action, $data = null) {
|
||||
);
|
||||
}
|
||||
|
||||
$cors_settings = !$cors_settings ? array('allowed_origins' => getBaseURL(), 'allowed_methods' => 'GET, POST, PUT, DELETE') : $cors_settings;
|
||||
$cors_settings['allowed_origins'] = empty($cors_settings['allowed_origins']) ? getBaseURL() : $cors_settings['allowed_origins'];
|
||||
$cors_settings = !$cors_settings ? array('allowed_origins' => $_SERVER['SERVER_NAME'], 'allowed_methods' => 'GET, POST, PUT, DELETE') : $cors_settings;
|
||||
$cors_settings['allowed_origins'] = empty($cors_settings['allowed_origins']) ? $_SERVER['SERVER_NAME'] : $cors_settings['allowed_origins'];
|
||||
$cors_settings['allowed_methods'] = empty($cors_settings['allowed_methods']) ? 'GET, POST, PUT, DELETE, OPTION' : $cors_settings['allowed_methods'];
|
||||
|
||||
return $cors_settings;
|
||||
break;
|
||||
case "set_headers":
|
||||
$cors_settings = cors('get');
|
||||
// normalize the stored list; it may still hold bare hostnames written before origins were validated as origins
|
||||
$allowed_origins = array_filter(array_map('normalize_cors_origin', explode(',', $cors_settings['allowed_origins'])));
|
||||
$origin = isset($_SERVER['HTTP_ORIGIN']) ? normalize_cors_origin($_SERVER['HTTP_ORIGIN']) : false;
|
||||
// check if requested origin is in allowed origins
|
||||
$allow_origin = null;
|
||||
if (in_array('*', $allowed_origins, true)) {
|
||||
$allow_origin = '*';
|
||||
} else if ($origin !== false && in_array($origin, $allowed_origins, true)) {
|
||||
$allow_origin = $origin;
|
||||
$allowed_origins = explode(', ', $cors_settings['allowed_origins']);
|
||||
$cors_settings['allowed_origins'] = $allowed_origins[0];
|
||||
if (in_array('*', $allowed_origins)){
|
||||
$cors_settings['allowed_origins'] = '*';
|
||||
} else if (array_key_exists('HTTP_ORIGIN', $_SERVER) && in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)) {
|
||||
$cors_settings['allowed_origins'] = $_SERVER['HTTP_ORIGIN'];
|
||||
}
|
||||
// always allow OPTIONS for preflight request
|
||||
$cors_settings["allowed_methods"] = empty($cors_settings["allowed_methods"]) ? 'OPTIONS' : $cors_settings["allowed_methods"] . ', ' . 'OPTIONS';
|
||||
|
||||
// a disallowed origin gets no Access-Control-Allow-Origin at all; echoing a different origin than the requesting one tells a browser nothing
|
||||
if ($allow_origin !== null) {
|
||||
header('Access-Control-Allow-Origin: ' . $allow_origin);
|
||||
}
|
||||
if ($allow_origin !== '*') {
|
||||
// the response depends on the request origin, keep caches from mixing them up
|
||||
header('Vary: Origin', false);
|
||||
}
|
||||
header('Access-Control-Allow-Origin: ' . $cors_settings['allowed_origins']);
|
||||
header('Access-Control-Allow-Methods: '. $cors_settings['allowed_methods']);
|
||||
header('Access-Control-Allow-Headers: Accept, Content-Type, X-Api-Key, Origin');
|
||||
|
||||
@@ -3563,16 +3503,7 @@ function protect_route($allowed_roles = ['admin', 'domainadmin', 'user'], $redir
|
||||
if (isset($redirects['unauthenticated'])) {
|
||||
header('Location: ' . $redirects['unauthenticated']);
|
||||
} else {
|
||||
// Send a deep link to the login page for its area instead of the user login at /,
|
||||
// e.g. /admin/dashboard -> /admin rather than /
|
||||
$request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
|
||||
if (strpos($request_uri, '/admin/') === 0) {
|
||||
header('Location: /admin');
|
||||
} elseif (strpos($request_uri, '/domainadmin/') === 0) {
|
||||
header('Location: /domainadmin');
|
||||
} else {
|
||||
header('Location: /');
|
||||
}
|
||||
header('Location: /');
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -41,15 +41,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
else {
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
}
|
||||
if (isset($_data["validity"])) {
|
||||
if (!filter_var($_data["validity"], FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 87600)))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'validity_missing'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (isset($_data["validity"]) && !filter_var($_data["validity"], FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 87600)))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'validity_missing'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Default to 1 yr
|
||||
@@ -62,8 +60,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$permanent = 0;
|
||||
}
|
||||
$domain = $_data['domain'];
|
||||
// spamalias.description is NOT NULL, an absent description must not become a null insert
|
||||
$description = $_data['description'] ?? '';
|
||||
$description = $_data['description'];
|
||||
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
|
||||
$valid_alias_domains = user_get_alias_details($username)['alias_domains'];
|
||||
if (!empty($valid_alias_domains)) {
|
||||
@@ -750,18 +747,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
|
||||
$goto_local_part = strstr($goto, '@', true);
|
||||
$goto = $goto_local_part.'@'.$goto_domain;
|
||||
// Deny external goto domains: global switch (all roles) overrides the per-DA ACL
|
||||
if (($GLOBALS['ALIAS_DISABLE_EXTERNAL_DOMAINS'] === true ||
|
||||
(isset($_SESSION['acl']['alias_external_goto']) && $_SESSION['acl']['alias_external_goto'] != "1")) &&
|
||||
!is_local_mailcow_domain($goto_domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('external_goto_denied', htmlspecialchars($goto))
|
||||
);
|
||||
unset($gotos[$i]);
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
|
||||
WHERE `kind` REGEXP 'location|thing|group'
|
||||
AND `username`= :goto");
|
||||
@@ -1126,21 +1111,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0;
|
||||
$quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
|
||||
$quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
|
||||
// Validate quarantine_category
|
||||
if (!in_array($quarantine_category, array('add_header', 'reject', 'all'))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'quarantine_category_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$quota_b = ($quota_m * 1048576);
|
||||
$attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : '';
|
||||
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
|
||||
$force_pw_update = 0;
|
||||
}
|
||||
if ($authsource == 'generic-oidc'){
|
||||
$force_tfa = 0;
|
||||
}
|
||||
$mailbox_attrs = json_encode(
|
||||
@@ -1757,15 +1731,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler']);
|
||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
|
||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
|
||||
// Validate quarantine_category
|
||||
if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'quarantine_category_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
||||
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
|
||||
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
|
||||
@@ -2095,14 +2060,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
return false;
|
||||
}
|
||||
foreach ($usernames as $username) {
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if ($_data['spam_score'] == "default") {
|
||||
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
|
||||
AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
|
||||
@@ -2727,19 +2684,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
unset($gotos[$i]);
|
||||
continue;
|
||||
}
|
||||
// Deny external goto domains: global switch (all roles) overrides the per-DA ACL
|
||||
$goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
|
||||
if (($GLOBALS['ALIAS_DISABLE_EXTERNAL_DOMAINS'] === true ||
|
||||
(isset($_SESSION['acl']['alias_external_goto']) && $_SESSION['acl']['alias_external_goto'] != "1")) &&
|
||||
!is_local_mailcow_domain($goto_domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('external_goto_denied', htmlspecialchars($goto))
|
||||
);
|
||||
unset($gotos[$i]);
|
||||
continue;
|
||||
}
|
||||
if ($goto == $address) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
@@ -3182,8 +3126,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
}
|
||||
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
|
||||
$force_pw_update = 0;
|
||||
}
|
||||
if ($authsource == 'generic-oidc'){
|
||||
$force_tfa = 0;
|
||||
}
|
||||
$pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email'];
|
||||
@@ -3533,6 +3475,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
// Track affected mailboxes for SOGo update
|
||||
$update_sogo_mailboxes[] = $username;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox_rename':
|
||||
$domain = $_data['domain'];
|
||||
@@ -3843,19 +3786,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : $is_now['tagged_mail_handler'];
|
||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
|
||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
|
||||
// Validate quarantine_category
|
||||
if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'quarantine_category_invalid'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
|
||||
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value'];
|
||||
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update'];
|
||||
$attr["force_tfa"] = isset($_data['force_tfa']) ? intval($_data['force_tfa']) : $is_now['force_tfa'];
|
||||
$attr["sogo_access"] = isset($_data['sogo_access']) ? intval($_data['sogo_access']) : $is_now['sogo_access'];
|
||||
$attr["active"] = isset($_data['active']) ? intval($_data['active']) : $is_now['active'];
|
||||
$attr["tls_enforce_in"] = isset($_data['tls_enforce_in']) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in'];
|
||||
@@ -5348,14 +5281,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$mailboxdata['rl_scope'] = 'domain';
|
||||
}
|
||||
$mailboxdata['is_relayed'] = $row['backupmx'];
|
||||
// Internal send-as ACL for this mailbox, mirrors the sender_acl field accepted by edit/mailbox
|
||||
$mailboxdata['sender_acl'] = array();
|
||||
$stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `external` = '0'");
|
||||
$stmt->execute(array(':username' => $_data));
|
||||
$sender_acl_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($sender_acl_rows as $sender_acl_row) {
|
||||
$mailboxdata['sender_acl'][] = $sender_acl_row['send_as'];
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `tag_name`
|
||||
FROM `tags_mailbox` WHERE `username`= :username");
|
||||
@@ -5683,11 +5608,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6083,11 +6005,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . str_replace('%', '\%', $username) . "/%' OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6169,6 +6088,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
// Track affected mailboxes for SOGo update
|
||||
$update_sogo_mailboxes[] = $username;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
@@ -6243,11 +6163,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $name
|
||||
));
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($name, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $name
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
|
||||
@@ -22,7 +22,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND user_acl.quarantine = 1
|
||||
AND rcpt IN (SELECT username FROM mailbox)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -65,7 +65,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND `user_acl`.`quarantine` = 1
|
||||
AND `username` IN (SELECT `username` FROM `mailbox`)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -170,8 +170,6 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $detail_row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$detail_row['msg'] = preg_replace('~^\.~m', '..', $detail_row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
@@ -182,7 +180,7 @@ function quarantine($_action, $_data = null) {
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
logger(array('return' => array(
|
||||
array(
|
||||
@@ -194,7 +192,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -467,19 +465,17 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$row['msg'] = preg_replace('~^\.~m', '..', $row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
array('250', 'RCPT TO: ' . $row['rcpt'] . chr(10)),
|
||||
array('250', 'DATA' . chr(10)),
|
||||
array('354', $row['msg'] . chr(10) . '.' . chr(10)),
|
||||
array('354', str_replace("\n.", '', $row['msg']) . chr(10) . '.' . chr(10)),
|
||||
array('250', 'QUIT' . chr(10)),
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
@@ -489,7 +485,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -837,7 +833,7 @@ function quarantine($_action, $_data = null) {
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
|
||||
@@ -22,9 +22,6 @@ if (preg_match("/debug/i", $_SERVER['REQUEST_URI'])) {
|
||||
if ($_SERVER['REQUEST_URI'] == '/') {
|
||||
$css_minifier->add('/web/css/site/index.css');
|
||||
}
|
||||
if (file_exists('/web/css/themes/'.$UI_THEME.'/theme.css')) {
|
||||
$css_minifier->add('/web/css/themes/'.$UI_THEME.'/theme.css');
|
||||
}
|
||||
|
||||
$hash = $css_minifier->getDataHash();
|
||||
$CSSPath = '/tmp/' . $hash . '.css';
|
||||
@@ -33,17 +30,6 @@ if(!file_exists($CSSPath)) {
|
||||
cleanupCSS($hash);
|
||||
}
|
||||
|
||||
// dark mode bundle, core dark mode + theme dark.css
|
||||
$dark_minifier = new CSSminifierExtended('/web/css/themes/mailcow-darkmode.css');
|
||||
if (file_exists('/web/css/themes/'.$UI_THEME.'/dark.css')) {
|
||||
$dark_minifier->add('/web/css/themes/'.$UI_THEME.'/dark.css');
|
||||
}
|
||||
$DarkCSSPath = '/tmp/' . $hash . '-' . $dark_minifier->getDataHash() . '.css';
|
||||
if(!file_exists($DarkCSSPath)) {
|
||||
$dark_minifier->minify($DarkCSSPath);
|
||||
}
|
||||
$dark_css_path = '/cache/' . basename($DarkCSSPath);
|
||||
|
||||
$mailcow_apps_processed = $MAILCOW_APPS;
|
||||
$app_links = customize('get', 'app_links');
|
||||
$app_links_processed = $app_links;
|
||||
@@ -91,7 +77,6 @@ $globalVariables = [
|
||||
'dual_login' => @$_SESSION['dual-login'],
|
||||
'ui_texts' => $UI_TEXTS,
|
||||
'css_path' => '/cache/'.basename($CSSPath),
|
||||
'dark_css_path' => $dark_css_path,
|
||||
'logo' => customize('get', 'main_logo'),
|
||||
'logo_dark' => customize('get', 'main_logo_dark'),
|
||||
'available_languages' => $AVAILABLE_LANGUAGES,
|
||||
@@ -104,7 +89,7 @@ $globalVariables = [
|
||||
'app_links' => $app_links,
|
||||
'app_links_processed' => $app_links_processed,
|
||||
'is_root_uri' => (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == '/'),
|
||||
'uri' => parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?: '/',
|
||||
'uri' => $_SERVER['REQUEST_URI'],
|
||||
];
|
||||
|
||||
foreach ($globalVariables as $globalVariableName => $globalVariableValue) {
|
||||
|
||||
@@ -4,7 +4,7 @@ function init_db_schema()
|
||||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "18082026_1200";
|
||||
$db_version = "19022026_1220";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
@@ -719,8 +719,7 @@ function init_db_schema()
|
||||
"alias_domains" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"mailbox_relayhost" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"domain_relayhost" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"domain_desc" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"alias_external_goto" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||
"domain_desc" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
||||
),
|
||||
"keys" => array(
|
||||
"primary" => array(
|
||||
@@ -1465,8 +1464,6 @@ function init_db_schema()
|
||||
"pop3_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['pop3_access']),
|
||||
"smtp_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['smtp_access']),
|
||||
"sieve_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['sieve_access']),
|
||||
"eas_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['eas_access']),
|
||||
"dav_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['dav_access']),
|
||||
"acl_spam_alias" => 1,
|
||||
"acl_tls_policy" => 1,
|
||||
"acl_spam_score" => 1,
|
||||
|
||||
@@ -319,16 +319,10 @@ $css_minifier = new CSSminifierExtended();
|
||||
$css_dir = array_diff(scandir('/web/css/build'), array('..', '.'));
|
||||
// get customized ui data
|
||||
$UI_TEXTS = customize('get', 'ui_texts');
|
||||
// theme selected in admin UI overrides $UI_THEME
|
||||
if ($ui_theme_selected = customize('get', 'ui_theme')) {
|
||||
$UI_THEME = $ui_theme_selected;
|
||||
}
|
||||
|
||||
|
||||
// minify bootstrap theme
|
||||
if (file_exists('/web/css/themes/'.$UI_THEME.'/bootstrap.css'))
|
||||
$css_minifier->add('/web/css/themes/'.$UI_THEME.'/bootstrap.css');
|
||||
elseif (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css'))
|
||||
if (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css'))
|
||||
$css_minifier->add('/web/css/themes/'.$UI_THEME.'-bootstrap.css');
|
||||
else
|
||||
$css_minifier->add('/web/css/themes/lumen-bootstrap.css');
|
||||
|
||||
@@ -13,9 +13,7 @@ $twig = new Environment($loader, [
|
||||
|
||||
// functions
|
||||
$twig->addFunction(new TwigFunction('query_string', function (array $params = []) {
|
||||
$allowed = ['lang', 'mobileconfig'];
|
||||
$filtered = array_intersect_key($_GET, array_flip($allowed));
|
||||
return http_build_query(array_merge($filtered, $params));
|
||||
return http_build_query(array_merge($_GET, $params));
|
||||
}));
|
||||
|
||||
$twig->addFunction(new TwigFunction('is_uri', function (string $uri, string $where = null) {
|
||||
|
||||
@@ -112,7 +112,6 @@ $AVAILABLE_LANGUAGES = array(
|
||||
'sv-se' => 'Svenska (Swedish)',
|
||||
'tr-tr' => 'Türkçe (Turkish)',
|
||||
'uk-ua' => 'Українська (Ukrainian)',
|
||||
'uz-uz' => 'Oʻzbekcha (Uzbek)',
|
||||
'vi-vn' => 'Tiếng Việt (Vietnamese)',
|
||||
'zh-cn' => '简体中文 (Simplified Chinese)',
|
||||
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||
@@ -121,8 +120,6 @@ $AVAILABLE_LANGUAGES = array(
|
||||
// default theme is lumen
|
||||
// additional themes can be found here: https://bootswatch.com/
|
||||
// copy them to data/web/css/themes/{THEME-NAME}-bootstrap.css
|
||||
// or use a theme folder data/web/css/themes/{THEME-NAME}/ (theme.css, optional dark.css, bootstrap.css, theme.json)
|
||||
// theme folders can be selected in System > Configuration > Customize, which overrides $UI_THEME
|
||||
$UI_THEME = "lumen";
|
||||
|
||||
// Show DKIM private keys - false by default
|
||||
@@ -248,10 +245,6 @@ $PW_RESET_TOKEN_LIMIT = 3;
|
||||
// Maximum time in minutes a password reset token is valid
|
||||
$PW_RESET_TOKEN_LIFETIME = 15;
|
||||
|
||||
// Globally forbid aliases with external (non-local) goto domains for ALL roles (incl. admins),
|
||||
// overriding the per-domain-admin da_acl. false = defer to da_acl.
|
||||
$ALIAS_DISABLE_EXTERNAL_DOMAINS = false;
|
||||
|
||||
// UV flag handling in FIDO2/WebAuthn - defaults to false to allow iOS logins
|
||||
// true = required
|
||||
// false = preferred
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.8 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Bootstrap v5.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,(t,e)=>`#${CSS.escape(e)}`)),t),s=t=>null==t?`${t}`:Object.prototype.toString.call(t).match(/\s([a-z]+)/i)[1].toLowerCase(),o=t=>{t.dispatchEvent(new Event(i))},r=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),a=t=>r(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,l=t=>{if(!r(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",()=>{for(const t of p)t()}),p.push(e)):e()},_=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,b=(t,e,n=!0)=>{if(!n)return void _(t);const s=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),_(t))};e.addEventListener(i,a),setTimeout(()=>{r||o(e)},s)},v=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,A=/::\d+$/,E={};let T=1;const C={mouseenter:"mouseover",mouseleave:"mouseout"},O=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function x(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function k(t){const e=x(t);return t.uidEvent=e,E[e]=E[e]||{},E[e]}function L(t,e,i=null){return Object.values(t).find(t=>t.callable===e&&t.delegationSelector===i)}function S(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=N(t);return O.has(o)||(o=t),[n,s,o]}function D(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=S(e,i,n);if(e in C){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=k(t),c=l[a]||(l[a]={}),h=L(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=x(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return j(s,{delegateTarget:r}),n.oneOff&&P.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return j(n,{delegateTarget:t}),i.oneOff&&P.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function $(t,e,i,n,s){const o=L(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function I(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&$(t,e,i,r.callable,r.delegationSelector)}function N(t){return t=t.replace(w,""),C[t]||t}const P={on(t,e,i,n){D(t,e,i,n,!1)},one(t,e,i,n){D(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=k(t),c=l[rLine truncated
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;s<o;s++){const o=t[n[s]];if(o.originalHandler===e&&o.delegationSelector===i)return o}return null}function S(t,e,i){const n="string"==typeof e,s=n?i:e;let o=P(t);return k.has(o)||(o=t),[n,s,o]}function N(t,e,i,n,s){if("string"!=typeof e||!t)return;if(i||(i=n,n=null),C.test(e)){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!Line truncated
|
||||
//# sourceMappingURL=bootstrap.bundle.min.js.map
|
||||
@@ -124,9 +124,6 @@ $(document).ready(function() {
|
||||
var lastTab = localStorage.getItem(key);
|
||||
if (lastTab) {
|
||||
$('[data-bs-target="#' + lastTab + '"]').click();
|
||||
var dropdown = $('[data-bs-target="#' + lastTab + '"]').closest('.dropdown');
|
||||
dropdown.find('.dropdown-menu').removeClass('show');
|
||||
dropdown.find('.dropdown-toggle').attr('aria-expanded', 'false');
|
||||
var tab = $('[id^="' + lastTab + '"]');
|
||||
$(tab).find('.card-body.collapse:first').collapse('show');
|
||||
}
|
||||
@@ -348,15 +345,15 @@ $(document).ready(function() {
|
||||
$('.main-logo-dark').addClass('d-none');
|
||||
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');
|
||||
localStorage.setItem('mailcow_theme', 'light');
|
||||
localStorage.setItem('theme', 'light');
|
||||
}else{
|
||||
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="' + mailcow_dark_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);
|
||||
$('.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_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
|
||||
localStorage.setItem('mailcow_theme', 'dark');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ $(document).ready(function() {
|
||||
// create host cpu and mem charts
|
||||
createHostCpuAndMemChart();
|
||||
// check for new version
|
||||
if (mailcow_info.branch === "master" && mailcow_cc_role === "admin"){
|
||||
if (mailcow_info.branch === "master"){
|
||||
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||
}
|
||||
$("#mailcow_version").click(function(){
|
||||
@@ -1089,8 +1089,6 @@ jQuery(function($){
|
||||
return str;
|
||||
}).join('<br>\n');
|
||||
item.subject = escapeHtml(item.subject);
|
||||
if (item.sender_mime != null) item.sender_mime = escapeHtml(item.sender_mime);
|
||||
if (item['message-id'] != null) item['message-id'] = escapeHtml(item['message-id']);
|
||||
var scan_time = item.time_real.toFixed(3);
|
||||
if (item.time_virtual) {
|
||||
scan_time += ' / ' + item.time_virtual.toFixed(3);
|
||||
@@ -1130,11 +1128,6 @@ jQuery(function($){
|
||||
item.ua = escapeHtml(item.ua);
|
||||
}
|
||||
item.ua = '<span style="font-size:small">' + item.ua + '</span>';
|
||||
if (item.user == null) {
|
||||
item.user = 'unknown';
|
||||
} else {
|
||||
item.user = escapeHtml(item.user);
|
||||
}
|
||||
if (item.service == "activesync") {
|
||||
item.service = '<span class="badge fs-6 bg-info">ActiveSync</span>';
|
||||
}
|
||||
@@ -1169,7 +1162,6 @@ jQuery(function($){
|
||||
if (item === null) { return true; }
|
||||
item.user = escapeHtml(item.user);
|
||||
item.call = escapeHtml(item.call);
|
||||
if (item.msg) item.msg = escapeHtml(item.msg);
|
||||
item.task = '<code>' + item.task + '</code>';
|
||||
item.type = '<span class="badge fs-6 bg-' + item.type + '">' + item.type + '</span>';
|
||||
});
|
||||
@@ -1177,7 +1169,6 @@ jQuery(function($){
|
||||
$.each(data, function (i, item) {
|
||||
if (item === null) { return true; }
|
||||
item.username = escapeHtml(item.username);
|
||||
item.real_rip = escapeHtml(item.real_rip);
|
||||
item.service = '<div class="badge fs-6 bg-secondary">' + item.service.toUpperCase() + '</div>';
|
||||
});
|
||||
} else if (table == 'general_syslog') {
|
||||
@@ -1216,11 +1207,6 @@ jQuery(function($){
|
||||
});
|
||||
} else if (table == 'rllog') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.header_from != null) item.header_from = escapeHtml(item.header_from);
|
||||
if (item.header_subject != null) item.header_subject = escapeHtml(item.header_subject);
|
||||
if (item.from != null) item.from = escapeHtml(item.from);
|
||||
if (item.rcpt != null) item.rcpt = escapeHtml(item.rcpt);
|
||||
if (item.message_id != null) item.message_id = escapeHtml(item.message_id);
|
||||
if (item.user == null) {
|
||||
item.user = "none";
|
||||
}
|
||||
@@ -1678,26 +1664,41 @@ function createHostCpuAndMemChart(){
|
||||
function check_update(current_version, github_repo_url){
|
||||
if (!current_version || !github_repo_url) return false;
|
||||
|
||||
var github_account = github_repo_url.split("/")[3];
|
||||
var github_repo_name = github_repo_url.split("/")[4];
|
||||
|
||||
window.fetch("/inc/ajax/update_check.php", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
if (!response.ok) throw new Error("update check returned " + response.status);
|
||||
// get details about latest release
|
||||
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
if (data.status === "no_update") {
|
||||
$("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
|
||||
$("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
|
||||
} else if (data.status === "update_available") {
|
||||
$("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
|
||||
$("#mailcow_update").html(lang_debug.update_available + ` <a href="#" id="mailcow_update_changelog">`+data.latest_tag+`</a>`);
|
||||
$("#mailcow_update_changelog").click(function(){
|
||||
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin")
|
||||
return;
|
||||
}).then(function(latest_data) {
|
||||
// get details about current release
|
||||
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(current_data) {
|
||||
// compare releases
|
||||
var date_current = new Date(current_data.created_at);
|
||||
var date_latest = new Date(latest_data.created_at);
|
||||
if (date_latest.getTime() <= date_current.getTime()){
|
||||
// no update available
|
||||
$("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
|
||||
$("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
|
||||
} else {
|
||||
// update available
|
||||
$("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
|
||||
$("#mailcow_update").html(lang_debug.update_available + ` <a href="#" id="mailcow_update_changelog">`+latest_data.tag_name+`</a>`);
|
||||
$("#mailcow_update_changelog").click(function(){
|
||||
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin")
|
||||
return;
|
||||
|
||||
showVersionModal("New Release " + data.latest_tag, data.latest_tag);
|
||||
})
|
||||
} else {
|
||||
throw new Error(data.message || "update check failed");
|
||||
}
|
||||
showVersionModal("New Release " + latest_data.tag_name, latest_data.tag_name);
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
// err
|
||||
console.log(err);
|
||||
$("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
|
||||
$("#mailcow_update").html("<b>"+ lang_debug.update_failed +"</b>");
|
||||
});
|
||||
}).catch(err => {
|
||||
// err
|
||||
console.log(err);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
$(document).ready(function() {
|
||||
var theme = localStorage.getItem("mailcow_theme");
|
||||
if (theme !== null) {
|
||||
localStorage.setItem("mailcow_theme", theme);
|
||||
}
|
||||
var theme = localStorage.getItem("theme");
|
||||
localStorage.clear();
|
||||
localStorage.setItem("theme", theme);
|
||||
});
|
||||
@@ -424,11 +424,6 @@ $(document).ready(function() {
|
||||
} else {
|
||||
$('#force_pw_update').prop('checked', false);
|
||||
}
|
||||
if (template.force_tfa == 1){
|
||||
$('#force_tfa').prop('checked', true);
|
||||
} else {
|
||||
$('#force_tfa').prop('checked', false);
|
||||
}
|
||||
if (template.sogo_access == 1){
|
||||
$('#sogo_access').prop('checked', true);
|
||||
} else {
|
||||
@@ -996,7 +991,6 @@ jQuery(function($){
|
||||
'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>'
|
||||
};
|
||||
item.username = escapeHtml(item.username);
|
||||
item.name = escapeHtml(item.name);
|
||||
|
||||
if (Array.isArray(item.tags)){
|
||||
var tags = '';
|
||||
@@ -1248,7 +1242,6 @@ jQuery(function($){
|
||||
item.attributes.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.eas_access == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.dav_access == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
|
||||
item.attributes.force_tfa = '<i class="text-' + (item.attributes.force_tfa == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.force_tfa == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.force_tfa == 1 ? '1' : '0') + '</span></i>';
|
||||
if (item.attributes.quarantine_notification === 'never') {
|
||||
item.attributes.quarantine_notification = lang.never;
|
||||
} else if (item.attributes.quarantine_notification === 'hourly') {
|
||||
@@ -1392,11 +1385,6 @@ jQuery(function($){
|
||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
||||
}
|
||||
},
|
||||
{
|
||||
title: lang.force_tfa,
|
||||
data: 'attributes.force_tfa',
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang_edit.ratelimit,
|
||||
data: 'attributes.ratelimit',
|
||||
|
||||
@@ -102,21 +102,18 @@ jQuery(function($){
|
||||
{
|
||||
title: 'ID',
|
||||
data: 'id',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang.qid,
|
||||
data: 'qid',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang.sender,
|
||||
data: 'sender',
|
||||
className: 'senders-mw220',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang.subj,
|
||||
@@ -131,8 +128,7 @@ jQuery(function($){
|
||||
{
|
||||
title: lang.rcpt,
|
||||
data: 'rcpt',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang.danger,
|
||||
@@ -230,18 +226,18 @@ jQuery(function($){
|
||||
}
|
||||
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
||||
$.each(data.fuzzy_hashes, function (index, value) {
|
||||
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + escapeHtml(value) + '</p>');
|
||||
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
|
||||
});
|
||||
} else {
|
||||
$('#qid_detail_fuzzy').append('-');
|
||||
}
|
||||
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
||||
if (data.action == "add header") {
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + escapeHtml(data.score) + '</b> - ' + lang.junk_folder + '</span>');
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
|
||||
} else if (data.action == "reject") {
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + escapeHtml(data.score) + '</b> - ' + lang.rejected + '</span>');
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
|
||||
} else if (data.action == "rewrite subject") {
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + escapeHtml(data.score) + '</b> - ' + lang.rewrite_subject + '</span>');
|
||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
|
||||
}
|
||||
}
|
||||
if (typeof data.recipients !== 'undefined') {
|
||||
@@ -258,8 +254,8 @@ jQuery(function($){
|
||||
qAtts.text('');
|
||||
$.each(data.attachments, function(index, value) {
|
||||
qAtts.append(
|
||||
'<p><a href="/inc/ajax/qitem_details.php?id=' + escapeHtml(qitem) + '&att=' + index + '" target="_blank">' + escapeHtml(value[0]) + '</a> (' + escapeHtml(value[1]) + ')' +
|
||||
' - <small><a href="' + escapeHtml(value[3]) + '" target="_blank">' + lang.check_hash + '</a></small></p>'
|
||||
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
|
||||
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,13 +48,13 @@ jQuery(function($){
|
||||
url: "/api/v1/get/mailq/all",
|
||||
dataSrc: function(data){
|
||||
$.each(data, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + escapeHtml(item.queue_id) + '" />';
|
||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
||||
rcpts = $.map(item.recipients, function(i) {
|
||||
return escapeHtml(i);
|
||||
});
|
||||
item.recipients = rcpts.join('<hr style="margin:1px!important">');
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + escapeHtml(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
|
||||
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
|
||||
'</div>';
|
||||
});
|
||||
return data;
|
||||
@@ -79,14 +79,12 @@ jQuery(function($){
|
||||
{
|
||||
title: 'QID',
|
||||
data: 'queue_id',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: 'Queue',
|
||||
data: 'queue_name',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang_admin.arrival_time,
|
||||
@@ -108,8 +106,7 @@ jQuery(function($){
|
||||
{
|
||||
title: lang_admin.sender,
|
||||
data: 'sender',
|
||||
defaultContent: '',
|
||||
render: $.fn.dataTable.render.text()
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
title: lang_admin.recipients,
|
||||
|
||||
@@ -98,8 +98,8 @@ jQuery(function($){
|
||||
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||
var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>';
|
||||
var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-key-fill"></i><span class="ms-1">' + escapeHtml(item.app_password_name || "App") + '</span></a>' : '';
|
||||
var real_rip = item.real_rip.startsWith("Web") ? escapeHtml(item.real_rip) : '<a href="https://bgp.tools/prefix/' + escapeHtml(item.real_rip) + '" target="_blank">' + escapeHtml(item.real_rip) + "</a>";
|
||||
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + escapeHtml(item.location.toLowerCase()) + '"></span>' : '';
|
||||
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.tools/prefix/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
|
||||
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
|
||||
var ip_data = real_rip + ip_location + app_password;
|
||||
|
||||
$(".last-sasl-login").append(`
|
||||
|
||||
@@ -2038,9 +2038,6 @@ if (isset($_GET['query'])) {
|
||||
case "custom_login":
|
||||
process_edit_return(customize('edit', 'custom_login', $attr));
|
||||
break;
|
||||
case "ui_theme":
|
||||
process_edit_return(customize('edit', 'ui_theme', $attr));
|
||||
break;
|
||||
case "self":
|
||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||
process_edit_return(domain_admin('edit', $attr));
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"acl": {
|
||||
"login_as": "E-poçt qutusu istifadəçisi olaraq daxil ol",
|
||||
"alias_domains": "Alternativ domenlər əlavə et",
|
||||
"app_passwds": "Tətbiq parollarını idarə et",
|
||||
"bcc_maps": "BCC yönləndirmə xəritələri",
|
||||
"delimiter_action": "Ayırıcı əməliyyatı",
|
||||
"domain_desc": "Domen təsvirini dəyiş",
|
||||
"domain_relayhost": "Domen üçün relay serveri dəyiş",
|
||||
"eas_reset": "EAS cihazlarını sıfırla",
|
||||
"extend_sender_acl": "Göndərən ACL-ni xarici ünvanlarla genişləndirməyə icazə ver",
|
||||
"filters": "Filtrlər",
|
||||
"mailbox_relayhost": "E-poçt qutusu üçün relay serveri dəyiş",
|
||||
"prohibited": "ACL tərəfindən məhdudlaşdırılıb",
|
||||
"protocol_access": "Protokol girişini dəyiş"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"acl": {
|
||||
"alias_domains": "Alias-Domains hinzufügen",
|
||||
"alias_external_goto": "Aliase mit externen Ziel-Domains erlauben",
|
||||
"app_passwds": "App-Passwörter verwalten",
|
||||
"bcc_maps": "BCC-Maps",
|
||||
"delimiter_action": "Delimiter-Aktionen (tags)",
|
||||
@@ -14,7 +13,7 @@
|
||||
"mailbox_relayhost": "Relayhost für eine Mailbox setzen",
|
||||
"prohibited": "Untersagt durch Richtlinie",
|
||||
"protocol_access": "Ändern der erlaubten Protokolle",
|
||||
"pushover": "Pushover-Berechtigung",
|
||||
"pushover": "Pushover",
|
||||
"pw_reset": "Verwalten der E-Mail zur Passwortwiederherstellung erlauben",
|
||||
"quarantine": "Quarantäne-Aktionen",
|
||||
"quarantine_attachments": "Anhänge aus Quarantäne",
|
||||
@@ -97,7 +96,7 @@
|
||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Transport-Maps können erstellt werden, um individuelle Ziele für eine Relay-Domain zu definieren.",
|
||||
"relay_unknown_only": "Nur nicht-lokale Mailboxen relayen. Existente Mailboxen werden weiterhin lokal zugestellt.",
|
||||
"relayhost_wrapped_tls_info": "Bitte <b>keine</b> \"TLS-wrapped Ports\" verwenden (etwa SMTPS via Port 465/tcp).<br>\r\nDer Transport wird stattdessen STARTTLS anfordern, um TLS zu verwenden. TLS kann unter \"TLS Policy Maps\" erzwungen werden.",
|
||||
"select": "Bitte auswählen...",
|
||||
"select": "Bitte auswählen",
|
||||
"select_domain": "Bitte zuerst eine Domain auswählen",
|
||||
"sieve_desc": "Kurze Beschreibung",
|
||||
"sieve_type": "Filtertyp",
|
||||
@@ -151,7 +150,7 @@
|
||||
"arrival_time": "Ankunftszeit (Serverzeit)",
|
||||
"authed_user": "Auth. Benutzer",
|
||||
"ays": "Soll der Vorgang wirklich ausgeführt werden?",
|
||||
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels kennzeichnen aktive permanente Sperren durch Denylisting.",
|
||||
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Allowlist-Einträge.",
|
||||
"change_logo": "Logo ändern",
|
||||
"configuration": "Konfiguration",
|
||||
"convert_html_to_text": "Konvertiere HTML zu reinem Text",
|
||||
@@ -395,11 +394,6 @@
|
||||
"ui_header_announcement_type_info": "Info",
|
||||
"ui_header_announcement_type_warning": "Wichtig",
|
||||
"ui_texts": "UI-Label und Texte",
|
||||
"ui_theme": "Theme",
|
||||
"ui_theme_dark": "Enthält eine Darkmode-Variante",
|
||||
"ui_theme_default": "Standard",
|
||||
"ui_theme_info": "Themes sind Ordner in <code>data/web/css/themes/<name>/</code> mit folgenden Dateien:<ul class=\"mb-0\"><li><code>theme.css</code> (erforderlich)</li><li><code>dark.css</code> (optional, Darkmode)</li><li><code>bootstrap.css</code> (optional, ersetzt das Bootstrap-Basistheme)</li><li><code>theme.json</code> (optional, <code>name</code> und <code>description</code>)</li></ul>",
|
||||
"ui_theme_none": "Keine Themes gefunden.",
|
||||
"unban_pending": "ausstehend",
|
||||
"unchanged_if_empty": "Unverändert, wenn leer",
|
||||
"upload": "Hochladen",
|
||||
@@ -437,7 +431,7 @@
|
||||
"comment_too_long": "Kommentarfeld darf maximal 160 Zeichen enthalten",
|
||||
"cors_invalid_method": "Allow-Methods enthält eine ungültige Methode",
|
||||
"cors_invalid_origin": "Allow-Origins enthält eine ungültige Origin",
|
||||
"defquota_empty": "Standard-Quota darf nicht 0 sein.",
|
||||
"defquota_empty": "Standard-Quota darf nicht 0 sein",
|
||||
"demo_mode_enabled": "Demo Mode ist aktiviert",
|
||||
"description_invalid": "Ressourcenbeschreibung für %s ist ungültig",
|
||||
"dkim_domain_or_sel_exists": "Ein DKIM-Key für die Domain \"%s\" existiert und wird nicht überschrieben",
|
||||
@@ -449,7 +443,6 @@
|
||||
"domain_not_found": "Domain %s nicht gefunden",
|
||||
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
||||
"extended_sender_acl_denied": "Keine Rechte zum Setzen von externen Absenderadressen",
|
||||
"external_goto_denied": "Externe Ziel-Adresse %s ist nicht erlaubt",
|
||||
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
|
||||
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
|
||||
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
|
||||
@@ -462,13 +455,13 @@
|
||||
"global_map_write_error": "Kann globale Map ID %s nicht schreiben: %s",
|
||||
"goto_empty": "Eine Alias-Adresse muss auf mindestens eine gültige Ziel-Adresse zeigen",
|
||||
"goto_invalid": "Ziel-Adresse %s ist ungültig",
|
||||
"ham_learn_error": "Ham-Lernfehler: %s",
|
||||
"ham_learn_error": "Ham Lernfehler: %s",
|
||||
"iam_test_connection": "Verbindung fehlgeschlagen",
|
||||
"imagick_exception": "Fataler Bildverarbeitungsfehler",
|
||||
"img_dimensions_exceeded": "Grafik überschreitet die maximale Bildgröße",
|
||||
"img_invalid": "Grafik konnte nicht validiert werden",
|
||||
"img_size_exceeded": "Grafik überschreitet die maximale Dateigröße",
|
||||
"img_tmp_missing": "Grafik konnte nicht validiert werden: Temporärer Datei nicht gefunden.",
|
||||
"img_tmp_missing": "Grafik konnte nicht validiert werden: Erstellung temporärer Datei fehlgeschlagen.",
|
||||
"invalid_bcc_map_type": "Ungültiger BCC-Map-Typ",
|
||||
"invalid_destination": "Ziel-Format \"%s\" ist ungültig",
|
||||
"invalid_filter_type": "Ungültiger Filtertyp",
|
||||
@@ -481,7 +474,7 @@
|
||||
"invalid_reset_token": "Ungültiger Rücksetz-Token",
|
||||
"ip_list_empty": "Liste erlaubter IPs darf nicht leer sein",
|
||||
"is_alias": "%s lautet bereits eine Alias-Adresse",
|
||||
"is_alias_or_mailbox": "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden.",
|
||||
"is_alias_or_mailbox": "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden",
|
||||
"is_spam_alias": "%s lautet bereits eine temporäre Alias-Adresse (Spam-Alias-Adresse)",
|
||||
"last_key": "Letzter Key kann nicht gelöscht werden, bitte stattdessen die 2FA deaktivieren.",
|
||||
"login_failed": "Anmeldung fehlgeschlagen",
|
||||
@@ -519,7 +512,6 @@
|
||||
"pushover_credentials_missing": "Pushover Token und/oder Key fehlen",
|
||||
"pushover_key": "Pushover Key hat das falsche Format",
|
||||
"pushover_token": "Pushover Token hat das falsche Format",
|
||||
"quarantine_category_invalid": "Quarantäne-Kategorie muss eine der folgenden sein: add_header, reject, all",
|
||||
"quota_not_0_not_numeric": "Speicherplatz muss numerisch und >= 0 sein",
|
||||
"recipient_map_entry_exists": "Eine Empfängerumschreibung für Objekt \"%s\" existiert bereits",
|
||||
"recovery_email_failed": "E-Mail zur Wiederherstellung konnte nicht gesendet werden. Bitte wenden Sie sich an Ihren Administrator.",
|
||||
@@ -555,7 +547,6 @@
|
||||
"webauthn_authenticator_failed": "Der ausgewählte Authenticator wurde nicht gefunden",
|
||||
"webauthn_publickey_failed": "Zu dem ausgewählten Authenticator wurde kein Publickey hinterlegt",
|
||||
"webauthn_username_failed": "Der ausgewählte Authenticator gehört zu einem anderen Konto",
|
||||
"ui_theme_invalid": "Theme nicht gefunden",
|
||||
"unknown": "Ein unbekannter Fehler trat auf",
|
||||
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
||||
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
||||
@@ -667,7 +658,7 @@
|
||||
"delete1": "Lösche Nachricht nach Übertragung vom Quell-Server",
|
||||
"delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind",
|
||||
"delete2duplicates": "Lösche Duplikate im Ziel",
|
||||
"delete_ays": "Bitte den Löschvorgang bestätigen.",
|
||||
"delete_ays": "Soll der Löschvorgang wirklich ausgeführt werden?",
|
||||
"description": "Beschreibung",
|
||||
"disable_login": "Login verbieten (Mails werden weiterhin angenommen)",
|
||||
"domain": "Domain bearbeiten",
|
||||
@@ -736,7 +727,6 @@
|
||||
"mta_sts_mx": "MX-Server",
|
||||
"mta_sts_mx_info": "Erlaubt das Senden nur an explizit aufgeführte Mailserver-Hostnamen; der sendende MTA überprüft, ob der DNS-MX-Hostname mit der Richtlinienliste übereinstimmt, und erlaubt die Zustellung nur mit einem gültigen TLS-Zertifikat (schützt vor MITM).",
|
||||
"mta_sts_mx_notice": "Es können mehrere MX-Server angegeben werden (durch Kommas getrennt).",
|
||||
"mta_sts_active_info": "Wenn nicht angehakt, wird die MTA-STS-Richtlinie nicht veröffentlicht und für die mta-sts-Subdomain kein Zertifikat über ACME angefordert.",
|
||||
"multiple_bookings": "Mehrfaches Buchen",
|
||||
"nexthop": "Next Hop",
|
||||
"none_inherit": "Keine Auswahl / Erben",
|
||||
@@ -787,7 +777,7 @@
|
||||
"spam_score": "Einen benutzerdefiniterten Spam-Score festlegen",
|
||||
"subfolder2": "Ziel-Ordner<br><small>(leer = kein Unterordner)</small>",
|
||||
"syncjob": "Sync-Job bearbeiten",
|
||||
"target_address": "Ziel-Adresse(n)<small>(kommasepariert)</small>",
|
||||
"target_address": "Ziel-Adresse(n)",
|
||||
"target_domain": "Ziel-Domain",
|
||||
"timeout1": "Timeout für Verbindung zum Remote-Host",
|
||||
"timeout2": "Timeout für Verbindung zum lokalen Host",
|
||||
@@ -969,7 +959,7 @@
|
||||
"private_comment": "Privater Kommentar",
|
||||
"public_comment": "Öffentlicher Kommentar",
|
||||
"q_add_header": "bei Mail in Junk-Ordner",
|
||||
"q_all": " bei Reject und Mail in Junk-Ordner",
|
||||
"q_all": "bei Reject und Mail in Junk-Ordner",
|
||||
"q_reject": "bei Reject",
|
||||
"quarantine_category": "Quarantäne-Benachrichtigungskategorie",
|
||||
"quarantine_notification": "Quarantäne-Benachrichtigung",
|
||||
@@ -1043,8 +1033,7 @@
|
||||
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
||||
"syncjob_EXIT_OVERQUOTA": "Ziel Mailbox ist über dem Limit",
|
||||
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen",
|
||||
"iam": "Identitätsanbieter",
|
||||
"force_tfa": "2-Faktor-Authentifizierung"
|
||||
"iam": "Identitätsanbieter"
|
||||
},
|
||||
"oauth2": {
|
||||
"access_denied": "Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.",
|
||||
@@ -1096,7 +1085,7 @@
|
||||
"rspamd_result": "Rspamd-Ergebnis",
|
||||
"sender": "Sender (SMTP)",
|
||||
"sender_header": "Sender (\"From\"-Header)",
|
||||
"settings_info": "Maximale Anzahl der zurückgehaltenen E-Mails (pro Mailbox): %s<br>Maximale Größe einer zu speichernden E-Mail: %s MiB",
|
||||
"settings_info": "Maximale Anzahl der zurückgehaltenen E-Mails: %s<br>Maximale Größe einer zu speichernden E-Mail: %s MiB",
|
||||
"show_item": "Details",
|
||||
"spam": "Spam",
|
||||
"spam_score": "Bewertung",
|
||||
@@ -1213,7 +1202,6 @@
|
||||
"tls_policy_map_entry_deleted": "TLS-Richtlinie mit der ID %s wurde gelöscht",
|
||||
"tls_policy_map_entry_saved": "TLS-Richtlinieneintrag \"%s\" wurde gespeichert",
|
||||
"ui_texts": "Änderungen an UI-Texten",
|
||||
"ui_theme_modified": "Theme wurde erfolgreich gespeichert",
|
||||
"upload_success": "Datei wurde erfolgreich hochgeladen",
|
||||
"verified_fido2_login": "FIDO2-Anmeldung verifiziert",
|
||||
"verified_totp_login": "TOTP-Anmeldung verifiziert",
|
||||
@@ -1239,7 +1227,7 @@
|
||||
"set_tfa": "Konfiguriere Zwei-Faktor-Authentifizierungsmethode",
|
||||
"start_webauthn_validation": "Starte Validierung",
|
||||
"tfa": "Zwei-Faktor-Authentifizierung",
|
||||
"tfa_token_invalid": "2FA-Token ungültig",
|
||||
"tfa_token_invalid": "TFA-Token ungültig!",
|
||||
"totp": "Time-based-OTP (Google Authenticator etc.)",
|
||||
"u2f_deprecated": "Es sieht so aus als wurde der Schlüssel mit der alten U2F Methode registriert. Wir werden die Zwei-Faktor-Authentifizierung deaktivieren und deinen Schlüssel löschen.",
|
||||
"u2f_deprecated_important": "Bitte registriere den Schlüssel im Adminbereich mit der neuen WebAuthn Methode.",
|
||||
@@ -1368,7 +1356,7 @@
|
||||
"shared_aliases_desc": "Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen, wie die des Spam-Filters oder der Verschlüsselungsrichtlinie, berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.",
|
||||
"show_sieve_filters": "Zeige aktiven Filter des Benutzers",
|
||||
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
|
||||
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller Kalender- und Kontaktdaten unwiederbringlich gelöscht</b>.",
|
||||
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
|
||||
"sogo_profile_reset_now": "Profil jetzt zurücksetzen",
|
||||
"spam_aliases": "Spam E-Mail-Aliasse",
|
||||
"spam_aliases_info": "Ein Spam-Alias ist eine temporäre E-Mailadresse, die benutzt werden kann, um eine echte E-Mail Adressen zu schützen. <br>Optional kann eine Ablaufzeit gesetzt werden, sodass der Alias nach dem definierten Zeitraum automatisch deaktiviert wird, was missbrauchte oder geleakte Adressen effektiv entsorgt.",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"acl": {
|
||||
"alias_domains": "Add alias domains",
|
||||
"alias_external_goto": "Allow aliases with external goto domains",
|
||||
"app_passwds": "Manage app passwords",
|
||||
"bcc_maps": "BCC maps",
|
||||
"delimiter_action": "Delimiter action",
|
||||
@@ -405,11 +404,6 @@
|
||||
"ui_header_announcement_type_info": "Info",
|
||||
"ui_header_announcement_type_warning": "Important",
|
||||
"ui_texts": "UI labels and texts",
|
||||
"ui_theme": "Theme",
|
||||
"ui_theme_dark": "Includes a dark mode variant",
|
||||
"ui_theme_default": "Default",
|
||||
"ui_theme_info": "Themes are folders in <code>data/web/css/themes/<name>/</code> with the following files:<ul class=\"mb-0\"><li><code>theme.css</code> (required)</li><li><code>dark.css</code> (optional, dark mode)</li><li><code>bootstrap.css</code> (optional, replaces the Bootstrap base theme)</li><li><code>theme.json</code> (optional, <code>name</code> and <code>description</code>)</li></ul>",
|
||||
"ui_theme_none": "No themes found.",
|
||||
"unban_pending": "unban pending",
|
||||
"unchanged_if_empty": "If unchanged leave blank",
|
||||
"upload": "Upload",
|
||||
@@ -450,7 +444,6 @@
|
||||
"domain_not_found": "Domain %s not found",
|
||||
"domain_quota_m_in_use": "Domain quota must be greater or equal to %s MiB",
|
||||
"extended_sender_acl_denied": "missing ACL to set external sender addresses",
|
||||
"external_goto_denied": "External goto address %s is not allowed",
|
||||
"extra_acl_invalid": "External sender address \"%s\" is invalid",
|
||||
"extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain",
|
||||
"fido2_verification_failed": "FIDO2 verification failed: %s",
|
||||
@@ -520,7 +513,6 @@
|
||||
"pushover_credentials_missing": "Pushover token and or key missing",
|
||||
"pushover_key": "Pushover key has a wrong format",
|
||||
"pushover_token": "Pushover token has a wrong format",
|
||||
"quarantine_category_invalid": "Quarantine category must be one of: add_header, reject, all",
|
||||
"quota_not_0_not_numeric": "Quota must be numeric and >= 0",
|
||||
"recipient_map_entry_exists": "A Recipient map entry \"%s\" exists",
|
||||
"recovery_email_failed": "Could not send a recovery email. Please contact your administrator.",
|
||||
@@ -559,7 +551,6 @@
|
||||
"webauthn_authenticator_failed": "The selected authenticator was not found",
|
||||
"webauthn_publickey_failed": "No public key was stored for the selected authenticator",
|
||||
"webauthn_username_failed": "The selected authenticator belongs to another account",
|
||||
"ui_theme_invalid": "Theme not found",
|
||||
"unknown": "An unknown error occurred",
|
||||
"unknown_tfa_method": "Unknown TFA method",
|
||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||
@@ -736,7 +727,6 @@
|
||||
"mta_sts_mx": "MX server",
|
||||
"mta_sts_mx_info": "Allows sending only to explicitly listed mail server hostnames; the sending MTA checks if the DNS MX hostname matches the policy list, and only allows delivery with a valid TLS certificate (guards against MITM).",
|
||||
"mta_sts_mx_notice": "Multiple MX servers can be specified (separated by commas).",
|
||||
"mta_sts_active_info": "If unchecked, the MTA-STS policy will not be published and no certificate will be requested for the mta-sts subdomain via ACME.",
|
||||
"multiple_bookings": "Multiple bookings",
|
||||
"none_inherit": "None / Inherit",
|
||||
"nexthop": "Next hop",
|
||||
@@ -938,7 +928,6 @@
|
||||
"filters": "Filters",
|
||||
"fname": "Full name",
|
||||
"force_pw_update": "Force password update at next login",
|
||||
"force_tfa": "TFA",
|
||||
"gal": "Global Address List",
|
||||
"goto_ham": "Learn as <b>ham</b>",
|
||||
"goto_spam": "Learn as <b>spam</b>",
|
||||
@@ -1096,7 +1085,7 @@
|
||||
"rspamd_result": "Rspamd result",
|
||||
"sender": "Sender (SMTP)",
|
||||
"sender_header": "Sender (\"From\" header)",
|
||||
"settings_info": "Maximum amount of elements to be quarantined (per mailbox): %s<br>Maximum email size: %s MiB",
|
||||
"settings_info": "Maximum amount of elements to be quarantined: %s<br>Maximum email size: %s MiB",
|
||||
"show_item": "Show item",
|
||||
"spam": "Spam",
|
||||
"spam_score": "Score",
|
||||
@@ -1220,7 +1209,6 @@
|
||||
"tls_policy_map_entry_deleted": "TLS policy map ID %s has been deleted",
|
||||
"tls_policy_map_entry_saved": "TLS policy map entry \"%s\" has been saved",
|
||||
"ui_texts": "Saved changes to UI texts",
|
||||
"ui_theme_modified": "Theme was saved successfully",
|
||||
"upload_success": "File uploaded successfully",
|
||||
"verified_fido2_login": "Verified FIDO2 login",
|
||||
"verified_totp_login": "Verified TOTP login",
|
||||
|
||||
+58
-261
@@ -17,7 +17,7 @@
|
||||
"spam_policy": "Lista de bloqueo/desbloqueo",
|
||||
"spam_score": "Puntuación de spam",
|
||||
"syncjobs": "Trabajos de sincronización",
|
||||
"tls_policy": "Política de TLS",
|
||||
"tls_policy": "Póliza de TLS",
|
||||
"unlimited_quota": "Cuota ilimitada para buzones",
|
||||
"app_passwds": "Gestionar las contraseñas de aplicaciones",
|
||||
"domain_desc": "Cambiar descripción del dominio",
|
||||
@@ -28,8 +28,7 @@
|
||||
"pw_reset": "Permitir el restablecimiento de la contraseña del usuario mailcow",
|
||||
"sogo_access": "Permitir la gestión del acceso a SOGo",
|
||||
"mailbox_relayhost": "Cambiar el host de reenvío para un buzón",
|
||||
"smtp_ip_access": "Cambiar hosts permitidos para SMTP",
|
||||
"alias_external_goto": "Permitir alias con dominios de destino externos"
|
||||
"smtp_ip_access": "Cambiar hosts permitidos para SMTP"
|
||||
},
|
||||
"add": {
|
||||
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
|
||||
@@ -79,8 +78,8 @@
|
||||
"relay_all": "Retransmitir todos los destinatarios",
|
||||
"relay_all_info": "↪ Si se elige <b>no</b> retransmitir todos los destinatarios, será necesario agregar un buzón \"ciego\" por cada destinatario que deba ser retransmitido.",
|
||||
"relay_domain": "Retransmitir este dominio",
|
||||
"select": "Seleccione...",
|
||||
"select_domain": "Elija primero un dominio",
|
||||
"select": "Por favor selecciona...",
|
||||
"select_domain": "Por favor elige un dominio primero",
|
||||
"sieve_desc": "Descripción",
|
||||
"sieve_type": "Tipo de filtro",
|
||||
"skipcrossduplicates": "Omitir mensajes duplicados en carpetas (orden de llegada)",
|
||||
@@ -109,9 +108,7 @@
|
||||
"domain_matches_hostname": "El dominio %s coincide con el nombre de host",
|
||||
"relay_unknown_only": "Reenviar sólo los buzones no existentes. Los buzones existentes se entregarán localmente.",
|
||||
"relayhost_wrapped_tls_info": "Por favor, no utilice puertos con TLS (habitualmente, el puerto 465). <br>Utilice cualquier puerto no cifrado y emita STARTTLS. Se puede crear una política para imponer TLS en \"TLS policy maps\".",
|
||||
"tags": "Etiquetas",
|
||||
"internal_info": "Los alias internos solo son accesibles desde el propio dominio o sus dominios alias.",
|
||||
"sender_allowed": "Permitir enviar como este alias"
|
||||
"tags": "Etiquetas"
|
||||
},
|
||||
"admin": {
|
||||
"access": "Acceso",
|
||||
@@ -174,7 +171,7 @@
|
||||
"f2b_max_ban_time": "Max tiempo de restricción (s)",
|
||||
"f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
|
||||
"f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
|
||||
"f2b_parameters": "Parámetros de Fail2ban",
|
||||
"f2b_parameters": "Parametros Fail2ban",
|
||||
"f2b_retry_window": "Ventana de tiempo entre reintentos",
|
||||
"f2b_whitelist": "Redes y hosts en lista de desbloqueo",
|
||||
"filter_table": "Filtrar tabla",
|
||||
@@ -193,7 +190,7 @@
|
||||
"include_exclude_info": "Por defecto - sin selección - <b>todos los buzones</b> son notificados.",
|
||||
"includes": "Incluye a estos destinatarios",
|
||||
"link": "Enlace",
|
||||
"loading": "Espere, por favor...",
|
||||
"loading": "Espera por favor...",
|
||||
"logo_info": "La imagen se escalará a una altura de 40 px para la barra de navegación superior y un máx. de ancho de 250 px para la página de inicio. Un gráfico escalable es muy recomendable.",
|
||||
"main_name": "Nombre \"mailcow UI\"",
|
||||
"merged_vars_hint": "Las filas grises fueron incorporadas de <code>vars.(local.)inc.php</code> y no son modificables.",
|
||||
@@ -284,7 +281,7 @@
|
||||
"password_length": "Longitud de la contraseña",
|
||||
"quicklink_text": "Mostrar u ocultar enlaces rápidos a otras páginas de inicio bajo el formulario de inicio de sesión",
|
||||
"password_policy_lowerupper": "Debe contener caracteres en minúsculas y mayúsculas",
|
||||
"rspamd_global_filters_regex": "Sus nombres indican su propósito. Todo el contenido debe constar de expresiones regulares válidas con el formato \"/patrón/opciones\" (por ejemplo, <code>/.+@domain\\.tld/i</code>).<br>\n Si bien se llevan a cabo comprobaciones básicas de cada expresión regular, la funcionalidad de Rspamd puede verse inutilizada, si no consigue interpretar correctamente la sintaxis utilizada.<br>\n Rspamd intentará leer el contenido del mapa cuando este se modifique. En caso de problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie Rspamd</a> para forzar una recarga del mapa.<br>Los elementos incluidos en listas de bloqueo se excluyen de la cuarentena.",
|
||||
"rspamd_global_filters_regex": "Sus nombres indican su propósito. Todo el contenido debe constar de expresiones regulares válidas con el formato \"/patrón/opciones\" (por ejemplo, <code>/.+@domain\\.tld/i</code>).<br>\n Si bien se llevan a cabo comprobaciones básicas de cada expresión regular, la funcionalidad de Rspamd puede verse inutilizada, si no consigue interpretar correctamente la sintaxis utilizada.<br>\n Rspamd intentará leer el contenido del mapa cuando éste se modifique. En caso de de problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie Rspamd</a> para forzar una recarga del mapa.<br>Los elementos incluidos en listas de bloqueo se excluyen de la cuarentena.",
|
||||
"iam_use_ssl_info": "Si se habilita SSL y el puerto se establece en el 389, se cambiará automáticamente al 636.",
|
||||
"iam_login_provisioning": "Crear usuarios automáticamente al iniciar sesión",
|
||||
"iam_periodic_full_sync": "Sincronización completa periódica",
|
||||
@@ -403,20 +400,7 @@
|
||||
"user_quicklink": "Ocultar enlace rápido a página de inicio de sesión de usuario",
|
||||
"validate_license_now": "Validar el GUID contra el servidor de licencias",
|
||||
"verify": "Verificar",
|
||||
"yes": "✓",
|
||||
"iam_auth_flow_info": "Además del Authorization Code Flow (Standard Flow en Keycloak), que se usa para el inicio de sesión único (SSO), mailcow también admite autenticación con credenciales directas. El Mailpassword Flow intenta validar las credenciales del usuario mediante la API REST de administración de Keycloak. mailcow obtiene la contraseña cifrada del atributo <code>mailcow_password</code>, que está mapeado en Keycloak.",
|
||||
"iam_extra_permission": "Para que los siguientes ajustes funcionen, el cliente de mailcow en Keycloak necesita una <code>Service account</code> y el permiso <code>view-users</code>.",
|
||||
"iam_bindpass": "Contraseña de enlace (bind)",
|
||||
"iam_rest_flow": "Mailpassword Flow",
|
||||
"iam_binddn": "Bind DN",
|
||||
"oauth2_info": "La implementación de OAuth2 admite el tipo de concesión \"Authorization Code\" y emite tokens de actualización.<br>\r\nEl servidor también emite automáticamente nuevos tokens de actualización después de usar uno.<br><br>\r\n• El alcance predeterminado es <i>profile</i>. Solo los usuarios de buzones pueden autenticarse mediante OAuth2. Si se omite el parámetro scope, se usa <i>profile</i>.<br>\r\n• El cliente debe enviar el parámetro <i>state</i> como parte de la solicitud de autorización.<br><br>\r\nRutas para las solicitudes a la API de OAuth2: <br>\r\n<ul>\r\n <li>Endpoint de autorización: <code>/oauth/authorize</code></li>\r\n <li>Endpoint de tokens: <code>/oauth/token</code></li>\r\n <li>Página de recursos: <code>/oauth/profile</code></li>\r\n</ul>\r\nRegenerar el secreto del cliente no invalida los códigos de autorización existentes, pero estos no podrán renovar su token.<br><br>\r\nRevocar los tokens de los clientes termina de inmediato todas las sesiones activas. Todos los clientes deberán volver a autenticarse.",
|
||||
"rate_name": "Nombre del límite",
|
||||
"ui_header_announcement_active": "Activar anuncio",
|
||||
"ui_theme": "Tema",
|
||||
"ui_theme_dark": "Incluye una variante en modo oscuro",
|
||||
"ui_theme_default": "Predeterminado",
|
||||
"ui_theme_info": "Los temas son carpetas en <code>data/web/css/themes/<name>/</code> con los siguientes archivos:<ul class=\"mb-0\"><li><code>theme.css</code> (obligatorio)</li><li><code>dark.css</code> (opcional, modo oscuro)</li><li><code>bootstrap.css</code> (opcional, reemplaza el tema base de Bootstrap)</li><li><code>theme.json</code> (opcional, <code>name</code> y <code>description</code>)</li></ul>",
|
||||
"ui_theme_none": "No se encontraron temas."
|
||||
"yes": "✓"
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Acceso denegado o datos del formulario inválidos",
|
||||
@@ -463,7 +447,7 @@
|
||||
"map_content_empty": "Contenido no puede estar vacío",
|
||||
"max_mailbox_exceeded": "Máx. de buzones superado (%d de %d)",
|
||||
"max_quota_in_use": "Cuota del buzón debe ser mayor o igual a %d MiB",
|
||||
"mysql_error": "Error de MySQL: %s",
|
||||
"mysql_error": "MySQL error: %s",
|
||||
"network_host_invalid": "Red o host inválido: %s",
|
||||
"next_hop_interferes": "%s interfiere con el siguiente destino %s",
|
||||
"next_hop_interferes_any": "Siguiente destino existente interfiere con %s",
|
||||
@@ -478,7 +462,7 @@
|
||||
"private_key_error": "Error en la llave privada: %s",
|
||||
"quota_not_0_not_numeric": "Cuota debe ser numérica y >= 0",
|
||||
"recipient_map_entry_exists": "Una regla de destinatario \"%s\" existe",
|
||||
"redis_error": "Error de Redis: %s",
|
||||
"redis_error": "Redis error: %s",
|
||||
"relayhost_invalid": "relayhost %s es invalido",
|
||||
"release_send_failed": "El mensaje no pudo ser liberado: %s",
|
||||
"rl_timeframe": "Marco de tiempo del límite de velocidad esta incorrecto",
|
||||
@@ -487,14 +471,14 @@
|
||||
"sender_acl_invalid": "Valor %s del ACL del remitente es inválido",
|
||||
"set_acl_failed": "Error al establecer el ACL",
|
||||
"settings_map_invalid": "Ajustes de ID %s invalidos",
|
||||
"sieve_error": "Error del analizador de Sieve: %s",
|
||||
"sieve_error": "Sieve parser error: %s",
|
||||
"spam_learn_error": "Error aprendiendo muestra: %s",
|
||||
"subject_empty": "Asunto no puede estar vacío",
|
||||
"target_domain_invalid": "El dominio \"goto\" es inválido",
|
||||
"targetd_not_found": "Dominio destino no encontrado",
|
||||
"text_empty": "Texto no puede estar vacío",
|
||||
"tls_policy_map_entry_exists": "La regla de política de TLS \"%s\" ya existe",
|
||||
"tls_policy_map_parameter_invalid": "El parámetro de la política no es válido.",
|
||||
"tls_policy_map_entry_exists": "Regla de póliza de TLS \"%s\" existe",
|
||||
"tls_policy_map_parameter_invalid": "El parámetro de póliza no es válido.",
|
||||
"totp_verification_failed": "Verificación TOTP fallida",
|
||||
"transport_dest_exists": "Destino de la regla de transporte ya existe",
|
||||
"webauthn_verification_failed": "Verificación WebAuthn fallida: %s",
|
||||
@@ -557,12 +541,7 @@
|
||||
"tfa_token_invalid": "Token de autenticación de doble factor no válido",
|
||||
"to_invalid": "El destinatario no puede quedar en blanco",
|
||||
"webauthn_authenticator_failed": "No se ha localizado el autenticador seleccionado",
|
||||
"webauthn_username_failed": "El autenticador seleccionado pertenece a otra cuenta",
|
||||
"tfa_removal_blocked": "No se puede quitar la autenticación de dos factores porque es obligatoria para su cuenta.",
|
||||
"external_goto_denied": "La dirección de destino externa %s no está permitida",
|
||||
"max_age_invalid": "La antigüedad máxima %s no es válida",
|
||||
"quarantine_category_invalid": "La categoría de cuarentena debe ser una de: add_header, reject, all",
|
||||
"ui_theme_invalid": "Tema no encontrado"
|
||||
"webauthn_username_failed": "El autenticador seleccionado pertenece a otra cuenta"
|
||||
},
|
||||
"debug": {
|
||||
"containers_info": "Información de los contenedores",
|
||||
@@ -572,11 +551,11 @@
|
||||
"log_info": "<p>Los <b>logs en memoria</b> son recopilados en listas de Redis y recortados a LOG_LINES (%d) cada minuto para prevenir sobrecarga en el sistema.\r\n <br>Los logs en memoria no están destinados a ser persistentes. Todas las aplicaciones que logean a la memoria, también logean en el daemon de Docker y, por lo tanto, en el controlador de registro predeterminado.\r\n El log en memoria se debe utilizar para analizar problemas menores con los contenedores.</p>\r\n <p>Los <b>logs externos</b> se recopilan a través de la API de la aplicación dada.</p>\r\n <p>Los <b>logs estáticos</b> son principalmente registros de actividad, que no están registrados en Dockerd pero que aún deben ser persistentes (excepto los registros de API).</p>",
|
||||
"logs": "Logs",
|
||||
"restart_container": "Reiniciar",
|
||||
"docs": "Documentación",
|
||||
"docs": "Docs",
|
||||
"last_modified": "Última modificación",
|
||||
"size": "Tamaño",
|
||||
"started_at": "Iniciado el",
|
||||
"uptime": "Tiempo en funcionamiento",
|
||||
"uptime": "Uptime",
|
||||
"static_logs": "Logs estáticos",
|
||||
"system_containers": "Sistema y Contenedores",
|
||||
"show_ip": "Mostrar IP pública",
|
||||
@@ -638,8 +617,8 @@
|
||||
"full_name": "Nombre completo",
|
||||
"gal": "Lista global de direcciones (GAL)",
|
||||
"gal_info": "El GAL contiene todos los objetos de un dominio y no puede ser editado por ningún usuario. Falta información de disponibilidad en SOGo, si está desactivada. <b>Reinicia SOGo para aplicar los cambios.</b>",
|
||||
"grant_types": "Tipos de concesión",
|
||||
"hostname": "Nombre de host",
|
||||
"grant_types": "Grant types",
|
||||
"hostname": "Hostname",
|
||||
"inactive": "Inactivo",
|
||||
"kind": "Tipo",
|
||||
"last_modified": "Última modificación",
|
||||
@@ -657,9 +636,9 @@
|
||||
"password_repeat": "Confirmación de contraseña (repetir):",
|
||||
"previous": "Página anterior",
|
||||
"quota_mb": "Cuota (MiB)",
|
||||
"redirect_uri": "URL de redirección/callback",
|
||||
"redirect_uri": "Redirect/Callback URL",
|
||||
"relay_all": "Retransmitir todos los destinatarios",
|
||||
"relay_all_info": "<small>Si elige <b>no</b> retransmitir a todos los destinatarios, deberá agregar un buzón \"ciego\" por cada destinatario que deba ser retransmitido.</small>",
|
||||
"relay_all_info": "<small>Si eliges <b>no</b> retransmitir a todos los destinatarios, necesitas agregar un buzón \"ciego\" por cada destinatario que debe ser retransmitido.</small>",
|
||||
"relay_domain": "Dominio de retransmisión",
|
||||
"relayhost": "Ruta saliente",
|
||||
"remove": "Eliminar",
|
||||
@@ -736,36 +715,12 @@
|
||||
"spam_alias": "Crear o modificar alias temporales (con caducidad)",
|
||||
"spam_filter": "Filtro de spam",
|
||||
"spam_policy": "Añadir o eliminar elementos de la lista de bloqueo/desbloqueo",
|
||||
"spam_score": "Establecer una puntuación de spam personalizada",
|
||||
"domain_footer_info_vars": {
|
||||
"auth_user": "{= auth_user =} - Nombre de usuario autenticado indicado por un MTA",
|
||||
"from_user": "{= from_user =} - Parte de usuario del remitente del sobre, p. ej. para \"moo@mailcow.tld\" devuelve \"moo\"",
|
||||
"from_name": "{= from_name =} - Nombre del remitente del sobre, p. ej. para \"Mailcow <moo@mailcow.tld>\" devuelve \"Mailcow\"",
|
||||
"from_addr": "{= from_addr =} - Dirección del remitente del sobre",
|
||||
"from_domain": "{= from_domain =} - Dominio del remitente del sobre",
|
||||
"custom": "{= foo =} - Si el buzón tiene el atributo personalizado \"foo\" con el valor \"bar\", devuelve \"bar\""
|
||||
},
|
||||
"domain_footer_plain": "Pie de página en texto plano",
|
||||
"internal_info": "Los alias internos solo son accesibles desde el propio dominio o sus dominios alias.",
|
||||
"sender_allowed": "Permitir enviar como este alias",
|
||||
"sender_allowed_info": "Si se desactiva, este alias solo puede recibir correo. Use la ACL de remitentes para anularlo y dar permiso de envío a buzones concretos.",
|
||||
"mta_sts": "MTA-STS",
|
||||
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> es un estándar que obliga a que la entrega de correo entre servidores use TLS con certificados válidos. <br>Se usa cuando <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> no es posible por falta de DNSSEC o porque no es compatible.<br><b>Nota</b>: si el dominio receptor admite DANE con DNSSEC, DANE <b>siempre</b> tiene preferencia; MTA-STS solo actúa como alternativa.",
|
||||
"mta_sts_version_info": "Define la versión del estándar MTA-STS; actualmente solo es válida <code>STSv1</code>.",
|
||||
"mta_sts_mode_info": "Hay tres modos para elegir:<ul><li><em>testing</em>: la política solo se supervisa, las infracciones no tienen efecto.</li><li><em>enforce</em>: la política se aplica estrictamente y se rechazan las conexiones sin TLS válido.</li><li><em>none</em>: la política se publica pero no se aplica.</li></ul>",
|
||||
"mta_sts_max_age": "Antigüedad máxima",
|
||||
"mta_sts_max_age_info": "Tiempo en segundos durante el cual los servidores receptores pueden guardar esta política en caché antes de volver a obtenerla.",
|
||||
"mta_sts_mx_info": "Permite enviar solo a los nombres de servidor de correo indicados explícitamente; el MTA emisor comprueba que el nombre del registro MX coincida con la lista de la política y solo entrega con un certificado TLS válido (protege contra ataques MITM).",
|
||||
"mta_sts_mx_notice": "Se pueden indicar varios servidores MX (separados por comas).",
|
||||
"mta_sts_active_info": "Si se desmarca, la política MTA-STS no se publicará y no se solicitará ningún certificado para el subdominio mta-sts mediante ACME.",
|
||||
"pushover_only_x_prio": "Considerar solo el correo de alta prioridad [<code>X-Priority: 1</code>]",
|
||||
"pushover_sender_regex": "Considerar la siguiente expresión regular de remitente",
|
||||
"pushover_vars": "Si no se define ningún filtro de remitente, se considerarán todos los correos.<br>Los filtros por expresión regular y las comprobaciones exactas de remitente pueden definirse por separado y se evalúan en orden. No dependen entre sí.<br>Variables utilizables para el texto y el título (tenga en cuenta las normas de protección de datos)"
|
||||
"spam_score": "Establecer una puntuación de spam personalizada"
|
||||
},
|
||||
"footer": {
|
||||
"hibp_nok": "¡Se encontró una coincidencia: esta es una contraseña <b>no segura</b>, elija otra!",
|
||||
"hibp_nok": "¡Se encontró coincidencia - esta es una contraseña <b>no segura</b>, selecciona otra!",
|
||||
"hibp_ok": "No se encontraron coincidencias",
|
||||
"loading": "Espere, por favor...",
|
||||
"loading": "Espera por favor...",
|
||||
"restart_now": "Reiniciar ahora",
|
||||
"restart_container": "Reiniciar contenedor",
|
||||
"restart_container_info": "<b>Importante:</b> Un reinicio limpio puede llevar un tiempo. Por favor, espere a que finalice.",
|
||||
@@ -780,7 +735,7 @@
|
||||
"header": {
|
||||
"administration": "Administración",
|
||||
"debug": "Información",
|
||||
"email": "Correo electrónico",
|
||||
"email": "E-Mail",
|
||||
"mailcow_config": "Configuración",
|
||||
"quarantine": "Cuarentena",
|
||||
"restart_sogo": "Reiniciar SOGo",
|
||||
@@ -826,12 +781,12 @@
|
||||
"add_bcc_entry": "Añadir regla de BCC",
|
||||
"add_domain": "Agregar dominio",
|
||||
"add_domain_alias": "Agregar alias de dominio",
|
||||
"add_domain_record_first": "Agregue primero un dominio",
|
||||
"add_domain_record_first": "Agrega un dominio primero",
|
||||
"add_filter": "Añadir filtro",
|
||||
"add_mailbox": "Agregar buzón",
|
||||
"add_recipient_map_entry": "Añadir regla de destinatario",
|
||||
"add_resource": "Añadir recurso",
|
||||
"add_tls_policy_map": "Agregar regla de política de TLS",
|
||||
"add_tls_policy_map": "Añadir regla de póliza de TLS",
|
||||
"address_rewriting": "Reescritura de direcciones",
|
||||
"alias": "Alias",
|
||||
"aliases": "Alias",
|
||||
@@ -909,8 +864,8 @@
|
||||
"running": "En marcha",
|
||||
"set_postfilter": "Marcar como posfiltro",
|
||||
"set_prefilter": "Marcar como prefiltro",
|
||||
"sieve_info": "Puede almacenar varios filtros por usuario, pero solo un prefiltro y un postfiltro pueden estar activos al mismo tiempo.<br>\r\nCada filtro será procesado en el orden descrito. Ni un script fallido ni un \"keep;\" emitido detendrá el procesamiento de otros scripts.<br>\r\n<a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_before\" target=\"_blank\">Prefiltro sieve global</a> → Prefiltro → Scripts del usuario → Posfiltro → <a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_after\" target=\"_blank\">Posfiltro sieve global</a>",
|
||||
"status": "Estado",
|
||||
"sieve_info": "Puedes almacenar múltiples filtros por usuario, pero solo un prefiltro y un postfiltro pueden estar activos al mismo tiempo.<br>\r\nCada filtro será procesado en el orden descrito. Ni un script fallido ni un \"keep;\" emitido detendrá el procesamiento de otros scripts.<br>\r\n<a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_before\" target=\"_blank\">Prefiltro sieve global</a> → Prefiltro → Scripts del usuario → Posfiltro → <a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_after\" target=\"_blank\">Posfiltro sieve global</a>",
|
||||
"status": "Status",
|
||||
"sync_jobs": "Trabajos de sincronización",
|
||||
"target_address": "Dirección destino",
|
||||
"target_domain": "Dominio destino",
|
||||
@@ -918,12 +873,12 @@
|
||||
"tls_enforce_out": "Forzar TLS saliente",
|
||||
"tls_map_dest": "Destino",
|
||||
"tls_map_dest_info": "Ejemplos: example.org, .example.org, [mail.example.org]:25",
|
||||
"tls_map_parameters": "Parámetros",
|
||||
"tls_map_parameters_info": "Vacío o parámetros, por ejemplo: protocols=!SSLv2 ciphers=medium exclude=3DES",
|
||||
"tls_map_policy": "Política",
|
||||
"tls_map_parameters": "Parametros",
|
||||
"tls_map_parameters_info": "Vacío o parametros, por ejemplo: protocols=!SSLv2 ciphers=medium exclude=3DES",
|
||||
"tls_map_policy": "Póliza",
|
||||
"tls_policy_maps": "Políticas de TLS",
|
||||
"tls_policy_maps_info": "Este mapa de políticas anula las reglas de transporte TLS salientes, independientemente de la configuración de la política TLS de los usuarios.<br>\r\n Consulte <a href=\"http://www.postfix.org/postconf.5.html#smtp_tls_policy_maps\" target=\"_blank\">la documentación de \"smtp_tls_policy_maps\"</a> para más información.",
|
||||
"tls_policy_maps_long": "Forzar política de TLS saliente",
|
||||
"tls_policy_maps_info": "Esta póliza anula las reglas de transporte TLS salientes independientemente de la configuración de la política TLS de los usuarios.<br>\r\n Revisa <a href=\"http://www.postfix.org/postconf.5.html#smtp_tls_policy_maps\" target=\"_blank\">the \"smtp_tls_policy_maps\" docs</a> para mas información.",
|
||||
"tls_policy_maps_long": "Forzar póliza de TLS saliente",
|
||||
"toggle_all": "Selecionar todo",
|
||||
"username": "Nombre de usuario",
|
||||
"waiting": "Esperando",
|
||||
@@ -987,12 +942,7 @@
|
||||
"table_size_show_n": "Mostrar %s elementos",
|
||||
"templates": "Plantillas",
|
||||
"template": "Plantilla",
|
||||
"yes": "✓",
|
||||
"add_alias_expand": "Extender el alias a los dominios alias",
|
||||
"alias_domain_backupmx": "Dominio alias inactivo para el dominio de retransmisión",
|
||||
"catch_all": "Recoge-todo (catch-all)",
|
||||
"force_tfa": "2FA",
|
||||
"last_run_reset": "Programar la siguiente"
|
||||
"yes": "✓"
|
||||
},
|
||||
"oauth2": {
|
||||
"access_denied": "Inicie sesión como propietario del buzón para otorgar acceso a través de OAuth2.",
|
||||
@@ -1017,8 +967,8 @@
|
||||
"medium_danger": "Peligro medio",
|
||||
"neutral_danger": "Neutral",
|
||||
"qhandler_success": "Solicitud enviada con éxito al sistema, puede cerrar la ventana",
|
||||
"qid": "QID de Rspamd",
|
||||
"qitem": "Elemento en cuarentena",
|
||||
"qid": "Rspamd QID",
|
||||
"qitem": "Quarantine item",
|
||||
"quarantine": "Cuarentena",
|
||||
"quick_actions": "Acciones",
|
||||
"rcpt": "Destinatario",
|
||||
@@ -1040,29 +990,14 @@
|
||||
"download_eml": "Descargar (.eml)",
|
||||
"info": "Información",
|
||||
"junk_folder": "Carpeta de correo no deseado",
|
||||
"notified": "Notificado",
|
||||
"qinfo": "El sistema de cuarentena guarda en la base de datos el correo rechazado (al remitente <em>no</em> se le dará la impresión de que el correo fue entregado) y también el correo que se entrega como copia en la carpeta de correo no deseado de un buzón.\r\n <br>\"Aprender como spam y eliminar\" aprende un mensaje como spam mediante el teorema de Bayes y calcula hashes difusos para rechazar mensajes similares en el futuro.\r\n <br>Tenga en cuenta que aprender varios mensajes puede llevar tiempo, según su sistema.<br>Los elementos de la lista de bloqueo se excluyen de la cuarentena.",
|
||||
"quick_delete_link": "Abrir enlace de eliminación rápida",
|
||||
"quick_release_link": "Abrir enlace de liberación rápida",
|
||||
"rewrite_subject": "Reescribir asunto",
|
||||
"sender_header": "Remitente (encabezado \"From\")",
|
||||
"settings_info": "Cantidad máxima de elementos en cuarentena (por buzón): %s<br>Tamaño máximo de correo: %s MiB"
|
||||
"notified": "Notificado"
|
||||
},
|
||||
"queue": {
|
||||
"queue_manager": "Administrador de cola",
|
||||
"flush": "Vaciar la cola",
|
||||
"info": "La cola de correo contiene todos los correos que esperan ser entregados. Si un correo permanece mucho tiempo en la cola, el sistema lo elimina automáticamente.<br>El mensaje de error de cada correo indica por qué no se pudo entregar.",
|
||||
"legend": "Acciones de la cola de correo:",
|
||||
"deliver_mail_legend": "Intenta volver a entregar los correos seleccionados.",
|
||||
"hold_mail_legend": "Retiene los correos seleccionados. (Impide nuevos intentos de entrega)",
|
||||
"show_message": "Mostrar mensaje",
|
||||
"unban": "desbloquear cola",
|
||||
"unhold_mail": "Liberar",
|
||||
"unhold_mail_legend": "Libera los correos seleccionados para su entrega. (Requiere que hayan sido retenidos antes)"
|
||||
"queue_manager": "Administrador de cola"
|
||||
},
|
||||
"start": {
|
||||
"help": "Mostrar/Ocultar panel de ayuda",
|
||||
"imap_smtp_server_auth_info": "Utilice su dirección de correo completa y el mecanismo de autenticación PLAIN.<br>\r\nSus datos de inicio de sesión se cifran gracias al cifrado obligatorio del servidor"
|
||||
"imap_smtp_server_auth_info": "Por favor utiliza tu dirección de correo completa y el mecanismo de autenticación PLAIN.<br>\r\nTus datos para iniciar sesión serán cifrados por el cifrado obligatorio del servidor"
|
||||
},
|
||||
"success": {
|
||||
"acl_saved": "ACL para el objeto %s guardado",
|
||||
@@ -1089,8 +1024,8 @@
|
||||
"domain_admin_removed": "Administrador del dominio %s removido",
|
||||
"domain_modified": "Cambios al dominio %s guardados",
|
||||
"domain_removed": "Dominio %s removido",
|
||||
"forwarding_host_added": "Se agregó el host de reenvío %s",
|
||||
"forwarding_host_removed": "Se eliminó el host de reenvío %s",
|
||||
"forwarding_host_added": "Forwarding host %s has been added",
|
||||
"forwarding_host_removed": "Forwarding host %s has been removed",
|
||||
"logged_in_as": "Conectado como %s",
|
||||
"mailbox_added": "Buzón %s agregado",
|
||||
"mailbox_modified": "Cambios al buzón %s guardados",
|
||||
@@ -1108,86 +1043,31 @@
|
||||
"saved_settings": "Ajustes guardados",
|
||||
"settings_map_added": "Ajustes agregados",
|
||||
"settings_map_removed": "Ajustes del ID %s removidos",
|
||||
"tls_policy_map_entry_deleted": "Se eliminó la regla de política de TLS con ID %s",
|
||||
"tls_policy_map_entry_saved": "Se guardó la regla de política de TLS \"%s\"",
|
||||
"tls_policy_map_entry_deleted": "Regla de póliza de TLS con ID %s ha sido elimindada",
|
||||
"tls_policy_map_entry_saved": "Regla de póliza de TLS \"%s\" ha sido guardada",
|
||||
"verified_totp_login": "Inicio de sesión TOTP verificado",
|
||||
"verified_webauthn_login": "Inicio de sesión WebAuthn verificado",
|
||||
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado",
|
||||
"admin_added": "Se agregó el administrador %s",
|
||||
"admin_api_modified": "Se guardaron los cambios en la API",
|
||||
"admin_removed": "Se eliminó el administrador %s",
|
||||
"app_links": "Se guardaron los cambios en los enlaces de aplicaciones",
|
||||
"app_passwd_added": "Se agregó una nueva contraseña de aplicación",
|
||||
"app_passwd_removed": "Se eliminó la contraseña de aplicación con ID %s",
|
||||
"cors_headers_edited": "Se guardó la configuración de CORS",
|
||||
"custom_login_modified": "La personalización del inicio de sesión se guardó correctamente",
|
||||
"domain_add_dkim_available": "Ya existía una clave DKIM",
|
||||
"dkim_duplicated": "La clave DKIM del dominio %s se copió a %s",
|
||||
"domain_footer_modified": "Se guardaron los cambios en el pie de página del dominio %s",
|
||||
"dovecot_restart_success": "Dovecot se reinició correctamente",
|
||||
"eas_reset": "Se restablecieron los dispositivos ActiveSync del usuario %s",
|
||||
"f2b_banlist_refreshed": "El ID de la lista de bloqueo se actualizó correctamente.",
|
||||
"f2b_modified": "Se guardaron los cambios en los parámetros de Fail2ban",
|
||||
"global_filter_written": "El filtro se escribió correctamente en el archivo",
|
||||
"hash_deleted": "Hash eliminado",
|
||||
"iam_test_connection": "Conexión correcta",
|
||||
"ip_check_opt_in_modified": "La comprobación de IP se guardó correctamente",
|
||||
"item_deleted": "Elemento %s eliminado correctamente",
|
||||
"item_released": "Elemento %s liberado",
|
||||
"items_deleted": "Elemento %s eliminado correctamente",
|
||||
"items_released": "Se liberaron los elementos seleccionados",
|
||||
"learned_ham": "El ID %s se aprendió correctamente como correo legítimo (ham)",
|
||||
"license_modified": "Se guardaron los cambios en la licencia",
|
||||
"mailbox_renamed": "El buzón se renombró de %s a %s",
|
||||
"nginx_reloaded": "Nginx se recargó",
|
||||
"object_modified": "Se guardaron los cambios en el objeto %s",
|
||||
"password_policy_saved": "La política de contraseñas se guardó correctamente",
|
||||
"password_changed_success": "La contraseña se cambió correctamente",
|
||||
"pushover_settings_edited": "La configuración de Pushover se guardó; verifique las credenciales.",
|
||||
"recovery_email_sent": "Se envió el correo de recuperación a %s",
|
||||
"reset_main_logo": "Se restableció el logo predeterminado",
|
||||
"resource_removed": "Se eliminó el recurso %s",
|
||||
"sogo_profile_reset": "Se restableció el perfil de SOGo del usuario %s",
|
||||
"template_added": "Se agregó la plantilla %s",
|
||||
"template_modified": "Se guardaron los cambios en la plantilla %s",
|
||||
"template_removed": "Se eliminó la plantilla con ID %s",
|
||||
"ui_texts": "Se guardaron los cambios en los textos de la interfaz",
|
||||
"ui_theme_modified": "El tema se guardó correctamente",
|
||||
"upload_success": "Archivo subido correctamente",
|
||||
"verified_fido2_login": "Inicio de sesión FIDO2 verificado"
|
||||
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado"
|
||||
},
|
||||
"tfa": {
|
||||
"api_register": "%s utiliza la API en la nube de Yubico. Obtenga una clave de API para su llave <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aquí</a>.",
|
||||
"api_register": "%s utiliza la API de la nube de Yubico. Por favor, obtén una clave API para tu llave <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aquí</a>.",
|
||||
"confirm": "Confirmar",
|
||||
"confirm_totp_token": "Confirme los cambios ingresando el token generado",
|
||||
"confirm_totp_token": "Por favor confirma los cambios ingresando el token generado",
|
||||
"delete_tfa": "Desactivar TFA",
|
||||
"disable_tfa": "Desactivar TFA hasta el próximo inicio de sesión exitoso",
|
||||
"enter_qr_code": "Su código TOTP si su dispositivo no puede escanear códigos QR",
|
||||
"key_id": "Un identificador para su YubiKey",
|
||||
"key_id_totp": "Un identificador para su llave",
|
||||
"key_id": "Un identificador para tu YubiKey",
|
||||
"key_id_totp": "Un identificador para tu llave",
|
||||
"none": "Desactivar",
|
||||
"scan_qr_code": "Escanee el siguiente código con su aplicación de autenticador o ingrese el código manualmente.",
|
||||
"select": "Seleccione",
|
||||
"select": "Selecciona",
|
||||
"set_tfa": "Establecer el método de autenticación de dos factores",
|
||||
"tfa": "Autenticación de dos factores",
|
||||
"totp": "OTP basado en tiempo (Google Authenticator, Authy, etc.)",
|
||||
"webauthn": "Autenticación WebAuthn",
|
||||
"waiting_usb_auth": "<i>Esperando al dispositivo USB...</i><br><br>Toque el botón en su dispositivo USB WebAuthn ahora.",
|
||||
"waiting_usb_register": "<i>Esperando al dispositivo USB....</i><br><br>Ingrese su contraseña arriba y confirme su registro WebAuthn tocando el botón en su dispositivo USB WebAuthn.",
|
||||
"yubi_otp": "Yubico OTP",
|
||||
"authenticators": "Autenticadores",
|
||||
"force_tfa": "Exigir configurar 2FA al iniciar sesión",
|
||||
"force_tfa_info": "El usuario deberá configurar la autenticación de dos factores antes de acceder al panel.",
|
||||
"setup_title": "Autenticación de dos factores obligatoria",
|
||||
"setup_required": "Su cuenta requiere autenticación de dos factores. Configure un método de 2FA para continuar.",
|
||||
"cancel_setup": "Cancelar y cerrar sesión",
|
||||
"error_code": "Código de error",
|
||||
"init_webauthn": "Inicializando, espere...",
|
||||
"reload_retry": "- (recargue el navegador si el error persiste)",
|
||||
"start_webauthn_validation": "Iniciar validación",
|
||||
"tfa_token_invalid": "Token de 2FA no válido",
|
||||
"u2f_deprecated": "Parece que su llave se registró con el método obsoleto U2F. Desactivaremos la autenticación de dos factores y eliminaremos su llave.",
|
||||
"u2f_deprecated_important": "Registre su llave en el panel de administración con el nuevo método WebAuthn."
|
||||
"yubi_otp": "Yubico OTP"
|
||||
},
|
||||
"user": {
|
||||
"action": "Acción",
|
||||
@@ -1195,7 +1075,7 @@
|
||||
"alias": "Alias",
|
||||
"alias_create_random": "Generar alias aleatorio",
|
||||
"alias_extend_all": "Extender alias por 1 hora",
|
||||
"alias_full_date": "d/m/Y, H:i:s T",
|
||||
"alias_full_date": "d.m.Y, H:i:s T",
|
||||
"alias_remove_all": "Eliminar todos los alias",
|
||||
"alias_select_validity": "Periodo de validez",
|
||||
"alias_time_left": "Tiempo restante",
|
||||
@@ -1246,7 +1126,7 @@
|
||||
"spamfilter_bl": "Lista negra",
|
||||
"spamfilter_bl_desc": "Direcciones en la lista negra <b>siempre</b> clasificarán como spam. Se puede usar comodines. Filtro solo aplica a los alias directos (alias con un solo buzón de destino) excluyendo los alias catch-all.",
|
||||
"spamfilter_default_score": "Valores predeterminados:",
|
||||
"spamfilter_green": "Verde: este mensaje no es spam",
|
||||
"spamfilter_green": "Verde: éste mensaje no es spam",
|
||||
"spamfilter_hint": "El primer valor representa la \"calificación baja de spam\", el segundo representa la \"calificación alta de spam\".",
|
||||
"spamfilter_red": "Rojo: Este mensaje es spam, será rechazado por el servidor y enviado a la quarantena (si esta configurada)",
|
||||
"spamfilter_table_action": "Acción",
|
||||
@@ -1256,8 +1136,8 @@
|
||||
"spamfilter_table_rule": "Regla",
|
||||
"spamfilter_wl": "Lista blanca",
|
||||
"spamfilter_wl_desc": "Direcciones en la lista blanca <b>nunca</b> clasificarán como spam. Se puede usar comodines. Filtro solo aplica a los alias directos (alias con un solo buzón de destino) excluyendo los alias catch-all.",
|
||||
"spamfilter_yellow": "Amarillo: este mensaje puede ser spam, será etiquetado como spam y movido a su carpeta de correo no deseado",
|
||||
"status": "Estado",
|
||||
"spamfilter_yellow": "Amarillo: éste mensaje puede ser spam, será etiquetado como spam y trasladado a tu carpeta de correo no deseado",
|
||||
"status": "Status",
|
||||
"sync_jobs": "Trabajos de sincronización",
|
||||
"tag_handling": "Establecer manejo para el correo etiquetado",
|
||||
"tag_help_example": "Ejemplo de una dirección email etiquetada: mi<b>+Facebook</b>@ejemplo.org",
|
||||
@@ -1277,89 +1157,13 @@
|
||||
"weeks": "Semanas",
|
||||
"with_app_password": "con contraseña de aplicación",
|
||||
"year": "año",
|
||||
"years": "años",
|
||||
"active_sieve": "Filtro activo",
|
||||
"advanced_settings": "Configuración avanzada",
|
||||
"app_hint": "Las contraseñas de aplicación son contraseñas alternativas para iniciar sesión en IMAP, SMTP, CalDAV, CardDAV y EAS. El nombre de usuario no cambia. El webmail SOGo no está disponible con contraseñas de aplicación.",
|
||||
"allowed_protocols": "Protocolos permitidos",
|
||||
"app_name": "Nombre de la aplicación",
|
||||
"app_passwds": "Contraseñas de aplicación",
|
||||
"apple_connection_profile": "Perfil de conexión de Apple",
|
||||
"apple_connection_profile_complete": "Este perfil de conexión incluye los parámetros de IMAP y SMTP, y las rutas de CalDAV (calendarios) y CardDAV (contactos) para un dispositivo Apple.",
|
||||
"apple_connection_profile_mailonly": "Este perfil de conexión incluye los parámetros de configuración de IMAP y SMTP para un dispositivo Apple.",
|
||||
"apple_connection_profile_with_app_password": "Se genera una nueva contraseña de aplicación y se agrega al perfil, para que no haga falta escribir ninguna contraseña al configurar el dispositivo. No comparta el archivo: da acceso completo a su buzón.",
|
||||
"attribute": "Atributo",
|
||||
"authentication": "Autenticación",
|
||||
"change_password_hint_app_passwords": "Su cuenta tiene %d contraseñas de aplicación que no se cambiarán. Para administrarlas, vaya a la pestaña Contraseñas de aplicación.",
|
||||
"clear_recent_successful_connections": "Borrar las conexiones correctas registradas",
|
||||
"client_configuration": "Mostrar guías de configuración para clientes de correo y teléfonos",
|
||||
"create_app_passwd": "Crear contraseña de aplicación",
|
||||
"delete_ays": "Confirme la eliminación.",
|
||||
"direct_protocol_access": "Este usuario de buzón tiene <b>acceso directo y externo</b> a los siguientes protocolos y aplicaciones. Su administrador controla este ajuste. Puede crear contraseñas de aplicación para dar acceso a protocolos y aplicaciones concretos.<br>El botón \"Webmail\" ofrece inicio de sesión único en SOGo y siempre está disponible.",
|
||||
"email": "Correo electrónico",
|
||||
"email_and_dav": "Correo, calendarios y contactos",
|
||||
"empty": "Sin resultados",
|
||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
||||
"force_pw_update": "<b>Debe</b> establecer una nueva contraseña para acceder a los servicios de groupware.",
|
||||
"from": "de",
|
||||
"generate": "generar",
|
||||
"in_use": "En uso",
|
||||
"last_mail_login": "Último inicio de sesión de correo",
|
||||
"last_pw_change": "Último cambio de contraseña",
|
||||
"last_ui_login": "Último inicio de sesión en la interfaz",
|
||||
"loading": "Cargando...",
|
||||
"login_history": "Historial de inicios de sesión",
|
||||
"mailbox": "Buzón",
|
||||
"mailbox_general": "General",
|
||||
"mailbox_settings": "Configuración",
|
||||
"messages": "mensajes",
|
||||
"month": "mes",
|
||||
"months": "meses",
|
||||
"no_active_filter": "No hay ningún filtro activo",
|
||||
"no_last_login": "No hay información del último inicio de sesión en la interfaz",
|
||||
"open_logs": "Abrir registros",
|
||||
"open_webmail_sso": "Webmail",
|
||||
"overview": "Resumen",
|
||||
"password": "Contraseña",
|
||||
"pw_update_required": "Su cuenta requiere un cambio de contraseña. Establezca una nueva contraseña para continuar.",
|
||||
"password_repeat": "Contraseña (repetir)",
|
||||
"password_reset_info": "Si no se indica un correo de recuperación de contraseña, esta función no se puede usar.",
|
||||
"protocols": "Protocolos",
|
||||
"pushover_evaluate_x_prio": "Escalar el correo de alta prioridad [<code>X-Priority: 1</code>]",
|
||||
"pushover_info": "La configuración de notificaciones push se aplicará a todo el correo limpio (no spam) entregado a <b>%s</b>, incluidos los alias (compartidos, no compartidos, etiquetados).",
|
||||
"pushover_only_x_prio": "Considerar solo el correo de alta prioridad [<code>X-Priority: 1</code>]",
|
||||
"pushover_sender_array": "Considerar las siguientes direcciones de remitente <small>(separadas por comas)</small>",
|
||||
"pushover_sender_regex": "Filtrar remitentes con la siguiente expresión regular",
|
||||
"pushover_text": "Texto de la notificación",
|
||||
"pushover_title": "Título de la notificación",
|
||||
"pushover_sound": "Sonido",
|
||||
"pushover_vars": "Si no se define ningún filtro de remitente, se considerarán todos los correos.<br>Los filtros por expresión regular y las comprobaciones exactas de remitente pueden definirse por separado y se evalúan en orden. No dependen entre sí.<br>Variables utilizables para el texto y el título (tenga en cuenta las normas de protección de datos)",
|
||||
"pushover_verify": "Verificar credenciales",
|
||||
"pw_recovery_email": "Correo de recuperación de contraseña",
|
||||
"q_add_header": "Carpeta de correo no deseado",
|
||||
"quarantine_category_info": "La categoría de notificación \"Rechazado\" incluye el correo que fue rechazado, mientras que \"Carpeta de correo no deseado\" avisa al usuario de los correos que se movieron a esa carpeta.",
|
||||
"recent_successful_connections": "Conexiones correctas registradas",
|
||||
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">La comprobación de remitente está desactivada</span>",
|
||||
"show_sieve_filters": "Mostrar el filtro Sieve activo del usuario",
|
||||
"sogo_profile_reset_help": "Esto destruirá el perfil de SOGo del usuario y <b>eliminará de forma irrecuperable todos sus contactos y datos de calendario</b>.",
|
||||
"spam_score_reset": "Restablecer al valor predeterminado del servidor",
|
||||
"spamfilter_table_domain_policy": "n/d (política del dominio)",
|
||||
"syncjob_check_log": "Revisar registro",
|
||||
"syncjob_EX_OK": "Correcto",
|
||||
"syncjob_EXIT_OVERQUOTA": "El buzón de destino superó su cuota",
|
||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Usuario o contraseña incorrectos",
|
||||
"tfa_info": "La autenticación de dos factores ayuda a proteger su cuenta. Si la activa, necesitará contraseñas de aplicación para iniciar sesión en aplicaciones o servicios que no admiten la autenticación de dos factores (p. ej., clientes de correo)."
|
||||
"years": "años"
|
||||
},
|
||||
"warning": {
|
||||
"domain_added_sogo_failed": "Se agregó el dominio, pero no se pudo reiniciar SOGo; revise los registros del servidor.",
|
||||
"domain_added_sogo_failed": "Se agregó el dominio pero no se pudo reiniciar SOGo, revisa los logs del servidor.",
|
||||
"fuzzy_learn_error": "Error aprendiendo hash: %s",
|
||||
"ip_invalid": "IP inválida omitida: %s",
|
||||
"cannot_delete_self": "No se puede eliminar el usuario conectado",
|
||||
"dovecot_restart_failed": "No se pudo reiniciar Dovecot, revise los registros",
|
||||
"is_not_primary_alias": "Se omitió el alias no principal %s",
|
||||
"quota_exceeded_scope": "Se superó la cuota del dominio: en este dominio solo se pueden crear buzones ilimitados.",
|
||||
"session_token": "Token de formulario no válido: los tokens no coinciden",
|
||||
"session_ua": "Token de formulario no válido: error de validación del User-Agent"
|
||||
"cannot_delete_self": "No se puede eliminar el usuario conectado"
|
||||
},
|
||||
"datatables": {
|
||||
"collapse_all": "Contraer todo",
|
||||
@@ -1400,12 +1204,5 @@
|
||||
"register_status": "Estado de registro",
|
||||
"rename": "Renombrar",
|
||||
"start_fido2_validation": "Iniciar validación FIDO2"
|
||||
},
|
||||
"ratelimit": {
|
||||
"disabled": "Desactivado",
|
||||
"second": "mensajes / segundo",
|
||||
"minute": "mensajes / minuto",
|
||||
"hour": "mensajes / hora",
|
||||
"day": "mensajes / día"
|
||||
}
|
||||
}
|
||||
@@ -111,8 +111,7 @@
|
||||
"app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application",
|
||||
"dry": "Simuler la synchronisation",
|
||||
"internal": "Interne",
|
||||
"internal_info": "Les alias internes sont accessibles uniquement depuis le domaine ou les alias du domaine.",
|
||||
"sender_allowed": "Autoriser l'envoi sous cet alias"
|
||||
"internal_info": "Les alias internes sont accessibles uniquement depuis le domaine ou les alias du domaine."
|
||||
},
|
||||
"admin": {
|
||||
"access": "Accès",
|
||||
@@ -557,9 +556,7 @@
|
||||
"max_age_invalid": "L'âge maximum %s est invalide",
|
||||
"mode_invalid": "Le mode %s est invalide",
|
||||
"mx_invalid": "L'enregistrement MX %s est invalide",
|
||||
"version_invalid": "La version %s est invalide",
|
||||
"tfa_removal_blocked": "L’authentification à deux facteurs ne peut pas être supprimée, car elle est requise pour votre compte.",
|
||||
"quarantine_category_invalid": "La catégorie de quarantaine doit être l’une des suivantes : add_header, reject, all\""
|
||||
"version_invalid": "La version %s est invalide"
|
||||
},
|
||||
"debug": {
|
||||
"chart_this_server": "Graphique (ce serveur)",
|
||||
@@ -758,9 +755,7 @@
|
||||
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> est un standard qui oblige la délivrance des courriels entre les serveurs de courriels à utiliser TLS avec des certificats valides. <br>Il est utilisé quand <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> n'est pas possible à cause d'un manque ou d'un non support de DNSSEC.<br><b>Note</b> : Si le domaine du destinataire supporte DANE avec DNSSEC, DANE est <b>toujours</b> préféré – MTA-STS sert seulement en secours.",
|
||||
"mta_sts_mode_info": "Il y a trois modes parmi lesquels choisir :<ul><li><em>testing</em> – la politique est seulement surveillée, les violations n'ont pas d'impact.</li><li><em>enforce</em> – la politique est appliquée strictement, les connexions sans TLS valide sont rejetées.</li><li><em>none</em> – la politique est publiée mais non appliquée.</li></ul>",
|
||||
"mta_sts_max_age_info": "Durée en secondes pendant laquelle les serveurs de courriel peuvent mettre en cache cette politique avant de revérifier.",
|
||||
"mta_sts_mx_info": "Autoriser l'envoi uniquement aux noms d'hôtes des serveurs de courriels indiqués explicitement ; le MTA émetteur vérifie si le nom d'hôte DNS du MX correspond à la liste de la politique, et autorise la délivrance seulement avec un certificat TLS valide (protège contre le MITM).",
|
||||
"sender_allowed": "Autoriser l'envoi sous cet alias",
|
||||
"sender_allowed_info": "Si cette option est désactivée, cet alias peut uniquement recevoir des courriels. Utilisez les ACL d’expéditeur pour autoriser certaines boîtes aux lettres à envoyer des messages via cet alias."
|
||||
"mta_sts_mx_info": "Autoriser l'envoi uniquement aux noms d'hôtes des serveurs de courriels indiqués explicitement ; le MTA émetteur vérifie si le nom d'hôte DNS du MX correspond à la liste de la politique, et autorise la délivrance seulement avec un certificat TLS valide (protège contre le MITM)."
|
||||
},
|
||||
"footer": {
|
||||
"cancel": "Annuler",
|
||||
@@ -993,8 +988,7 @@
|
||||
"recipient": "Destinataire",
|
||||
"open_logs": "Afficher les journaux",
|
||||
"iam": "Fournisseur d'identité",
|
||||
"internal": "Interne",
|
||||
"force_tfa": "TFA"
|
||||
"internal": "Interne"
|
||||
},
|
||||
"oauth2": {
|
||||
"access_denied": "Veuillez vous connecter en tant que propriétaire de la boîte de réception pour accorder l’accès via Oauth2.",
|
||||
@@ -1195,11 +1189,7 @@
|
||||
"yubi_otp": "Authentification OTP Yubico",
|
||||
"authenticators": "Authentificateurs",
|
||||
"u2f_deprecated_important": "Veuillez enregistrer votre clé dans le panneau d'administration avec la nouvelle méthode WebAuthn.",
|
||||
"u2f_deprecated": "Il semble que votre clé ait été enregistrée à l'aide de la méthode U2F obsolète. Nous allons désactiver l'authentification à deux facteurs pour vous et supprimer votre clé.",
|
||||
"force_tfa": "Imposer l'authentification à deux facteurs lors de la connexion",
|
||||
"force_tfa_info": "L’utilisateur doit configurer l’authentification à deux facteurs avant de pouvoir accéder à l’interface.",
|
||||
"setup_title": "Authentification à deux facteurs requise",
|
||||
"setup_required": "Votre compte nécessite l’authentification à deux facteurs. Veuillez configurer une méthode 2FA pour continuer."
|
||||
"u2f_deprecated": "Il semble que votre clé ait été enregistrée à l'aide de la méthode U2F obsolète. Nous allons désactiver l'authentification à deux facteurs pour vous et supprimer votre clé."
|
||||
},
|
||||
"fido2": {
|
||||
"set_fn": "Définir un nom",
|
||||
@@ -1388,8 +1378,7 @@
|
||||
"forever": "Pour toujours",
|
||||
"spam_aliases_info": "Un alias de spam est une adresse de courriel temporaire qui peut être utilisée pour protéger les véritables adresses de courriel. <br> De manière optionnelle, une durée d'expiration peut être définie afin que l'alias soit automatiquement désactivé après la période définie, éliminant ainsi les adresses étant abusées ou ayant fuité.",
|
||||
"authentication": "Authentification",
|
||||
"protocols": "Protocoles",
|
||||
"pw_update_required": "Votre compte nécessite un changement de mot de passe. Veuillez définir un nouveau mot de passe pour continuer."
|
||||
"protocols": "Protocoles"
|
||||
},
|
||||
"warning": {
|
||||
"cannot_delete_self": "Impossible de supprimer l’utilisateur connecté",
|
||||
|
||||
@@ -1144,8 +1144,7 @@
|
||||
"subscribeall": "Feliratkozás minden mappára",
|
||||
"syncjob": "Szinkronizálási feladat hozzáadása",
|
||||
"internal": "Belső",
|
||||
"internal_info": "Belső álnevek csak a saját domain vagy domain álnév számára elérhető.",
|
||||
"sender_allowed": "Küldés engedélyezése ezzel az aliasszal"
|
||||
"internal_info": "Belső álnevek csak a saját domain vagy domain álnév számára elérhető."
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Hozzáférés megtagatva vagy nem megfelelő űrlap adat",
|
||||
@@ -1246,21 +1245,6 @@
|
||||
"pushover_key": "A pushover kulcs rossz formátumú",
|
||||
"pushover_token": "A Pushover token rossz formátumú",
|
||||
"quota_not_0_not_numeric": "A kvótának numerikusnak és >= 0-nak kell lennie.",
|
||||
"recipient_map_entry_exists": "Létezik egy \"%s\" címzett-térkép bejegyzés",
|
||||
"redis_error": "Redis hiba lépett fel: %s",
|
||||
"relayhost_invalid": "A(z) %s elem érvénytelen a leképezésben.",
|
||||
"release_send_failed": "Az üzenet felszabadítása sikertelen: %s",
|
||||
"reset_f2b_regex": "A regex-szűrő időtúllépés miatt nem állt le. Próbálja újra, vagy várjon egy kicsit, és töltse újra az oldalt.",
|
||||
"resource_invalid": "A(z) %s erőforrásnév érvénytelen",
|
||||
"rl_timeframe": "Érvénytelen időkeret a lekérdezési korláthoz",
|
||||
"rspamd_ui_pw_length": "A Rspamd UI jelszónak legalább 6 karakter hosszúnak kell lennie.",
|
||||
"script_empty": "A szkript nem lehet üres",
|
||||
"sender_acl_invalid": "A küldőhöz tartozó ACL-érték (%s) érvénytelen",
|
||||
"set_acl_failed": "Az ACL beállítása meghiúsult",
|
||||
"settings_map_invalid": "Érvénytelen beállítás-leképezési azonosító: %s",
|
||||
"recovery_email_failed": "A helyreállítási email kiküldése sikertelen. Kérlek, lépj kapcsolatba az adminisztrátorral!",
|
||||
"reset_token_limit_exceeded": "Túl sok visszaállítási kísérlet. Kérjük, várjon, mielőtt újra próbálkozna.",
|
||||
"required_data_missing": "Hiányzik a(z) szükséges %s adat",
|
||||
"tfa_removal_blocked": "A kétfaktoros hitelesítés nem távolítható el, mert elengedhetetlen a fiókod használatához."
|
||||
"recipient_map_entry_exists": "Létezik egy \"%s\" címzett-térkép bejegyzés"
|
||||
}
|
||||
}
|
||||
@@ -109,10 +109,7 @@
|
||||
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
|
||||
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
|
||||
"tags": "Tag",
|
||||
"dry": "Simula sincronizzazione",
|
||||
"internal": "Interno",
|
||||
"internal_info": "Gli alias interni sono accessibili solo dal proprio dominio o dai domini alias.",
|
||||
"sender_allowed": "Consenti l'invio con questo alias"
|
||||
"dry": "Simula sincronizzazione"
|
||||
},
|
||||
"admin": {
|
||||
"access": "Accedi",
|
||||
@@ -134,7 +131,7 @@
|
||||
"additional_rows": " righe aggiuntive inserite",
|
||||
"admin": "Amministratore",
|
||||
"admin_details": "Modifica impostazioni amministratore",
|
||||
"admin_domains": "Assegnazioni di dominio",
|
||||
"admin_domains": "Assengazioni di dominio",
|
||||
"admins": "Amministratori",
|
||||
"admins_ldap": "Amministratori LDAP",
|
||||
"advanced_settings": "Impostazioni avanzate",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"domain_relayhost": "Mainīt domēna relayhost",
|
||||
"eas_reset": "EAS ierīču atiestatīšana",
|
||||
"extend_sender_acl": "Ļauj paplašināt sūtītāja ACL ar ārējām adresēm",
|
||||
"login_as": "Pieteikties kā pastkastes lietotājam",
|
||||
"login_as": "Pieteikšanās kā pastkastes lietotājam",
|
||||
"mailbox_relayhost": "Pasta kastītes relayhost maiņa",
|
||||
"prohibited": "Aizliegts ar ACL",
|
||||
"protocol_access": "Protokola piekļuves maiņa",
|
||||
@@ -91,12 +91,12 @@
|
||||
"activate_api": "Aktivizēt API",
|
||||
"active": "Aktīvs",
|
||||
"add": "Pievienot",
|
||||
"add_domain_admin": "Pievienot domēna pārvaldītāju",
|
||||
"add_domain_admin": "Pievienot domēna administratoru",
|
||||
"add_forwarding_host": "Pievienot pāradresācijas hostu",
|
||||
"add_relayhost": "Pievienot Relayhost",
|
||||
"add_row": "Pievienot rindu",
|
||||
"admin": "Pārvaldītājs",
|
||||
"admin_details": "Labot informāciju par pārvaldītāju",
|
||||
"admin": "Administrators",
|
||||
"admin_details": "Labot administratora detaļas",
|
||||
"admin_domains": "Domēna uzdevumi",
|
||||
"api_allow_from": "Atļaut API piekļuvi no šīm IP",
|
||||
"api_key": "API atslēga",
|
||||
@@ -114,7 +114,7 @@
|
||||
"dkim_keys": "ARC/DKIM atslēgas",
|
||||
"dkim_private_key": "Privāta atslēga",
|
||||
"domain": "Domēns",
|
||||
"domain_admins": "Domēna pārvaldītāji",
|
||||
"domain_admins": "Domēna administratori",
|
||||
"edit": "Labot",
|
||||
"empty": "Nav iznākuma",
|
||||
"f2b_ban_time": "Aizlieguma laiks (s)",
|
||||
@@ -188,14 +188,7 @@
|
||||
"quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:<br><small>Noklusējums ir 9999.0</small>",
|
||||
"options": "Iespējas",
|
||||
"password_reset_settings": "Paroļu atkopes iestatījumi",
|
||||
"password_settings": "Paroļu iestatījumi",
|
||||
"add_admin": "Pievienot pārvaldītāju",
|
||||
"admins": "Pārvaldītāji",
|
||||
"admins_ldap": "LDAP pārvaldītāji",
|
||||
"admin_quicklink": "Paslēpt ātro saiti uz pārvaldītāju pieteikšanās lapu",
|
||||
"domain_admin": "Domēna pārvaldītājs",
|
||||
"domainadmin_quicklink": "Paslēpt ātro saiti uz domēna pārvaldītāju pieteikšanās lapu",
|
||||
"user_quicklink": "Paslēpt ātro saiti uz lietotāju pieteikšanās lapu"
|
||||
"password_settings": "Paroļu iestatījumi"
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Piekļuve liegta, vai nepareizi dati",
|
||||
@@ -251,10 +244,7 @@
|
||||
"app_passwd_id_invalid": "Lietotnes paroles Id %s ir nederīgs",
|
||||
"img_dimensions_exceeded": "Attēls pārsniedz lielāko pieļaujamo attēla lielumu",
|
||||
"img_size_exceeded": "Attēls pārsniedz lielāko pieļaujamo datnes lielumu",
|
||||
"version_invalid": "Versija %s ir nederīga",
|
||||
"generic_server_error": "Atgadījās neparedzēta servera kļūda. Lūgums sazināties ar pārvaldītāju.",
|
||||
"password_reset_na": "Paroļu atkope šobrīd nav pieejama. Lūgums sazināties ar pārvaldītāju.",
|
||||
"recovery_email_failed": "Nevarēja nosūtīt atkopes e-pasta ziņojumu. Lūgums sazināties ar pārvaldītāju."
|
||||
"version_invalid": "Versija %s ir nederīga"
|
||||
},
|
||||
"diagnostics": {
|
||||
"cname_from_a": "Vērtība, kas iegūta no A/AAAA ieraksta. Tas tiek atbalstīts tik ilgi, kamēr ieraksts norāda uz pareizo resursu.",
|
||||
@@ -276,7 +266,7 @@
|
||||
"delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī",
|
||||
"description": "Apraksts",
|
||||
"domain": "Labot domēnu",
|
||||
"domain_admin": "Labot domēna pārvaldītāju",
|
||||
"domain_admin": "Labot domēna administratoru",
|
||||
"domain_quota": "Domēna kvota",
|
||||
"domains": "Domēni",
|
||||
"dont_check_sender_acl": "Atspējot sūtītāju pārbaudi domēnam %s (+ aizstājdomēni)",
|
||||
@@ -339,8 +329,7 @@
|
||||
"app_passwd": "Lietotnes parole",
|
||||
"mta_sts_version": "Versija",
|
||||
"mta_sts_version_info": "Norāda MTA-STS standarta versiju – pašreiz ir derīga tikai <code>STSv1</code>.",
|
||||
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sūtītāja pārbaude ir atspējota</span>",
|
||||
"admin": "Labot pārvaldītāju"
|
||||
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sūtītāja pārbaude ir atspējota</span>"
|
||||
},
|
||||
"footer": {
|
||||
"cancel": "Atcelt",
|
||||
@@ -373,14 +362,7 @@
|
||||
"username": "Lietotājvārds",
|
||||
"fido2_webauthn": "FIDO/WebAuthn pieteikšanās",
|
||||
"mobileconfig_info": "Lūgums pieteikties kā pastkastes lietotājam, lai lejupielādētu pieprasīto Apple savienojuma profilu.",
|
||||
"other_logins": "vai pieteikties ar",
|
||||
"forgot_password": "> Aizmirsta parole?",
|
||||
"login_linkstext": "Nepareiza pieteikšanās?",
|
||||
"login_domainadmintext": "Pieteikties kā domēna pārvaldītājam",
|
||||
"login_admintext": "Pieteikties kā pārvaldītājam",
|
||||
"login_user": "Lietotu pieteikšanās",
|
||||
"login_dadmin": "Domēna pārvaldītāju pieteikšanās",
|
||||
"login_admin": "Pārvaldītāju pieteikšanās"
|
||||
"other_logins": "vai pieslēgties ar"
|
||||
},
|
||||
"mailbox": {
|
||||
"action": "Rīcība",
|
||||
@@ -415,7 +397,7 @@
|
||||
"description": "Apraksts",
|
||||
"dkim_key_length": "DKIM atslēgas garums (bits)",
|
||||
"domain": "Domēns",
|
||||
"domain_admins": "Domēna pārvaldītāji",
|
||||
"domain_admins": "Domēna administratori",
|
||||
"domain_aliases": "Domēna aizstājvārdi",
|
||||
"domain_quota": "Kvota",
|
||||
"domain_quota_total": "Kopējais domēna ierobežojums",
|
||||
@@ -525,7 +507,7 @@
|
||||
"imap_smtp_server_auth_info": "Lūgums izmantot pilnu e-pasta adresi un PLAIN autentificēšanās mehānismu.<br>\nPieteikšanās dati tiks šifrēti ar servera puses obligātu šifrēšanu."
|
||||
},
|
||||
"success": {
|
||||
"admin_modified": "Pārvaldītāja izmaiņas tika saglabātas",
|
||||
"admin_modified": "Izmaiņas administrātoram ir saglabātas",
|
||||
"alias_added": "Aizstājadrese %s (%d) tika pievienota",
|
||||
"alias_domain_removed": "Aizstājdomēns %s tika noņemts",
|
||||
"alias_modified": "Aizstājadreses izmaiņas %s tika saglabātas",
|
||||
@@ -536,9 +518,9 @@
|
||||
"dkim_added": "DKIM atslēga saglabāta",
|
||||
"dkim_removed": "DKIM atslēga %s ir noņemta",
|
||||
"domain_added": "Pievienots domēns %s",
|
||||
"domain_admin_added": "Tika pievienots domēna pārvaldītājs %s",
|
||||
"domain_admin_modified": "Domēna pārvaldītāja %s izmaiņas tika saglabātas",
|
||||
"domain_admin_removed": "Tika noņemts domēna pārvaldītājs %s",
|
||||
"domain_admin_added": "Domēna administrātors %s pievienots",
|
||||
"domain_admin_modified": "Izmaiņas domēna administrātoram %s ir saglabātas",
|
||||
"domain_admin_removed": "Domēna administrators %s tika noņemts",
|
||||
"domain_modified": "Izmaiņas domēnam %s ir saglabātas",
|
||||
"domain_removed": "Domēns %s ir noņemts",
|
||||
"eas_reset": "ActiveSync ierīces priekš lietotāja %s tika atiestatītas",
|
||||
@@ -566,9 +548,7 @@
|
||||
"verified_yotp_login": "Apliecināta Yubico OTP pieteikšanās",
|
||||
"app_passwd_removed": "Noņemta lietotnes parole ar Id %s",
|
||||
"app_passwd_added": "Pievienota jauna lietotnes parole",
|
||||
"f2b_banlist_refreshed": "Liegumu saraksta Id tika sekmīgi atsvaidzināts.",
|
||||
"admin_added": "Tika pievienots pārvaldītājs %s",
|
||||
"admin_removed": "Tika noņemts pārvaldītājs %s"
|
||||
"f2b_banlist_refreshed": "Liegumu saraksta Id tika sekmīgi atsvaidzināts."
|
||||
},
|
||||
"tfa": {
|
||||
"api_register": "%s izmanto Yubico Cloud API. Lūdzu iegūstiet API atslēgu priekš Jūsu atslēgas<a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">here</a>",
|
||||
@@ -589,8 +569,7 @@
|
||||
"waiting_usb_auth": "<i>Gaida USB ierīci...</i><br><br>Lūdzu, tagad nospiežiet pogu uz Jūsu WebAuthn USB ierīces.",
|
||||
"waiting_usb_register": "<i>Gaida USB ierīci...</i><br><br>Lūgums augstāk ievadīt savu paroli un apstiprināt reģistrēšanos ar USB ierīces pogas nospiešanu.",
|
||||
"yubi_otp": "Yubico OTP autentifikators",
|
||||
"authenticators": "Autentificētāji",
|
||||
"u2f_deprecated_important": "Lūgums reģistrēt savu atslēgu pārvaldības lapā ar jauno WebAuthn veidu."
|
||||
"authenticators": "Autentificētāji"
|
||||
},
|
||||
"user": {
|
||||
"action": "Rīcība",
|
||||
@@ -722,8 +701,7 @@
|
||||
"warning": {
|
||||
"domain_added_sogo_failed": "Domēns pievienots, bet neizdevās pārsāknēt SOGO. Lūgums pārbaudīt servera žurnālus.",
|
||||
"dovecot_restart_failed": "Dovecot neizdevās pārsāknēties. Lūgums pārbaudīt žurnālus",
|
||||
"is_not_primary_alias": "Izlaists aizstājvārds %s, kas nav galvenais",
|
||||
"no_active_admin": "Nevar deaktivēt pēdējo aktīvo pārvaldītāju"
|
||||
"is_not_primary_alias": "Izlaists aizstājvārds %s, kas nav galvenais"
|
||||
},
|
||||
"oauth2": {
|
||||
"access_denied": "Lūgums pieteikties kā pastkastes īpašniekam, lai nodrošinātu piekļuvi ar OAuth2."
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
"tls_policy": "Versleutelingsbeleid",
|
||||
"unlimited_quota": "Onbeperkte quota voor mailboxen",
|
||||
"domain_desc": "Wijzig domeinbeschrijving",
|
||||
"pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten",
|
||||
"domain_relayhost": "Verander relayhost voor een domein"
|
||||
"pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten"
|
||||
},
|
||||
"add": {
|
||||
"activate_filter_warn": "Alle andere filters worden gedeactiveerd zolang deze geactiveerd is.",
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
"api_info": "API jest w trakcie prac. Dokumentację można znaleźć pod adresem <a href=\"/api\">/api</a>",
|
||||
"api_key": "klucz API",
|
||||
"api_read_only": "Dostęp tylko do odczytu",
|
||||
"api_read_write": "Dostęp do odczytu i zapisu",
|
||||
"api_read_write": "Dostęp tylko do odczytu",
|
||||
"api_skip_ip_check": "Pomiń sprawdzenie IP dla API",
|
||||
"app_hide": "Ukryj dla logowania",
|
||||
"app_links": "Linki aplikacji",
|
||||
@@ -1226,7 +1226,7 @@
|
||||
"decimal": ".",
|
||||
"emptyTable": "Brak danych w tabeli",
|
||||
"expand_all": "Rozszerz wszystko",
|
||||
"info": "Wyświetlanie od _START_ do _END_ z _TOTAL_ wpisów",
|
||||
"info": "Wyświetlanie od START do END z TOTAL wpisów",
|
||||
"infoEmpty": "Wyświetlanie od 0 do 0 z 0 wpisów",
|
||||
"infoFiltered": "(filtrowane z _MAX_ suma wpisów)",
|
||||
"thousands": ",",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"add_domain_only": "Adicionar somente domínio",
|
||||
"add_domain_restart": "Adicionar domínio e reiniciar o SoGo",
|
||||
"alias_address": "Endereço (s) de alias",
|
||||
"alias_address_info": "<small>Endereço(s) de e-mail completo(s) ou @example.com, para capturar todas as mensagens de um domínio (separadas por vírgula). <b>Apenas domínios do Mailcow</b>.</small>",
|
||||
"alias_address_info": "<small>Endereço/s de e-mail completo ou @example .com, para capturar todas as mensagens de um domínio (separadas por vírgula). <b> somente domínios mailcow</b>.</small>",
|
||||
"alias_domain": "Domínio de alias",
|
||||
"alias_domain_info": "<small>Somente nomes de domínio válidos (separados por vírgula).</small>",
|
||||
"app_name": "Nome do aplicativo",
|
||||
@@ -111,8 +111,7 @@
|
||||
"validation_success": "Validado com sucesso",
|
||||
"dry": "Simular sincronização",
|
||||
"internal": "Interno",
|
||||
"internal_info": "Aliases internos são acessíveis apenas a partir do próprio domínio ou alias de domínio.",
|
||||
"sender_allowed": "Permitir enviar como este alias"
|
||||
"internal_info": "Aliases internos são acessíveis apenas a partir do próprio domínio ou alias de domínio."
|
||||
},
|
||||
"admin": {
|
||||
"access": "Acesso",
|
||||
@@ -152,13 +151,13 @@
|
||||
"arrival_time": "Hora de chegada (hora do servidor)",
|
||||
"authed_user": "Usuário autoritário",
|
||||
"ays": "Tem certeza de que deseja continuar?",
|
||||
"ban_list_info": "Veja abaixo a lista de IPs banidos: <b>rede (tempo restante do banimento) - [ações]</b>.<br />IPs na fila para serem desbanidos serão removidos da lista de banimentos ativos em alguns segundos.<br />Rótulos vermelhos indicam banimentos permanentes ativos na lista de bloqueio.",
|
||||
"ban_list_info": "Veja uma lista de IPs banidos abaixo: <b>rede (tempo restante de banimento) - [ações]</b>. <br />Os IPs na fila para serem desbanidos serão removidos da lista de banimentos ativos em alguns segundos. <br />Rótulos vermelhos indicam proibições permanentes ativas na lista negra.",
|
||||
"change_logo": "Alterar logotipo",
|
||||
"logo_normal_label": "Normal",
|
||||
"logo_dark_label": "Invertido para o modo escuro",
|
||||
"configuration": "Configuração",
|
||||
"convert_html_to_text": "Converter HTML em texto sem formatação",
|
||||
"copy_to_clipboard": "Copiado para a área de transferência!",
|
||||
"copy_to_clipboard": "Text copied to clipboard!",
|
||||
"cors_settings": "Configurações do CORS",
|
||||
"credentials_transport_warning": "<b>Aviso</b>: Adicionar uma nova entrada no mapa de transporte atualizará as credenciais de todas as entradas com uma coluna correspondente do próximo salto.",
|
||||
"customer_id": "ID do cliente",
|
||||
@@ -189,9 +188,9 @@
|
||||
"excludes": "Exclui esses destinatários",
|
||||
"f2b_ban_time": "Tempo (s) de proibição",
|
||||
"f2b_ban_time_increment": "O tempo de banimento é incrementado com cada banimento",
|
||||
"f2b_blacklist": "Redes/hosts na lista de bloqueio",
|
||||
"f2b_blacklist": "Redes/hosts na lista negra",
|
||||
"f2b_filter": "Filtros Regex",
|
||||
"f2b_list_info": "Um host ou rede na lista de bloqueio sempre terá prioridade sobre uma entidade na lista de permissões. <b>As atualizações das listas levarão alguns segundos para serem aplicadas.</b>",
|
||||
"f2b_list_info": "Um host ou rede na lista negra sempre superará uma entidade na lista branca. <b>As atualizações da lista levarão alguns segundos para serem aplicadas.</b>",
|
||||
"f2b_manage_external": "Gerenciar Fail2Ban externamente",
|
||||
"f2b_manage_external_info": "O Fail2ban ainda manterá a lista de banimentos, mas não definirá ativamente regras para bloquear o tráfego. Use a lista de banimento gerada abaixo para bloquear externamente o tráfego.",
|
||||
"f2b_max_attempts": "Máximo de tentativas",
|
||||
@@ -201,7 +200,7 @@
|
||||
"f2b_parameters": "Parâmetros do Fail2ban",
|
||||
"f2b_regex_info": "Registros considerados: SoGo, Postfix, Dovecot, PHP-FPM.",
|
||||
"f2b_retry_window": "Repita a (s) janela (s) para o máximo de tentativas",
|
||||
"f2b_whitelist": "Redes/hosts na lista de permissões",
|
||||
"f2b_whitelist": "Redes/hosts incluídos na lista branca",
|
||||
"filter_table": "Tabela de filtros",
|
||||
"forwarding_hosts": "Anfitriões de encaminhamento",
|
||||
"forwarding_hosts_add_hint": "Você pode especificar endereços IPv4/IPv6, redes em notação CIDR, nomes de host (que serão resolvidos para endereços IP) ou nomes de domínio (que serão resolvidos para endereços IP consultando registros SPF ou, na ausência deles, registros MX).",
|
||||
@@ -223,7 +222,7 @@
|
||||
"includes": "Inclua esses destinatários",
|
||||
"ip_check": "Verificação de IP",
|
||||
"ip_check_disabled": "A verificação de IP está desativada. Você pode ativá-lo em <br><strong>Sistema > Configuração > Opções > Personalizar</strong>",
|
||||
"ip_check_opt_in": "Opte por usar o serviço de terceiros <strong>ipv4.mailcow.email</strong> e <strong>ipv6.mailcow.email</strong> para resolver endereços IP externos.",
|
||||
"ip_check_opt_in": "Opte por usar o serviço de terceiros <strong>ipv4.mailcow.email.</strong> e <strong>ipv6.mailcow.email</strong> para resolver endereços IP externos.",
|
||||
"is_mx_based": "Baseado em MX",
|
||||
"last_applied": "Aplicado pela última vez",
|
||||
"license_info": "Uma licença não é necessária, mas ajuda no desenvolvimento.<br><a href=\"https://www.servercow.de/mailcow? Lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registre seu GUID aqui</a> ou <a href=\"https://www.servercow.de/mailcow? Lang=en#support\" target=\"_blank\" alt=\"Support order\">comprar suporte para sua instalação de mailcow.</a>",
|
||||
@@ -245,7 +244,7 @@
|
||||
"oauth2_add_client": "Adicionar cliente OAuth2",
|
||||
"oauth2_client_id": "ID do cliente",
|
||||
"oauth2_client_secret": "Segredo do cliente",
|
||||
"oauth2_info": "A implementação do OAuth2 suporta o tipo de concessão \"Código de Autorização\" e emite tokens de atualização.<br>\nO servidor também emite automaticamente novos tokens de atualização após um token de atualização ter sido usado.<br>\nO escopo padrão é <i>profile</i>. Somente usuários de caixa de correio podem ser autenticados com o OAuth2. Se o parâmetro de escopo for omitido, o padrão será <i>profile</i>.<br>\nO parâmetro <i>state</i> deve ser enviado pelo cliente como parte da solicitação de autorização.<br><br>\nCaminhos para solicitações à API OAuth2: <br>\n\n<ul>\n <li>Ponto de extremidade de autorização: <code>/oauth/authorize</code></li>\n <li>Endpoint do token: <code>/oauth/token</code></li>\n <li>Página de recursos: <code>/oauth/profile</code></li>\n</ul>\n\nRegenerar o segredo do cliente não expirará os códigos de autorização existentes, mas impedirá a renovação do token.<br><br>\nA revogação dos tokens de cliente causará o encerramento imediato de todas as sessões ativas. Todos os clientes precisarão se autenticar novamente.",
|
||||
"oauth2_info": "A implementação OAuth2 suporta o tipo de concessão \"Código de Autorização\" e emite tokens de atualização.<br>\nO servidor também emite automaticamente novos tokens de atualização, depois que um token de atualização foi usado.<br><br>\n• O escopo padrão é <i>perfil</i>. Somente usuários com caixa de e-mail podem ser autenticados contra o OAuth2. Se o parâmetro de escopo for omitido, ele voltará para <i>perfil</i>.<br>\nCaminhos para solicitações OAuth2 API: <br>\n<ul>\n<li>Endpoint de autorização: <code>/oauth/authorize</code></li>\n<li>Endpoint token: <code>/oauth/token</code></li>\n<li>Página de recursos: <code>/oauth/profile</code></li>\n</ul>\nRegenerar o segredo do cliente não expirará os códigos de autorização existentes, mas eles não renovarão seu token.<br><br>\nA revogação dos tokens do cliente causará o término imediato de todas as sessões ativas. Todos os clientes precisam se autenticar novamente.",
|
||||
"oauth2_redirect_uri": "URI de redirecionamento",
|
||||
"oauth2_renew_secret": "Gere um novo segredo de cliente",
|
||||
"oauth2_revoke_tokens": "Revogar todos os tokens do cliente",
|
||||
@@ -321,8 +320,8 @@
|
||||
"rspamd_com_settings": "Um nome de configuração será gerado automaticamente, veja os exemplos de predefinições abaixo. Para obter mais detalhes, consulte a documentação <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">do Rspamd</a>",
|
||||
"rspamd_global_filters": "Mapas de filtro globais",
|
||||
"rspamd_global_filters_agree": "Eu vou ter cuidado!",
|
||||
"rspamd_global_filters_info": "Os mapas de filtros globais contêm diferentes tipos de listas globais de bloqueio e de permissões.",
|
||||
"rspamd_global_filters_regex": "Os nomes explicam sua finalidade. Todo o conteúdo deve conter uma expressão regular válida no formato \"/padrão/opções\" (por exemplo, <code>/.[+@domain.tld](mailto:+@domain.tld)/i</code>).<br>\nEmbora verificações básicas sejam executadas em cada linha de regex, a funcionalidade do Rspamd pode ser comprometida se ele não conseguir ler a sintaxe corretamente.<br>\nO Rspamd tentará ler o conteúdo do mapa quando ele for alterado. Se você tiver problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie o Rspamd</a> para forçar o recarregamento do mapa.<br>Elementos na lista de bloqueio são excluídos da quarentena.",
|
||||
"rspamd_global_filters_info": "Os mapas de filtros globais contêm diferentes tipos de listas negras e brancas globais.",
|
||||
"rspamd_global_filters_regex": "Seus nomes explicam seu propósito. <code>Todo o conteúdo deve conter uma expressão regular válida no formato “/padrão/opções” (por exemplo, /. + @domain\\ .tld/i</code>). <br>\r\n Embora verificações rudimentares estejam sendo executadas em cada linha de regex, a funcionalidade do Rspamd pode ser interrompida se não conseguir ler a sintaxe corretamente. <br>\r\n O Rspamd tentará ler o conteúdo do mapa quando alterado. Se você tiver problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie o Rspamd</a> para forçar o recarregamento do mapa. <br>Os elementos da lista negra são excluídos da quarentena.",
|
||||
"rspamd_settings_map": "Mapa de configurações do Rspamd",
|
||||
"sal_level": "Nível de humor",
|
||||
"save": "Salvar alterações",
|
||||
@@ -557,9 +556,7 @@
|
||||
"mode_invalid": "Modo %s é inválido",
|
||||
"mx_invalid": "Registro MX %s é inválido",
|
||||
"required_data_missing": "Dados obrigatórios %s estão ausentes",
|
||||
"version_invalid": "Versão %s é inválida",
|
||||
"tfa_removal_blocked": "A autenticação de dois fatores não pode ser removida, pois é obrigatória para a sua conta.",
|
||||
"quarantine_category_invalid": "A categoria da quarentena deve ser uma das seguintes: add_header, reject, all"
|
||||
"version_invalid": "Versão %s é inválida"
|
||||
},
|
||||
"datatables": {
|
||||
"collapse_all": "Recolher tudo",
|
||||
@@ -730,7 +727,7 @@
|
||||
"pushover_verify": "Verifique as credenciais",
|
||||
"quota_mb": "Cota (MiB)",
|
||||
"quota_warning_bcc": "Aviso de cota BCC",
|
||||
"quota_warning_bcc_info": "Os avisos serão enviados em cópias separadas para os seguintes destinatários. O assunto será precedido pelo nome de usuário correspondente entre colchetes, por exemplo: <code>Aviso de cota (user@example.com)</code>.",
|
||||
"quota_warning_bcc_info": "Os avisos serão enviados em cópias separadas para os seguintes destinatários. O assunto será sufixado pelo nome de usuário correspondente entre colchetes, por exemplo: <code>Aviso de cota (</code>user@example.com).",
|
||||
"ratelimit": "Limite de taxa",
|
||||
"redirect_uri": "URL de redirecionamento/retorno de chamada",
|
||||
"relay_all": "Retransmita todos os destinatários",
|
||||
@@ -749,17 +746,17 @@
|
||||
"sieve_desc": "Breve descrição",
|
||||
"sieve_type": "Tipo de filtro",
|
||||
"skipcrossduplicates": "Ignore mensagens duplicadas entre pastas (primeiro a chegar, primeiro a ser servido)",
|
||||
"sogo_access": "Encaminhamento direto para o SOGo",
|
||||
"sogo_access_info": "Após o login, o usuário é automaticamente redirecionado para o SOGo.",
|
||||
"sogo_access": "Encaminhamento direto para o SOGoo",
|
||||
"sogo_access_info": "Depois de fazer login, o usuário é automaticamente redirecionado para o SOGo.",
|
||||
"sogo_visible": "O alias é visível no SoGo",
|
||||
"sogo_visible_info": "Essa opção afeta somente objetos, que podem ser exibidos no SoGo (endereços de alias compartilhados ou não compartilhados apontando para pelo menos uma mailbox local). Se estiver oculto, um alias não aparecerá como remetente selecionável no SoGo.",
|
||||
"spam_alias": "Crie ou altere endereços de alias com limite de tempo",
|
||||
"spam_filter": "Filtro de spam",
|
||||
"spam_policy": "Adicionar ou remover itens da lista de permissões/bloqueio",
|
||||
"spam_policy": "Adicionar ou remover itens da lista branca/negra",
|
||||
"spam_score": "Defina uma pontuação de spam personalizada",
|
||||
"subfolder2": "Sincronizar na subpasta no destino <br><small>(vazio = não usar subpasta</small>)",
|
||||
"syncjob": "Editar tarefa de sincronização",
|
||||
"target_address": "Ir para o(s) endereço(s) <small>(separados por vírgula)</small>",
|
||||
"target_address": "<small>Ir para endereço/es (separados por vírgula)</small>",
|
||||
"target_domain": "Domínio de destino",
|
||||
"timeout1": "Tempo limite para conexão com o host remoto",
|
||||
"timeout2": "Tempo limite para conexão com o host local",
|
||||
@@ -784,10 +781,7 @@
|
||||
"mta_sts_max_age_info": "Tempo em segundos que servidores de email de recepção podem armazenar esta política em cache até buscar novamente.",
|
||||
"mta_sts_mx": "Servidor MX",
|
||||
"mta_sts_mx_info": "Permite envio apenas para nomes de host de servidor de email explicitamente listados; o MTA de envio verifica se o nome do host DNS MX corresponde à lista de políticas e permite entrega apenas com certificado TLS válido (protege contra MITM).",
|
||||
"mta_sts_mx_notice": "Múltiplos servidores MX podem ser especificados (separados por vírgulas).",
|
||||
"sender_allowed": "Permitir enviar como este alias",
|
||||
"sender_allowed_info": "Se desativado, este alias poderá apenas receber e-mails. Use a ACL de remetente para substituir essa configuração e conceder a caixas de correio específicas permissão para enviar.",
|
||||
"mta_sts_active_info": "Se esta opção estiver desmarcada, a política MTA-STS não será publicada e nenhum certificado será solicitado via ACME para o subdomínio mta-sts."
|
||||
"mta_sts_mx_notice": "Múltiplos servidores MX podem ser especificados (separados por vírgulas)."
|
||||
},
|
||||
"fido2": {
|
||||
"confirm": "Confirme",
|
||||
@@ -808,7 +802,7 @@
|
||||
"cancel": "Cancelar",
|
||||
"confirm_delete": "Confirme a exclusão",
|
||||
"delete_now": "Excluir agora",
|
||||
"delete_these_items": "Por favor, confirme as alterações feitas no seguinte ID de objeto.",
|
||||
"delete_these_items": "Confirme suas alterações no seguinte ID de objeto",
|
||||
"hibp_check": "Verifique em haveibeenpwned.com",
|
||||
"hibp_nok": "Combinado! Essa é uma senha potencialmente perigosa!",
|
||||
"hibp_ok": "Nenhuma combinação encontrada.",
|
||||
@@ -976,7 +970,7 @@
|
||||
"recipient_map_new": "Novo destinatário",
|
||||
"recipient_map_new_info": "O destino do mapa do destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
|
||||
"recipient_map_old": "Destinatário original",
|
||||
"recipient_map_old_info": "O destino original do mapa de destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
|
||||
"recipient_map_old_info": "O destino original do mapa de um destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
|
||||
"recipient_maps": "Mapas de destinatários",
|
||||
"relay_all": "Retransmita todos os destinatários",
|
||||
"relay_unknown": "Retransmitir mailboxes desconhecidas",
|
||||
@@ -1035,8 +1029,7 @@
|
||||
"weekly": "Semanalmente",
|
||||
"yes": "✓",
|
||||
"iam": "Provedor de Identidade",
|
||||
"internal": "Interno",
|
||||
"force_tfa": "A2F"
|
||||
"internal": "Interno"
|
||||
},
|
||||
"oauth2": {
|
||||
"access_denied": "Faça login como proprietário da mailbox para conceder acesso via OAuth2.",
|
||||
@@ -1068,7 +1061,7 @@
|
||||
"notified": "Notificado",
|
||||
"qhandler_success": "Solicitação enviada com sucesso para o sistema. Agora você pode fechar a janela.",
|
||||
"qid": "Respand AID",
|
||||
"qinfo": "O sistema de quarentena irá salvar no banco de dados os e-mails rejeitados (o remetente <em>não</em> terá a impressão de que o e-mail foi entregue), bem como os e-mails que são entregues como cópia na pasta de spam de uma mailbox.\n<br>“Aprender como spam e excluir” irá classificar a mensagem como spam por meio do Teorema de Bayes e também calcular hashes fuzzy para bloquear mensagens semelhantes no futuro.\n<br>Esteja ciente de que o aprendizado de múltiplas mensagens pode, dependendo do seu sistema, levar tempo. <br>Elementos na lista de bloqueio são excluídos da quarentena.",
|
||||
"qinfo": "O sistema de quarentena salvará as mensagens rejeitadas no banco de dados (o remetente <em>não</em> terá a impressão de uma mensagem entregue), bem como as mensagens, que são entregues como cópia na pasta Lixo eletrônico de uma mailbox.\n <br>“Aprenda como spam e exclua” aprenderá uma mensagem como spam por meio do Teorema de Bayes e também calculará hashes difusos para negar mensagens semelhantes no futuro.\n <br>Esteja ciente de que aprender várias mensagens pode ser demorado, dependendo do seu sistema. <br>Os elementos da lista negra são excluídos da quarentena.",
|
||||
"qitem": "Item de quarentena",
|
||||
"quarantine": "Quarentena",
|
||||
"quick_actions": "Ações",
|
||||
@@ -1088,7 +1081,7 @@
|
||||
"rspamd_result": "Resultado do Rspamd",
|
||||
"sender": "Remetente (SMTP)",
|
||||
"sender_header": "Remetente (cabeçalho “De”)",
|
||||
"settings_info": "Número máximo de elementos em quarentena (por caixa de correio): %s <br>Tamanho máximo do e-mail: %s MiB",
|
||||
"settings_info": "Quantidade máxima de elementos a serem colocados em quarentena: %s <br>Tamanho máximo do e-mail: %s MiB",
|
||||
"show_item": "Mostrar item",
|
||||
"spam": "Spam",
|
||||
"spam_score": "Ponto",
|
||||
@@ -1244,12 +1237,7 @@
|
||||
"webauthn": "Autenticação WebAuthn",
|
||||
"waiting_usb_auth": "<i>Aguardando o dispositivo USB...</i> <br><br>Toque no botão no seu dispositivo USB agora.",
|
||||
"waiting_usb_register": "<i>Aguardando o dispositivo USB...</i> <br><br>Digite sua senha acima e confirme seu registro tocando no botão no seu dispositivo USB.",
|
||||
"yubi_otp": "Autenticação Yubico OTP",
|
||||
"force_tfa": "Forçar o cadastro de 2FA no login",
|
||||
"force_tfa_info": "O usuário será obrigado a configurar a autenticação de dois fatores antes de acessar o painel.",
|
||||
"setup_title": "Autenticação de Dois Fatores Obrigatória",
|
||||
"setup_required": "Sua conta exige autenticação de dois fatores. Configure um método de 2FA para continuar.",
|
||||
"cancel_setup": "Cancelar e sair"
|
||||
"yubi_otp": "Autenticação Yubico OTP"
|
||||
},
|
||||
"user": {
|
||||
"action": "Ação",
|
||||
@@ -1288,7 +1276,7 @@
|
||||
"delete_ays": "Confirme o processo de exclusão.",
|
||||
"direct_aliases": "Endereços de alias diretos",
|
||||
"direct_aliases_desc": "Os endereços de alias diretos são afetados pelo filtro de spam e pelas configurações da política TLS.",
|
||||
"direct_protocol_access": "Este usuário da mailbox possui <b>acesso direto e externo</b> aos seguintes protocolos e aplicações. Esta configuração é controlada pelo seu administrador. Senhas de aplicativo podem ser criadas para conceder acesso a protocolos e aplicações específicos.<br>O botão “Webmail” fornece login único (SSO) para o SOGo e está sempre disponível.",
|
||||
"direct_protocol_access": "Esse usuário da mailbox tem <b>acesso externo direto</b> aos seguintes protocolos e aplicativos. Essa configuração é controlada pelo administrador. As senhas de aplicativos podem ser criadas para conceder acesso a protocolos e aplicativos individuais. <br>O botão “Login no webmail” fornece login único no SoGo e está sempre disponível.",
|
||||
"eas_reset": "Redefinir o cache do dispositivo ActiveSync",
|
||||
"eas_reset_help": "Em muitos casos, uma redefinição do cache do dispositivo ajudará a recuperar um perfil quebrado do ActiveSync. <br><b>Atenção:</b> Todos os elementos serão baixados novamente!",
|
||||
"eas_reset_now": "Reinicie agora",
|
||||
@@ -1364,12 +1352,12 @@
|
||||
"sogo_profile_reset": "Redefinir perfil SoGo",
|
||||
"sogo_profile_reset_help": "Isso destruirá o perfil SoGo de um usuário e <b>excluirá todos os dados de contato e calendário irrecuperáveis</b>.",
|
||||
"sogo_profile_reset_now": "Redefina o perfil agora",
|
||||
"spam_aliases": "Aliases de e-mail de spam",
|
||||
"spam_aliases": "Aliases de e-mail temporários",
|
||||
"spam_score_reset": "Redefinir para o padrão do servidor",
|
||||
"spamfilter": "Filtro de spam",
|
||||
"spamfilter_behavior": "Avaliação",
|
||||
"spamfilter_bl": "Lista de bloqueio",
|
||||
"spamfilter_bl_desc": "Endereços de e-mail na lista de bloqueio para <b>sempre</b> classificar como spam e rejeitados. E-mails rejeitados <b>não</b> serão copiados para a quarentena. Coringas podem ser utilizados. O filtro é aplicado apenas a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases catch-all e a própria mailbox.",
|
||||
"spamfilter_bl": "Lista negra",
|
||||
"spamfilter_bl_desc": "Endereços de e-mail na lista negra para <b>sempre</b> serem classificados como spam e rejeitados. E-mails rejeitados <b>não</b> serão copiados para a quarentena. Podem ser usados curingas. Um filtro só é aplicado a aliases diretos (aliases com uma única caixa de correio de destino), excluindo aliases abrangentes e a própria mailbox.",
|
||||
"spamfilter_default_score": "Valores padrão",
|
||||
"spamfilter_green": "Verde: esta mensagem não é spam",
|
||||
"spamfilter_hint": "O primeiro valor descreve a “pontuação baixa de spam”, o segundo representa a “alta pontuação de spam”.",
|
||||
@@ -1380,8 +1368,8 @@
|
||||
"spamfilter_table_empty": "Não há dados para exibir",
|
||||
"spamfilter_table_remove": "remover",
|
||||
"spamfilter_table_rule": "Regra",
|
||||
"spamfilter_wl": "Lista de permissões",
|
||||
"spamfilter_wl_desc": "Endereços de e-mail na lista de permissões são configurados para <b>nunca</b> serem classificados como spam. Coringas podem ser utilizados. O filtro é aplicado apenas a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases catch-all e a própria mailbox.",
|
||||
"spamfilter_wl": "Lista branca",
|
||||
"spamfilter_wl_desc": "Os endereços de e-mail incluídos na lista branca são programados para <b>nunca</b> serem classificados como spam. Podem ser usados curingas. Um filtro só é aplicado a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases abrangentes e a própria mailbox.",
|
||||
"spamfilter_yellow": "Amarelo: esta mensagem pode ser spam, será marcada como spam e movida para sua pasta de lixo eletrônico",
|
||||
"status": "Status",
|
||||
"sync_jobs": "Trabalhos de sincronização",
|
||||
@@ -1395,7 +1383,7 @@
|
||||
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Não é possível se conectar ao servidor remoto",
|
||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Nome de usuário ou senha incorretos",
|
||||
"tag_handling": "Definir o tratamento para e-mails marcados",
|
||||
"tag_help_example": "Exemplo de endereço de e-mail com tag: eu+Facebook</b>@exemplo.org",
|
||||
"tag_help_example": "Exemplo de um endereço de e-mail marcado: me <b>+Facebook</b> @example .org",
|
||||
"tag_help_explain": "Na subpasta: uma nova subpasta com o nome da tag será criada abaixo da CAIXA DE ENTRADA (“Caixa de entrada/Facebook”). <br>\r\nNo assunto: o nome das tags será anexado ao assunto do e-mail, por exemplo: “[Facebook] Minhas notícias”.",
|
||||
"tag_in_none": "Não faça nada",
|
||||
"tag_in_subfolder": "Na subpasta",
|
||||
@@ -1420,11 +1408,7 @@
|
||||
"authentication": "Autenticação",
|
||||
"overview": "Visão geral",
|
||||
"protocols": "Protocolos",
|
||||
"tfa_info": "A autenticação de dois fatores ajuda a proteger sua conta. Se você habilitá-la, precisará de senhas de aplicativo para fazer login em aplicativos ou serviços que não suportam autenticação de dois fatores (por exemplo, clientes de email).",
|
||||
"expire_never": "Nunca expirar",
|
||||
"forever": "Para sempre",
|
||||
"pw_update_required": "Sua conta exige a alteração de senha. Defina uma nova senha para continuar.",
|
||||
"spam_aliases_info": "Um alias de spam é um endereço de e-mail temporário que pode ser usado para proteger endereços de e-mail reais. <br>Opcionalmente, pode-se definir um tempo de expiração para que o alias seja automaticamente desativado após o período definido, descartando efetivamente endereços que tenham sido abusados ou vazados."
|
||||
"tfa_info": "A autenticação de dois fatores ajuda a proteger sua conta. Se você habilitá-la, precisará de senhas de aplicativo para fazer login em aplicativos ou serviços que não suportam autenticação de dois fatores (por exemplo, clientes de email)."
|
||||
},
|
||||
"warning": {
|
||||
"cannot_delete_self": "Não é possível excluir o usuário conectado",
|
||||
|
||||
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user