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>"
ARG PIP_BREAK_SYSTEM_PACKAGES=1
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \
bash \

View File

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

View File

@ -5,63 +5,16 @@ import json
import uuid
import async_timeout
import asyncio
import aioredis
import aiodocker
import docker
import logging
from logging.config import dictConfig
from fastapi import FastAPI, Response, Request
from modules.DockerApi import DockerApi
from redis import asyncio as aioredis
from contextlib import asynccontextmanager
dockerapi = None
@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)
app = FastAPI()
# Define Routes
@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'))
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
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>.*)$
ARG DOVECOT=2.3.21
# 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

View File

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

View File

@ -1,7 +1,6 @@
FROM alpine:3.19
FROM alpine:3.17
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ARG PIP_BREAK_SYSTEM_PACKAGES=1
WORKDIR /app
#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>"
# 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>.*)$
ARG IMAGICK_PECL_VERSION=3.7.0
# 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>.*)$
ARG MEMCACHED_PECL_VERSION=3.2.0
# 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>.*)$
ARG COMPOSER_VERSION=2.6.6
ARG COMPOSER_VERSION=2.6.5
RUN apk add -U --no-cache autoconf \
aspell-dev \

View File

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

View File

@ -3,7 +3,7 @@ FROM solr:7.7-slim
USER root
# 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-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>"

View File

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

View File

@ -49,14 +49,13 @@ $from = $headers['From'];
$empty_footer = json_encode(array(
'html' => '',
'plain' => '',
'skip_replies' => 0,
'vars' => array()
));
error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL);
try {
$stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude`, `skip_replies` FROM `domain_wide_footer`
$stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude` FROM `domain_wide_footer`
WHERE `domain` = :domain");
$stmt->execute(array(
':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
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 from_name = ""
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'
);
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);
$description = $_data['description'];
if (empty($description)) $description = $domain;
$tags = (isset($_data['tags'])) ? (array)$_data['tags'] : $DOMAIN_DEFAULT_ATTRIBUTES['tags'];
$aliases = (isset($_data['aliases'])) ? (int)$_data['aliases'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_num_aliases_for_domain'];
$mailboxes = (isset($_data['mailboxes'])) ? (int)$_data['mailboxes'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_num_mboxes_for_domain'];
$defquota = (isset($_data['defquota'])) ? (int)$_data['defquota'] : $DOMAIN_DEFAULT_ATTRIBUTES['def_quota_for_mbox'] / 1024 ** 2;
$maxquota = (isset($_data['maxquota'])) ? (int)$_data['maxquota'] : $DOMAIN_DEFAULT_ATTRIBUTES['max_quota_for_mbox'] / 1024 ** 2;
$tags = (array)$_data['tags'];
$aliases = (int)$_data['aliases'];
$mailboxes = (int)$_data['mailboxes'];
$defquota = (int)$_data['defquota'];
$maxquota = (int)$_data['maxquota'];
$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) {
$_SESSION['return'][] = array(
'type' => 'danger',
@ -528,11 +520,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$active = (isset($_data['active'])) ? intval($_data['active']) : $DOMAIN_DEFAULT_ATTRIBUTES['active'];
$relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_all_recipients'];
$relay_unknown_only = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : $DOMAIN_DEFAULT_ATTRIBUTES['relay_unknown_only'];
$backupmx = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $DOMAIN_DEFAULT_ATTRIBUTES['backupmx'];
$gal = (isset($_data['gal'])) ? intval($_data['gal']) : $DOMAIN_DEFAULT_ATTRIBUTES['gal'];
$active = intval($_data['active']);
$relay_all_recipients = intval($_data['relay_all_recipients']);
$relay_unknown_only = intval($_data['relay_unknown_only']);
$backupmx = intval($_data['backupmx']);
$gal = intval($_data['gal']);
if ($relay_all_recipients == 1) {
$backupmx = '1';
}
@ -633,13 +625,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$_data['rl_value'] = (isset($_data['rl_value'])) ? intval($_data['rl_value']) : $DOMAIN_DEFAULT_ATTRIBUTES['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'])){
if (!empty(intval($_data['rl_value']))) {
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($redis->hGet('DKIM_SELECTORS', $domain))) {
$_SESSION['return'][] = array(
@ -1018,21 +1006,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
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'];
$password2 = $_data['password2'];
$name = ltrim(rtrim($_data['name'], '>'), '<');
$tags = (isset($_data['tags'])) ? $_data['tags'] : $MAILBOX_DEFAULT_ATTRIBUTES['tags'];
$quota_m = (isset($_data['quota'])) ? intval($_data['quota']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['quota']) / 1024 ** 2;
$tags = $_data['tags'];
$quota_m = intval($_data['quota']);
if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && $quota_m === 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
@ -1041,7 +1019,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
if (empty($name)) {
$name = $local_part;
}
if (isset($_data['protocol_access'])) {
$_data['protocol_access'] = (array)$_data['protocol_access'];
$_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['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']);
$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']);
@ -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_category'] = (in_array('quarantine_category', $_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`
(`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,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
$stmt->execute(array(
':username' => $username,
':spam_alias' => $_data['spam_alias'],
@ -1288,17 +1251,31 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':app_passwds' => $_data['app_passwds']
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
return false;
else {
$stmt = $pdo->prepare("INSERT INTO `user_acl`
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`)
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
$stmt->execute(array(
':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'])){
ratelimit('edit', 'mailbox', array(
'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']);
if (isset($_data['protocol_access'])) {
$_data['protocol_access'] = (array)$_data['protocol_access'];
$attr['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0;
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$attr['sieve_access'] = (in_array('sieve', $_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 : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$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 : intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
}
else {
$attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
$attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
}
}
if (isset($_data['acl'])) {
$_data['acl'] = (array)$_data['acl'];
$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['html'] = isset($_data['html']) ? $_data['html'] : '';
$footers['plain'] = isset($_data['plain']) ? $_data['plain'] : '';
$footers['skip_replies'] = isset($_data['skip_replies']) ? (int)$_data['skip_replies'] : 0;
$footers['mbox_exclude'] = array();
if (isset($_data["mbox_exclude"])){
if (!is_array($_data["mbox_exclude"])) {
@ -3484,13 +3460,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
try {
$stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain");
$stmt->execute(array(':domain' => $domain));
$stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`, `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(
':domain' => $domain,
':html' => $footers['html'],
':plain' => $footers['plain'],
':mbox_exclude' => json_encode($footers['mbox_exclude']),
':skip_replies' => $footers['skip_replies'],
));
}
catch (PDOException $e) {
@ -4647,7 +4622,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
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");
$stmt->execute(array(
':domain' => $domain

View File

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

View File

@ -588,19 +588,10 @@
"disable_login": "Login verbieten (Mails werden weiterhin angenommen)",
"domain": "Domain bearbeiten",
"domain_admin": "Domain-Administrator bearbeiten",
"domain_footer": "Domänenweite Fußzeile",
"domain_footer_html": "Fußzeile im HTML Format",
"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_vars": {
"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_footer": "Domain wide footer",
"domain_footer_html": "HTML footer",
"domain_footer_info": "Domain wide footer werden allen ausgehenden E-Mails hinzugefügt, die einer Adresse innerhalb dieser Domain gehört.<br>Die folgenden Variablen können für den Footer benutzt werden:",
"domain_footer_plain": "PLAIN footer",
"domain_quota": "Domain Speicherplatz gesamt (MiB)",
"domains": "Domains",
"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",
"username": "Benutzername",
"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": {
"confirm": "Bestätigen",
@ -1093,7 +1088,6 @@
"verified_yotp_login": "Yubico-OTP-Anmeldung verifiziert"
},
"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.",
"confirm": "Bestätigen",
"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\""
},
"domain_footer_plain": "PLAIN footer",
"domain_footer_skip_replies": "Ignore footer on reply e-mails",
"domain_quota": "Domain quota",
"domains": "Domains",
"dont_check_sender_acl": "Disable sender check for domain %s (+ alias domains)",
@ -1100,7 +1099,6 @@
"verified_yotp_login": "Verified Yubico OTP login"
},
"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>",
"confirm": "Confirm",
"confirm_totp_token": "Please confirm your changes by entering the generated token",

View File

@ -305,14 +305,6 @@
</select>
</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">
<label class="control-label col-sm-2" for="domain_footer_html">{{ lang.edit.domain_footer_html }}:</label>
<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 %}
<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>
{% endif %}
@ -173,7 +173,7 @@
<form role="form" method="post" id="webauthn_auth_form">
<legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }}
Authenticators
<hr />
</legend>
<div class="list-group">
@ -216,7 +216,7 @@
<form role="form" method="post">
<legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }}
Authenticate
<hr />
</legend>
<div class="collapse show pending-tfa-collapse" id="collapseYubiTFA">
@ -244,7 +244,7 @@
<form role="form" method="post">
<legend class="mt-2 mb-2">
<i class="bi bi-shield-fill-check"></i>
{{ lang.tfa.authenticators }}
Authenticators
<hr />
</legend>
<div class="list-group">

View File

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