Compare commits

..

No commits in common. "b5a1a18b041cea4b725ea2850721ac9581416971" and "fd206a7ef633ad995790f4a84aa01fa190905eda" have entirely different histories.

20 changed files with 137 additions and 195 deletions

View File

@ -1,8 +1,7 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG PIP_BREAK_SYSTEM_PACKAGES=1
RUN apk upgrade --no-cache \ RUN apk upgrade --no-cache \
&& apk add --update --no-cache \ && apk add --update --no-cache \
bash \ bash \

View File

@ -1,8 +1,7 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG PIP_BREAK_SYSTEM_PACKAGES=1
WORKDIR /app WORKDIR /app
RUN apk add --update --no-cache python3 \ RUN apk add --update --no-cache python3 \
@ -10,13 +9,12 @@ RUN apk add --update --no-cache python3 \
openssl \ openssl \
tzdata \ tzdata \
py3-psutil \ py3-psutil \
py3-redis \
py3-async-timeout \
&& pip3 install --upgrade pip \ && pip3 install --upgrade pip \
fastapi \ fastapi \
uvicorn \ uvicorn \
aiodocker \ aiodocker \
docker docker \
aioredis
RUN mkdir /app/modules RUN mkdir /app/modules
COPY docker-entrypoint.sh /app/ COPY docker-entrypoint.sh /app/

View File

@ -5,63 +5,16 @@ import json
import uuid import uuid
import async_timeout import async_timeout
import asyncio import asyncio
import aioredis
import aiodocker import aiodocker
import docker import docker
import logging import logging
from logging.config import dictConfig from logging.config import dictConfig
from fastapi import FastAPI, Response, Request from fastapi import FastAPI, Response, Request
from modules.DockerApi import DockerApi from modules.DockerApi import DockerApi
from redis import asyncio as aioredis
from contextlib import asynccontextmanager
dockerapi = None dockerapi = None
app = FastAPI()
@asynccontextmanager
async def lifespan(app: FastAPI):
global dockerapi
# Initialize a custom logger
logger = logging.getLogger("dockerapi")
logger.setLevel(logging.INFO)
# Configure the logger to output logs to the terminal
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
formatter = logging.Formatter("%(levelname)s: %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("Init APP")
# Init redis client
if os.environ['REDIS_SLAVEOF_IP'] != "":
redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0")
else:
redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0")
# Init docker clients
sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
dockerapi = DockerApi(redis_client, sync_docker_client, async_docker_client, logger)
logger.info("Subscribe to redis channel")
# Subscribe to redis channel
dockerapi.pubsub = redis.pubsub()
await dockerapi.pubsub.subscribe("MC_CHANNEL")
asyncio.create_task(handle_pubsub_messages(dockerapi.pubsub))
yield
# Close docker connections
dockerapi.sync_docker_client.close()
await dockerapi.async_docker_client.close()
# Close redis
await dockerapi.pubsub.unsubscribe("MC_CHANNEL")
await dockerapi.redis_client.close()
app = FastAPI(lifespan=lifespan)
# Define Routes # Define Routes
@app.get("/host/stats") @app.get("/host/stats")
@ -191,7 +144,53 @@ async def post_container_update_stats(container_id : str):
stats = json.loads(await dockerapi.redis_client.get(container_id + '_stats')) stats = json.loads(await dockerapi.redis_client.get(container_id + '_stats'))
return Response(content=json.dumps(stats, indent=4), media_type="application/json") return Response(content=json.dumps(stats, indent=4), media_type="application/json")
# Events
@app.on_event("startup")
async def startup_event():
global dockerapi
# Initialize a custom logger
logger = logging.getLogger("dockerapi")
logger.setLevel(logging.INFO)
# Configure the logger to output logs to the terminal
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
formatter = logging.Formatter("%(levelname)s: %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("Init APP")
# Init redis client
if os.environ['REDIS_SLAVEOF_IP'] != "":
redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0")
else:
redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0")
# Init docker clients
sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
dockerapi = DockerApi(redis_client, sync_docker_client, async_docker_client, logger)
logger.info("Subscribe to redis channel")
# Subscribe to redis channel
dockerapi.pubsub = redis.pubsub()
await dockerapi.pubsub.subscribe("MC_CHANNEL")
asyncio.create_task(handle_pubsub_messages(dockerapi.pubsub))
@app.on_event("shutdown")
async def shutdown_event():
global dockerapi
# Close docker connections
dockerapi.sync_docker_client.close()
await dockerapi.async_docker_client.close()
# Close redis
await dockerapi.pubsub.unsubscribe("MC_CHANNEL")
await dockerapi.redis_client.close()
# PubSub Handler # PubSub Handler
async def handle_pubsub_messages(channel: aioredis.client.PubSub): async def handle_pubsub_messages(channel: aioredis.client.PubSub):

View File

@ -5,7 +5,7 @@ ARG DEBIAN_FRONTEND=noninteractive
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=(?<version>.*)$ # renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=(?<version>.*)$
ARG DOVECOT=2.3.21 ARG DOVECOT=2.3.21
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$ # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
ARG GOSU_VERSION=1.17 ARG GOSU_VERSION=1.16
ENV LC_ALL C ENV LC_ALL C

View File

@ -1,9 +1,8 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
WORKDIR /app WORKDIR /app
ARG PIP_BREAK_SYSTEM_PACKAGES=1
ENV XTABLES_LIBDIR /usr/lib/xtables ENV XTABLES_LIBDIR /usr/lib/xtables
ENV PYTHON_IPTABLES_XTABLES_VERSION 12 ENV PYTHON_IPTABLES_XTABLES_VERSION 12
ENV IPTABLES_LIBDIR /usr/lib ENV IPTABLES_LIBDIR /usr/lib
@ -15,7 +14,6 @@ RUN apk add --virtual .build-deps \
openssl-dev \ openssl-dev \
&& apk add -U python3 \ && apk add -U python3 \
iptables \ iptables \
iptables-dev \
ip6tables \ ip6tables \
xtables-addons \ xtables-addons \
nftables \ nftables \

View File

@ -1,7 +1,6 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG PIP_BREAK_SYSTEM_PACKAGES=1
WORKDIR /app WORKDIR /app
#RUN addgroup -S olefy && adduser -S olefy -G olefy \ #RUN addgroup -S olefy && adduser -S olefy -G olefy \

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -1,5 +1,5 @@
FROM alpine:3.19 FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "André Peters <andre.peters@servercow.de>"
# Installation # Installation
RUN apk add --update \ RUN apk add --update \

View File

@ -49,14 +49,13 @@ $from = $headers['From'];
$empty_footer = json_encode(array( $empty_footer = json_encode(array(
'html' => '', 'html' => '',
'plain' => '', 'plain' => '',
'skip_replies' => 0,
'vars' => array() 'vars' => array()
)); ));
error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL); error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL);
try { try {
$stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude`, `skip_replies` FROM `domain_wide_footer` $stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude` FROM `domain_wide_footer`
WHERE `domain` = :domain"); WHERE `domain` = :domain");
$stmt->execute(array( $stmt->execute(array(
':domain' => $domain ':domain' => $domain

View File

@ -567,14 +567,6 @@ rspamd_config:register_symbol({
if footer and type(footer) == "table" and (footer.html and footer.html ~= "" or footer.plain and footer.plain ~= "") then if footer and type(footer) == "table" and (footer.html and footer.html ~= "" or footer.plain and footer.plain ~= "") then
rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s, vars=%s", uname, footer.html, footer.plain, footer.vars) rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s, vars=%s", uname, footer.html, footer.plain, footer.vars)
if footer.skip_replies then
in_reply_to = task:get_header_raw('in-reply-to')
if in_reply_to then
rspamd_logger.infox(rspamd_config, "mail is a reply - skip footer")
return
end
end
local envfrom_mime = task:get_from(2) local envfrom_mime = task:get_from(2)
local from_name = "" local from_name = ""
if envfrom_mime and envfrom_mime[1].name then if envfrom_mime and envfrom_mime[1].name then

View File

@ -477,25 +477,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
}
$DOMAIN_DEFAULT_ATTRIBUTES = null;
if ($_data['template']){
$DOMAIN_DEFAULT_ATTRIBUTES = mailbox('get', 'domain_templates', $_data['template'])['attributes'];
} }
if (empty($DOMAIN_DEFAULT_ATTRIBUTES)){
$DOMAIN_DEFAULT_ATTRIBUTES = mailbox('get', 'domain_templates')[0]['attributes'];
}
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
$description = $_data['description']; $description = $_data['description'];
if (empty($description)) $description = $domain; if (empty($description)) $description = $domain;
$tags = (isset($_data['tags'])) ? (array)$_data['tags'] : $DOMAIN_DEFAULT_ATTRIBUTES['tags']; $tags = (array)$_data['tags'];
$aliases = (isset($_data['aliases'])) ? (int)$_data['aliases'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_num_aliases_for_domain']; $aliases = (int)$_data['aliases'];
$mailboxes = (isset($_data['mailboxes'])) ? (int)$_data['mailboxes'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_num_mboxes_for_domain']; $mailboxes = (int)$_data['mailboxes'];
$defquota = (isset($_data['defquota'])) ? (int)$_data['defquota'] : $DOMAIN_DEFAULT_ATTRIBUTES['def_quota_for_mbox'] / 1024 ** 2; $defquota = (int)$_data['defquota'];
$maxquota = (isset($_data['maxquota'])) ? (int)$_data['maxquota'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_quota_for_mbox'] / 1024 ** 2; $maxquota = (int)$_data['maxquota'];
$restart_sogo = (int)$_data['restart_sogo']; $restart_sogo = (int)$_data['restart_sogo'];
$quota = (isset($_data['quota'])) ? (int)$_data['quota'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_quota_for_domain'] / 1024 ** 2; $quota = (int)$_data['quota'];
if ($defquota > $maxquota) { if ($defquota > $maxquota) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -528,11 +520,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
$active = (isset($_data['active'])) ? intval($_data['active']) : $DOMAIN_DEFAULT_ATTRIBUTES['active']; $active = intval($_data['active']);
$relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_all_recipients']; $relay_all_recipients = intval($_data['relay_all_recipients']);
$relay_unknown_only = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_unknown_only']; $relay_unknown_only = intval($_data['relay_unknown_only']);
$backupmx = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $DOMAIN_DEFAULT_ATTRIBUTES['backupmx']; $backupmx = intval($_data['backupmx']);
$gal = (isset($_data['gal'])) ? intval($_data['gal']) : $DOMAIN_DEFAULT_ATTRIBUTES['gal']; $gal = intval($_data['gal']);
if ($relay_all_recipients == 1) { if ($relay_all_recipients == 1) {
$backupmx = '1'; $backupmx = '1';
} }
@ -633,13 +625,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
$_data['rl_value'] = (isset($_data['rl_value'])) ? intval($_data['rl_value']) : $DOMAIN_DEFAULT_ATTRIBUTES['rl_value']; if (!empty(intval($_data['rl_value']))) {
$_data['rl_frame'] = (isset($_data['rl_frame'])) ? intval($_data['rl_frame']) : $DOMAIN_DEFAULT_ATTRIBUTES['rl_frame'];
if (!empty($_data['rl_value']) && !empty($_data['rl_frame'])){
ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $domain)); ratelimit('edit', 'domain', array('rl_value' => $_data['rl_value'], 'rl_frame' => $_data['rl_frame'], 'object' => $domain));
} }
$_data['key_size'] = (isset($_data['key_size'])) ? intval($_data['key_size']) : $DOMAIN_DEFAULT_ATTRIBUTES['key_size'];
$_data['dkim_selector'] = (isset($_data['dkim_selector'])) ? intval($_data['dkim_selector']) : $DOMAIN_DEFAULT_ATTRIBUTES['dkim_selector'];
if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) { if (!empty($_data['key_size']) && !empty($_data['dkim_selector'])) {
if (!empty($redis->hGet('DKIM_SELECTORS', $domain))) { if (!empty($redis->hGet('DKIM_SELECTORS', $domain))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@ -1018,21 +1006,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
if (empty($name)) {
$name = $local_part;
}
$MAILBOX_DEFAULT_ATTRIBUTES = null;
if ($_data['template']){
$MAILBOX_DEFAULT_ATTRIBUTES = mailbox('get', 'mailbox_templates', $_data['template'])['attributes'];
}
if (empty($MAILBOX_DEFAULT_ATTRIBUTES)){
$MAILBOX_DEFAULT_ATTRIBUTES = mailbox('get', 'mailbox_templates')[0]['attributes'];
}
$password = $_data['password']; $password = $_data['password'];
$password2 = $_data['password2']; $password2 = $_data['password2'];
$name = ltrim(rtrim($_data['name'], '>'), '<'); $name = ltrim(rtrim($_data['name'], '>'), '<');
$tags = (isset($_data['tags'])) ? $_data['tags'] : $MAILBOX_DEFAULT_ATTRIBUTES['tags']; $tags = $_data['tags'];
$quota_m = (isset($_data['quota'])) ? intval($_data['quota']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['quota']) / 1024 ** 2; $quota_m = intval($_data['quota']);
if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && $quota_m === 0) { if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && $quota_m === 0) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
@ -1041,7 +1019,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
return false; return false;
} }
if (empty($name)) {
$name = $local_part;
}
if (isset($_data['protocol_access'])) { if (isset($_data['protocol_access'])) {
$_data['protocol_access'] = (array)$_data['protocol_access']; $_data['protocol_access'] = (array)$_data['protocol_access'];
$_data['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0; $_data['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0;
@ -1049,7 +1029,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0; $_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0; $_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
} }
$active = (isset($_data['active'])) ? intval($_data['active']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['active']); $active = intval($_data['active']);
$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']); $force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
$tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']); $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']);
$tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']); $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']);
@ -1247,29 +1227,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0; $_data['quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
$_data['quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0; $_data['quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
$_data['app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0; $_data['app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
} else {
$_data['spam_alias'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_spam_alias']);
$_data['tls_policy'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_tls_policy']);
$_data['spam_score'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_spam_score']);
$_data['spam_policy'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_spam_policy']);
$_data['delimiter_action'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_delimiter_action']);
$_data['syncjobs'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_syncjobs']);
$_data['eas_reset'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_eas_reset']);
$_data['sogo_profile_reset'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_sogo_profile_reset']);
$_data['pushover'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_pushover']);
$_data['quarantine'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine']);
$_data['quarantine_attachments'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_attachments']);
$_data['quarantine_notification'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_notification']);
$_data['quarantine_category'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_category']);
$_data['app_passwds'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_app_passwds']);
}
try {
$stmt = $pdo->prepare("INSERT INTO `user_acl` $stmt = $pdo->prepare("INSERT INTO `user_acl`
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`, (`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`) `pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`)
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset, VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) "); :pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
':spam_alias' => $_data['spam_alias'], ':spam_alias' => $_data['spam_alias'],
@ -1288,17 +1251,31 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':app_passwds' => $_data['app_passwds'] ':app_passwds' => $_data['app_passwds']
)); ));
} }
catch (PDOException $e) { else {
$_SESSION['return'][] = array( $stmt = $pdo->prepare("INSERT INTO `user_acl`
'type' => 'danger', (`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), `pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`)
'msg' => $e->getMessage() VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
); :pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
return false; $stmt->execute(array(
':username' => $username,
':spam_alias' => 0,
':tls_policy' => 0,
':spam_score' => 0,
':spam_policy' => 0,
':delimiter_action' => 0,
':syncjobs' => 0,
':eas_reset' => 0,
':sogo_profile_reset' => 0,
':pushover' => 0,
':quarantine' => 0,
':quarantine_attachments' => 0,
':quarantine_notification' => 0,
':quarantine_category' => 0,
':app_passwds' => 0
));
} }
$_data['rl_frame'] = (isset($_data['rl_frame'])) ? $_data['rl_frame'] : $MAILBOX_DEFAULT_ATTRIBUTES['rl_frame'];
$_data['rl_value'] = (isset($_data['rl_value'])) ? $_data['rl_value'] : $MAILBOX_DEFAULT_ATTRIBUTES['rl_value'];
if (isset($_data['rl_frame']) && isset($_data['rl_value'])){ if (isset($_data['rl_frame']) && isset($_data['rl_value'])){
ratelimit('edit', 'mailbox', array( ratelimit('edit', 'mailbox', array(
'object' => $username, 'object' => $username,
@ -1547,17 +1524,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr["tls_enforce_out"] = isset($_data['tls_enforce_out']) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']); $attr["tls_enforce_out"] = isset($_data['tls_enforce_out']) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']);
if (isset($_data['protocol_access'])) { if (isset($_data['protocol_access'])) {
$_data['protocol_access'] = (array)$_data['protocol_access']; $_data['protocol_access'] = (array)$_data['protocol_access'];
$attr['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0; $attr['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0; $attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0; $attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0; $attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
} }
else { else {
$attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']); $attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
$attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']); $attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']); $attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']); $attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
} }
if (isset($_data['acl'])) { if (isset($_data['acl'])) {
$_data['acl'] = (array)$_data['acl']; $_data['acl'] = (array)$_data['acl'];
$attr['acl_spam_alias'] = (in_array('spam_alias', $_data['acl'])) ? 1 : 0; $attr['acl_spam_alias'] = (in_array('spam_alias', $_data['acl'])) ? 1 : 0;
@ -3434,7 +3411,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$footers = array(); $footers = array();
$footers['html'] = isset($_data['html']) ? $_data['html'] : ''; $footers['html'] = isset($_data['html']) ? $_data['html'] : '';
$footers['plain'] = isset($_data['plain']) ? $_data['plain'] : ''; $footers['plain'] = isset($_data['plain']) ? $_data['plain'] : '';
$footers['skip_replies'] = isset($_data['skip_replies']) ? (int)$_data['skip_replies'] : 0;
$footers['mbox_exclude'] = array(); $footers['mbox_exclude'] = array();
if (isset($_data["mbox_exclude"])){ if (isset($_data["mbox_exclude"])){
if (!is_array($_data["mbox_exclude"])) { if (!is_array($_data["mbox_exclude"])) {
@ -3484,13 +3460,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
try { try {
$stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain"); $stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain");
$stmt->execute(array(':domain' => $domain)); $stmt->execute(array(':domain' => $domain));
$stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`, `skip_replies`) VALUES (:domain, :html, :plain, :mbox_exclude, :skip_replies)"); $stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`) VALUES (:domain, :html, :plain, :mbox_exclude)");
$stmt->execute(array( $stmt->execute(array(
':domain' => $domain, ':domain' => $domain,
':html' => $footers['html'], ':html' => $footers['html'],
':plain' => $footers['plain'], ':plain' => $footers['plain'],
':mbox_exclude' => json_encode($footers['mbox_exclude']), ':mbox_exclude' => json_encode($footers['mbox_exclude']),
':skip_replies' => $footers['skip_replies'],
)); ));
} }
catch (PDOException $e) { catch (PDOException $e) {
@ -4647,7 +4622,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
try { try {
$stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude`, `skip_replies` FROM `domain_wide_footer` $stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude` FROM `domain_wide_footer`
WHERE `domain` = :domain"); WHERE `domain` = :domain");
$stmt->execute(array( $stmt->execute(array(
':domain' => $domain ':domain' => $domain

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try { try {
global $pdo; global $pdo;
$db_version = "08012024_1442"; $db_version = "21112023_1644";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -273,7 +273,6 @@ function init_db_schema() {
"html" => "LONGTEXT", "html" => "LONGTEXT",
"plain" => "LONGTEXT", "plain" => "LONGTEXT",
"mbox_exclude" => "JSON NOT NULL DEFAULT ('[]')", "mbox_exclude" => "JSON NOT NULL DEFAULT ('[]')",
"skip_replies" => "TINYINT(1) NOT NULL DEFAULT '0'"
), ),
"keys" => array( "keys" => array(
"primary" => array( "primary" => array(

View File

@ -588,19 +588,10 @@
"disable_login": "Login verbieten (Mails werden weiterhin angenommen)", "disable_login": "Login verbieten (Mails werden weiterhin angenommen)",
"domain": "Domain bearbeiten", "domain": "Domain bearbeiten",
"domain_admin": "Domain-Administrator bearbeiten", "domain_admin": "Domain-Administrator bearbeiten",
"domain_footer": "Domänenweite Fußzeile", "domain_footer": "Domain wide footer",
"domain_footer_html": "Fußzeile im HTML Format", "domain_footer_html": "HTML footer",
"domain_footer_info": "Domänenweite Footer (Domain wide footer) werden allen ausgehenden E-Mails hinzugefügt, die einer Adresse innerhalb dieser Domain gehört.<br>Die folgenden Variablen können für die Fußzeile benutzt werden:", "domain_footer_info": "Domain wide footer werden allen ausgehenden E-Mails hinzugefügt, die einer Adresse innerhalb dieser Domain gehört.<br>Die folgenden Variablen können für den Footer benutzt werden:",
"domain_footer_info_vars": { "domain_footer_plain": "PLAIN footer",
"auth_user": "{= auth_user =} - Angemeldeter Benutzername vom MTA",
"from_user": "{= from_user =} - Absender Teil der E-Mail z.B. für \"moo@mailcow.tld\" wird \"moo\" zurückgeben.",
"from_name": "{= from_name =} - Namen des Absenders z.B. für \"Mailcow &lt;moo@mailcow.tld&gt;\", wird \"Mailcow\" zurückgegeben.",
"from_addr": "{= from_addr =} - Adresse des Absenders.",
"from_domain": "{= from_domain =} - Domain des Absenders",
"custom": "{= foo =} - Wenn die Mailbox das benutzerdefinierte Attribut \"foo\" mit dem Wert \"bar\" hat, wird \"bar\" zurückgegeben."
},
"domain_footer_plain": "Fußzeile im PLAIN Format",
"domain_footer_skip_replies": "Ignoriere Footer bei Antwort E-Mails",
"domain_quota": "Domain Speicherplatz gesamt (MiB)", "domain_quota": "Domain Speicherplatz gesamt (MiB)",
"domains": "Domains", "domains": "Domains",
"dont_check_sender_acl": "Absender für Domain %s u. Alias-Domain nicht prüfen", "dont_check_sender_acl": "Absender für Domain %s u. Alias-Domain nicht prüfen",
@ -689,7 +680,11 @@
"unchanged_if_empty": "Unverändert, wenn leer", "unchanged_if_empty": "Unverändert, wenn leer",
"username": "Benutzername", "username": "Benutzername",
"validate_save": "Validieren und speichern", "validate_save": "Validieren und speichern",
"pushover_sound": "Ton" "pushover_sound": "Ton",
"domain_footer_info_vars": {
"auth_user": "{= auth_user =} - Angemeldeter Benutzername vom MTA",
"from_user": "{= from_user =} - Von Teil des Benutzers z.B. \"moo@mailcow.tld\" wird \"moo\" zurückgeben."
}
}, },
"fido2": { "fido2": {
"confirm": "Bestätigen", "confirm": "Bestätigen",
@ -1093,7 +1088,6 @@
"verified_yotp_login": "Yubico-OTP-Anmeldung verifiziert" "verified_yotp_login": "Yubico-OTP-Anmeldung verifiziert"
}, },
"tfa": { "tfa": {
"authenticators": "Authentikatoren",
"api_register": "%s verwendet die Yubico-Cloud-API. Ein API-Key für den Yubico-Stick kann <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">hier</a> bezogen werden.", "api_register": "%s verwendet die Yubico-Cloud-API. Ein API-Key für den Yubico-Stick kann <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">hier</a> bezogen werden.",
"confirm": "Bestätigen", "confirm": "Bestätigen",
"confirm_totp_token": "Bitte bestätigen Sie die Änderung durch Eingabe eines generierten Tokens", "confirm_totp_token": "Bitte bestätigen Sie die Änderung durch Eingabe eines generierten Tokens",

View File

@ -600,7 +600,6 @@
"custom": "{= foo =} - If mailbox has the custom attribute \"foo\" with value \"bar\" it returns \"bar\"" "custom": "{= foo =} - If mailbox has the custom attribute \"foo\" with value \"bar\" it returns \"bar\""
}, },
"domain_footer_plain": "PLAIN footer", "domain_footer_plain": "PLAIN footer",
"domain_footer_skip_replies": "Ignore footer on reply e-mails",
"domain_quota": "Domain quota", "domain_quota": "Domain quota",
"domains": "Domains", "domains": "Domains",
"dont_check_sender_acl": "Disable sender check for domain %s (+ alias domains)", "dont_check_sender_acl": "Disable sender check for domain %s (+ alias domains)",
@ -1100,7 +1099,6 @@
"verified_yotp_login": "Verified Yubico OTP login" "verified_yotp_login": "Verified Yubico OTP login"
}, },
"tfa": { "tfa": {
"authenticators": "Authenticators",
"api_register": "%s uses the Yubico Cloud API. Please get an API key for your key <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">here</a>", "api_register": "%s uses the Yubico Cloud API. Please get an API key for your key <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">here</a>",
"confirm": "Confirm", "confirm": "Confirm",
"confirm_totp_token": "Please confirm your changes by entering the generated token", "confirm_totp_token": "Please confirm your changes by entering the generated token",

View File

@ -305,14 +305,6 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-4">
<label class="control-label col-sm-2" for="domain_footer_skip_replies">{{ lang.edit.domain_footer_skip_replies }}:</label>
<div class="col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" id="domain_footer_skip_replies" name="skip_replies"{% if domain_footer.skip_replies == '1' %} checked{% endif %}></label>
</div>
</div>
</div>
<div class="row mb-2"> <div class="row mb-2">
<label class="control-label col-sm-2" for="domain_footer_html">{{ lang.edit.domain_footer_html }}:</label> <label class="control-label col-sm-2" for="domain_footer_html">{{ lang.edit.domain_footer_html }}:</label>
<div class="col-sm-10"> <div class="col-sm-10">

View File

@ -155,7 +155,7 @@
{% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %} {% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link {% if pending_tfa_authmechs["totp"] %}active{% endif %}" href="#tfa_tab_totp" data-bs-toggle="tab" id="pending_tfa_tab_totp"><i class="bi bi-clock-history"></i> Time-based OTP</a> <a class="nav-link {% if pending_tfa_authmechs["totp"] %}active{% endif %}" href="#tfa_tab_totp" data-bs-toggle="tab" id="pending_tfa_tab_totp"><i class="bi bi-clock-history"></i> Time based OTP</a>
</li> </li>
{% endif %} {% endif %}
@ -173,7 +173,7 @@
<form role="form" method="post" id="webauthn_auth_form"> <form role="form" method="post" id="webauthn_auth_form">
<legend class="mt-2 mb-2"> <legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i> <i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }} Authenticators
<hr /> <hr />
</legend> </legend>
<div class="list-group"> <div class="list-group">
@ -216,7 +216,7 @@
<form role="form" method="post"> <form role="form" method="post">
<legend class="mt-2 mb-2"> <legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i> <i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }} Authenticate
<hr /> <hr />
</legend> </legend>
<div class="collapse show pending-tfa-collapse" id="collapseYubiTFA"> <div class="collapse show pending-tfa-collapse" id="collapseYubiTFA">
@ -244,7 +244,7 @@
<form role="form" method="post"> <form role="form" method="post">
<legend class="mt-2 mb-2"> <legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i> <i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }} Authenticators
<hr /> <hr />
</legend> </legend>
<div class="list-group"> <div class="list-group">

View File

@ -2,7 +2,7 @@ version: '2.1'
services: services:
unbound-mailcow: unbound-mailcow:
image: mailcow/unbound:1.19 image: mailcow/unbound:1.18
environment: environment:
- TZ=${TZ} - TZ=${TZ}
volumes: volumes:
@ -107,7 +107,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: mailcow/phpfpm:1.86 image: mailcow/phpfpm:1.85
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on: depends_on:
- redis-mailcow - redis-mailcow
@ -398,7 +398,7 @@ services:
condition: service_started condition: service_started
unbound-mailcow: unbound-mailcow:
condition: service_healthy condition: service_healthy
image: mailcow/acme:1.86 image: mailcow/acme:1.85
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
@ -434,7 +434,7 @@ services:
- acme - acme
netfilter-mailcow: netfilter-mailcow:
image: mailcow/netfilter:1.55 image: mailcow/netfilter:1.54
stop_grace_period: 30s stop_grace_period: 30s
depends_on: depends_on:
- dovecot-mailcow - dovecot-mailcow
@ -457,7 +457,7 @@ services:
- /lib/modules:/lib/modules:ro - /lib/modules:/lib/modules:ro
watchdog-mailcow: watchdog-mailcow:
image: mailcow/watchdog:2.01 image: mailcow/watchdog:2.00
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
tmpfs: tmpfs:
@ -529,7 +529,7 @@ services:
- watchdog - watchdog
dockerapi-mailcow: dockerapi-mailcow:
image: mailcow/dockerapi:2.07 image: mailcow/dockerapi:2.06
security_opt: security_opt:
- label=disable - label=disable
restart: always restart: always
@ -564,7 +564,7 @@ services:
- solr - solr
olefy-mailcow: olefy-mailcow:
image: mailcow/olefy:1.12 image: mailcow/olefy:1.11
restart: always restart: always
environment: environment:
- TZ=${TZ} - TZ=${TZ}