Merge branch 'master' into patch-4
commit
f8ff11a1e3
|
@ -1,5 +1,6 @@
|
|||
rebuild-images.sh
|
||||
data/conf/sogo/sieve.creds
|
||||
data/conf/phpfpm/sogo-sso/sogo-sso.pass
|
||||
data/conf/dovecot/dovecot-master.passwd
|
||||
data/conf/dovecot/dovecot-master.userdb
|
||||
mailcow.conf
|
||||
|
@ -24,6 +25,7 @@ data/conf/nginx/*.custom
|
|||
data/conf/nginx/*.bak
|
||||
data/conf/dovecot/acl_anyone
|
||||
data/conf/dovecot/mail_plugins*
|
||||
data/conf/dovecot/sogo-sso.conf
|
||||
data/conf/dovecot/extra.conf
|
||||
data/conf/rspamd/custom/*
|
||||
data/conf/portainer/
|
||||
|
|
|
@ -5,6 +5,16 @@ exec 5>&1
|
|||
# Thanks to https://github.com/cvmiller -> https://github.com/cvmiller/expand6
|
||||
source /srv/expand6.sh
|
||||
|
||||
# Skipping IP check when we like to live dangerously
|
||||
if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
SKIP_IP_CHECK=y
|
||||
fi
|
||||
|
||||
# Skipping HTTP check when we like to live dangerously
|
||||
if [[ "${SKIP_HTTP_VERIFICATION}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
SKIP_HTTP_VERIFICATION=y
|
||||
fi
|
||||
|
||||
log_f() {
|
||||
if [[ ${2} == "no_nl" ]]; then
|
||||
echo -n "$(date) - ${1}"
|
||||
|
@ -42,7 +52,6 @@ mkdir -p ${ACME_BASE}/acme
|
|||
[[ -f ${ACME_BASE}/acme/private/privkey.pem ]] && mv ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/acme/key.pem
|
||||
[[ -f ${ACME_BASE}/acme/private/account.key ]] && mv ${ACME_BASE}/acme/private/account.key ${ACME_BASE}/acme/account.pem
|
||||
|
||||
|
||||
reload_configurations(){
|
||||
# Reading container IDs
|
||||
# Wrapping as array to ensure trimmed content when calling $NGINX etc.
|
||||
|
@ -121,7 +130,10 @@ verify_challenge_path(){
|
|||
# verify_challenge_path URL 4|6
|
||||
RAND_FILE=${RANDOM}${RANDOM}${RANDOM}
|
||||
touch /var/www/acme/${RAND_FILE}
|
||||
if [[ "$(curl -${2} http://${1}/.well-known/acme-challenge/${RAND_FILE} --write-out %{http_code} --silent --output /dev/null)" =~ ^(2|3) ]]; then
|
||||
if [[ ${SKIP_HTTP_VERIFICATION} == "y" ]]; then
|
||||
echo '(skipping check, returning 0)'
|
||||
return 0
|
||||
elif [[ "$(curl -${2} http://${1}/.well-known/acme-challenge/${RAND_FILE} --write-out %{http_code} --silent --output /dev/null)" =~ ^(2|3) ]]; then
|
||||
rm /var/www/acme/${RAND_FILE}
|
||||
return 0
|
||||
else
|
||||
|
@ -156,6 +168,7 @@ else
|
|||
exec env TRIGGER_RESTART=1 $(readlink -f "$0")
|
||||
fi
|
||||
fi
|
||||
chmod 600 ${ACME_BASE}/key.pem
|
||||
|
||||
log_f "Waiting for database... " no_nl
|
||||
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
||||
|
@ -196,10 +209,8 @@ while true; do
|
|||
log_f "Using existing Lets Encrypt account key ${ACME_BASE}/acme/account.pem"
|
||||
fi
|
||||
|
||||
# Skipping IP check when we like to live dangerously
|
||||
if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
SKIP_IP_CHECK=y
|
||||
fi
|
||||
chmod 600 ${ACME_BASE}/acme/key.pem
|
||||
chmod 600 ${ACME_BASE}/acme/account.pem
|
||||
|
||||
# Cleaning up and init validation arrays
|
||||
unset SQL_DOMAIN_ARR
|
||||
|
@ -476,6 +487,7 @@ while true; do
|
|||
ACME_RESPONSE_B64=$(echo "${ACME_RESPONSE}" | openssl enc -e -A -base64)
|
||||
log_f "${ACME_RESPONSE_B64}" redis_only b64
|
||||
log_f "Retrying in 30 minutes..."
|
||||
redis-cli -h redis SET ACME_FAIL_TIME "$(date +%s)"
|
||||
sleep 30m
|
||||
exec $(readlink -f "$0")
|
||||
;;
|
||||
|
|
|
@ -7,19 +7,29 @@ if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
|||
fi
|
||||
|
||||
# Prepare whitelist
|
||||
|
||||
mkdir -p /run/clamav /var/lib/clamav
|
||||
|
||||
if [[ -s /etc/clamav/whitelist.ign2 ]]; then
|
||||
echo "Copying non-empty whitelist.ign2 to /var/lib/clamav/whitelist.ign2"
|
||||
cp /etc/clamav/whitelist.ign2 /var/lib/clamav/whitelist.ign2
|
||||
fi
|
||||
if [[ ! -f /var/lib/clamav/whitelist.ign2 ]]; then
|
||||
echo "Creating /var/lib/clamav/whitelist.ign2"
|
||||
echo "Example-Signature.Ignore-1" > /var/lib/clamav/whitelist.ign2
|
||||
fi
|
||||
chown clamav:clamav /var/lib/clamav/whitelist.ign2
|
||||
mkdir -p /run/clamav /var/lib/clamav
|
||||
chown clamav:clamav /run/clamav /var/lib/clamav
|
||||
chmod 750 /run/clamav
|
||||
|
||||
chown clamav:clamav -R /var/lib/clamav /run/clamav
|
||||
|
||||
chmod 755 /var/lib/clamav
|
||||
chmod 644 -R /var/lib/clamav/*
|
||||
chmod 750 /run/clamav
|
||||
|
||||
echo "Stating whitelist.ign2"
|
||||
stat /var/lib/clamav/whitelist.ign2
|
||||
|
||||
dos2unix /var/lib/clamav/whitelist.ign2
|
||||
|
||||
sed -i '/^\s*$/d' /var/lib/clamav/whitelist.ign2
|
||||
|
||||
BACKGROUND_TASKS=()
|
||||
|
@ -38,7 +48,7 @@ while true; do
|
|||
sleep 2m
|
||||
SANE_MIRRORS="$(dig +ignore +short rsync.sanesecurity.net)"
|
||||
for sane_mirror in ${SANE_MIRRORS}; do
|
||||
rsync -avp --chown=clamav:clamav --timeout=5 rsync://${sane_mirror}/sanesecurity/ \
|
||||
rsync -avp --chown=clamav:clamav --chmod=Du=rwx,Dgo=rx,Fu=rw,Fog=r --timeout=5 rsync://${sane_mirror}/sanesecurity/ \
|
||||
--include 'blurl.ndb' \
|
||||
--include 'junk.ndb' \
|
||||
--include 'jurlbl.ndb' \
|
||||
|
|
|
@ -3,8 +3,8 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
|||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV LC_ALL C
|
||||
ENV DOVECOT_VERSION 2.3.4
|
||||
ENV PIGEONHOLE_VERSION 0.5.4
|
||||
ENV DOVECOT_VERSION 2.3.5.1
|
||||
ENV PIGEONHOLE_VERSION 0.5.5
|
||||
|
||||
RUN apt-get update && apt-get -y --no-install-recommends install \
|
||||
automake \
|
||||
|
|
|
@ -106,7 +106,7 @@ chmod 644 /usr/local/etc/dovecot/mail_plugins /usr/local/etc/dovecot/mail_plugin
|
|||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql-userdb.conf
|
||||
driver = mysql
|
||||
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
||||
user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/:VOLATILEDIR=/var/volatile/%u') AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
|
||||
user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/${MAILDIR_SUB}:VOLATILEDIR=/var/volatile/%u') AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
|
||||
iterate_query = SELECT username FROM mailbox WHERE active='1';
|
||||
EOF
|
||||
|
||||
|
@ -127,6 +127,10 @@ if [[ $(stat -c %U /var/vmail/) != "vmail" ]] ; then chown -R vmail:vmail /var/v
|
|||
if [[ $(stat -c %U /var/vmail/_garbage) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail/_garbage ; fi
|
||||
if [[ $(stat -c %U /var/attachments) != "vmail" ]] ; then chown -R vmail:vmail /var/attachments ; fi
|
||||
|
||||
# Cleanup random user maildirs
|
||||
rm -rf /var/vmail/mailcow.local/*
|
||||
|
||||
|
||||
# Create random master for SOGo sieve features
|
||||
RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
|
||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
|
||||
|
@ -135,6 +139,21 @@ echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{p
|
|||
echo ${RAND_USER}@mailcow.local::5000:5000:::: > /usr/local/etc/dovecot/dovecot-master.userdb
|
||||
echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds
|
||||
|
||||
if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
# Create random master Password for SOGo 'login as user' via proxy auth
|
||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
|
||||
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
|
||||
cat <<EOF > /usr/local/etc/dovecot/sogo-sso.conf
|
||||
passdb {
|
||||
driver = static
|
||||
args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
|
||||
}
|
||||
EOF
|
||||
else
|
||||
rm -f /usr/local/etc/dovecot/sogo-sso.pass
|
||||
rm -f /usr/local/etc/dovecot/sogo-sso.conf
|
||||
fi
|
||||
|
||||
# 401 is user dovecot
|
||||
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then
|
||||
openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem
|
||||
|
@ -174,7 +193,7 @@ echo '30 3 * * * vmail /usr/local/bin/doveadm quota recalc -A' > /etc/cron.d/d
|
|||
echo '* * * * * vmail /usr/local/bin/trim_logs.sh >> /dev/console 2>&1' > /etc/cron.d/trim_logs
|
||||
echo '25 * * * * vmail /usr/local/bin/maildir_gc.sh >> /dev/console 2>&1' > /etc/cron.d/maildir_gc
|
||||
echo '30 1 * * * root /usr/local/bin/sa-rules.sh >> /dev/console 2>&1' > /etc/cron.d/sa-rules
|
||||
echo '0 2 * * * root /usr/bin/curl http://solr:8983/solr/dovecot/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
|
||||
echo '0 2 * * * root /usr/bin/curl http://solr:8983/solr/dovecot-fts/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
|
||||
echo '*/20 * * * * vmail /usr/local/bin/quarantine_notify.py >> /dev/console 2>&1' > /etc/cron.d/quarantine_notify
|
||||
|
||||
# Fix more than 1 hardlink issue
|
||||
|
|
|
@ -123,5 +123,3 @@ for record in records:
|
|||
if last_notification == 0 or (last_notification + 604800) < time_now:
|
||||
print "Notifying %s about %d new items in quarantine" % (record['rcpt'], record['counter'])
|
||||
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
|
||||
else:
|
||||
break
|
||||
|
|
|
@ -31,7 +31,8 @@ RULES[2] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([
|
|||
RULES[3] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
RULES[4] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
|
||||
RULES[5] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
|
||||
#RULES[6] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
RULES[6] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
|
||||
#RULES[7] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
|
||||
bans = {}
|
||||
log = {}
|
||||
|
|
|
@ -25,23 +25,26 @@ CONTAINER_ID=
|
|||
# Todo: Better check if upgrade failed
|
||||
# This can happen due to a broken sogo_view
|
||||
[ -s /mysql_upgrade_loop ] && SQL_LOOP_C=$(cat /mysql_upgrade_loop)
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id")
|
||||
if [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ [^a-zA-Z0-9] ]]; then
|
||||
SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
|
||||
if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
|
||||
if [ -z ${SQL_LOOP_C} ]; then
|
||||
echo 1 > /mysql_upgrade_loop
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
else
|
||||
rm /mysql_upgrade_loop
|
||||
echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
|
||||
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
||||
echo "Waiting for SQL to return..."
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
until [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ ^[[:alnum:]]*$ ]]; do
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id" 2> /dev/null)
|
||||
done
|
||||
echo "MySQL @ ${CONTAINER_ID}"
|
||||
SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
|
||||
if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
|
||||
if [ -z ${SQL_LOOP_C} ]; then
|
||||
echo 1 > /mysql_upgrade_loop
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
else
|
||||
rm /mysql_upgrade_loop
|
||||
echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
|
||||
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
||||
echo "Waiting for SQL to return..."
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo "MySQL is up-to-date"
|
||||
fi
|
||||
|
||||
# Trigger db init
|
||||
|
|
|
@ -104,8 +104,8 @@ query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
|
|||
WHERE id IN (
|
||||
SELECT relayhost FROM domain
|
||||
WHERE CONCAT('@', domain) = '%s'
|
||||
OR '%s' IN (
|
||||
SELECT CONCAT('@', alias_domain) FROM alias_domain
|
||||
OR domain IN (
|
||||
SELECT target_domain FROM alias_domain WHERE CONCAT('@', alias_domain) = '%s'
|
||||
)
|
||||
)
|
||||
AND active = '1'
|
||||
|
|
|
@ -13,7 +13,6 @@ RUN apt-get update && apt-get install -y \
|
|||
&& echo "deb https://rspamd.com/apt-stable/ bionic main" > /etc/apt/sources.list.d/rspamd.list \
|
||||
&& apt-get update && apt-get install -y rspamd \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspamd.conf.local \
|
||||
&& apt-get autoremove --purge \
|
||||
&& apt-get clean \
|
||||
&& mkdir -p /run/rspamd \
|
||||
|
@ -21,7 +20,6 @@ RUN apt-get update && apt-get install -y \
|
|||
|
||||
COPY settings.conf /etc/rspamd/settings.conf
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
COPY metadata_exporter.lua /usr/share/rspamd/lua/metadata_exporter.lua
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||
gettext \
|
||||
gnupg \
|
||||
mysql-client \
|
||||
rsync \
|
||||
supervisor \
|
||||
syslog-ng \
|
||||
syslog-ng-core \
|
||||
|
@ -52,6 +53,4 @@ RUN chmod +x /bootstrap-sogo.sh \
|
|||
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
|
||||
VOLUME /usr/lib/GNUstep/SOGo/
|
||||
|
||||
RUN rm -rf /tmp/* /var/tmp/*
|
||||
|
|
|
@ -83,9 +83,16 @@ EOF
|
|||
done
|
||||
|
||||
|
||||
mkdir -p /var/lib/sogo/GNUstep/Defaults/
|
||||
if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
TRUST_PROXY="YES"
|
||||
else
|
||||
TRUST_PROXY="NO"
|
||||
fi
|
||||
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
|
||||
RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
|
||||
|
||||
# Generate plist header with timezone data
|
||||
mkdir -p /var/lib/sogo/GNUstep/Defaults/
|
||||
cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//GNUstep//DTD plist 0.9//EN" "http://www.gnustep.org/plist-0_9.xml">
|
||||
|
@ -93,6 +100,12 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
|||
<dict>
|
||||
<key>OCSAclURL</key>
|
||||
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
||||
<key>SOGoIMAPServer</key>
|
||||
<string>imap://${IPV4_NETWORK}.250:143/?tls=YES</string>
|
||||
<key>SOGoTrustProxyAuthentication</key>
|
||||
<string>${TRUST_PROXY}</string>
|
||||
<key>SOGoEncryptionKey</key>
|
||||
<string>${RAND_PASS}</string>
|
||||
<key>OCSCacheFolderURL</key>
|
||||
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_cache_folder</string>
|
||||
<key>OCSEMailAlarmsFolderURL</key>
|
||||
|
@ -183,4 +196,8 @@ fi
|
|||
# Copy logo, if any
|
||||
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
|
||||
|
||||
# Rsync web content
|
||||
echo "Syncing web content with named volume"
|
||||
rsync -a /usr/lib/GNUstep/SOGo/. /sogo_web/
|
||||
|
||||
exec gosu sogo /usr/sbin/sogod
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
FROM solr:7-alpine
|
||||
FROM solr:7.7-alpine
|
||||
USER root
|
||||
COPY docker-entrypoint.sh /
|
||||
COPY solr-config-7.7.0.xml /
|
||||
COPY solr-schema-7.7.0.xml /
|
||||
|
||||
|
||||
RUN apk --no-cache add su-exec curl tzdata \
|
||||
&& chmod +x /docker-entrypoint.sh \
|
||||
&& sync \
|
||||
&& /docker-entrypoint.sh --bootstrap
|
||||
&& bash /docker-entrypoint.sh --bootstrap
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
|
|
@ -18,192 +18,44 @@ fi
|
|||
|
||||
set -e
|
||||
|
||||
# allow easier debugging with `docker run -e VERBOSE=yes`
|
||||
if [[ "$VERBOSE" = "yes" ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# run the optional initdb
|
||||
. /opt/docker-solr/scripts/run-initdb
|
||||
|
||||
function solr_config() {
|
||||
curl -XPOST http://localhost:8983/solr/dovecot/schema -H 'Content-type:application/json' -d '{
|
||||
"add-field-type":{
|
||||
"name":"long",
|
||||
"class":"solr.TrieLongField"
|
||||
},
|
||||
"add-field-type":{
|
||||
"name":"text",
|
||||
"class":"solr.TextField",
|
||||
"positionIncrementGap":100,
|
||||
"indexAnalyser":{
|
||||
"tokenizer":{
|
||||
"class":"solr.StandardTokenizerFactory"
|
||||
},
|
||||
"filter":{
|
||||
"class":"solr.WordDelimiterFilterFactory",
|
||||
"generateWordParts":1,
|
||||
"generateNumberParts":1,
|
||||
"catenateWorks":1,
|
||||
"catenateNumbers":1,
|
||||
"catenateAll":0
|
||||
},
|
||||
"filter":{
|
||||
"class":"solr.LowerCaseFilterFactory"
|
||||
},
|
||||
"filter":{
|
||||
"class":"solr.KeywordMarkerFilterFactory",
|
||||
"protected":"protwords.txt"
|
||||
}
|
||||
},
|
||||
"queryAnalyzer":{
|
||||
"tokenizer":{
|
||||
"class":"solr.StandardTokenizerFactory"
|
||||
},
|
||||
"filter":{
|
||||
"synonyms":"synonyms.txt",
|
||||
"ignoreCase":true,
|
||||
"expand":true
|
||||
},
|
||||
"filter":{
|
||||
"class":"solr.LowerCaseFilterFactory"
|
||||
},
|
||||
"filter":{
|
||||
"class":"solr.WordDelimiterFilterFactory",
|
||||
"generateWordParts":1,
|
||||
"generateNumberParts":1,
|
||||
"catenateWords":0,
|
||||
"catenateNumbers":0,
|
||||
"catenateAll":0,
|
||||
"splitOnCaseChange":1
|
||||
}
|
||||
}
|
||||
},
|
||||
"add-field":{
|
||||
"name":"uid",
|
||||
"type":"long",
|
||||
"indexed":true,
|
||||
"stored":true,
|
||||
"required":true
|
||||
},
|
||||
"add-field":{
|
||||
"name":"box",
|
||||
"type":"string",
|
||||
"indexed":true,
|
||||
"stored":true,
|
||||
"required":true
|
||||
},
|
||||
"add-field":{
|
||||
"name":"user",
|
||||
"type":"string",
|
||||
"indexed":true,
|
||||
"stored":true,
|
||||
"required":true
|
||||
},
|
||||
"add-field":{
|
||||
"name":"hdr",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
|
||||
},
|
||||
"add-field":{
|
||||
"name":"body",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
},
|
||||
"add-field":{
|
||||
"name":"from",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
},
|
||||
"add-field":{
|
||||
"name":"to",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
},
|
||||
"add-field":{
|
||||
"name":"cc",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
},
|
||||
"add-field":{
|
||||
"name":"bcc",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
},
|
||||
"add-field":{
|
||||
"name":"subject",
|
||||
"type":"text",
|
||||
"indexed":true,
|
||||
"stored":false
|
||||
}
|
||||
}'
|
||||
|
||||
curl -XPOST http://localhost:8983/solr/dovecot/config -H 'Content-type:application/json' -d '{
|
||||
"update-requesthandler":{
|
||||
"name":"/select",
|
||||
"class":"solr.SearchHandler",
|
||||
"defaults":{
|
||||
"wt":"xml"
|
||||
}
|
||||
}
|
||||
}'
|
||||
|
||||
curl -XPOST http://localhost:8983/solr/dovecot/config/updateHandler -d '{
|
||||
"set-property": {
|
||||
"updateHandler.autoSoftCommit.maxDocs":500,
|
||||
"updateHandler.autoSoftCommit.maxTime":120000,
|
||||
"updateHandler.autoCommit.maxDocs":200,
|
||||
"updateHandler.autoCommit.maxTime":1800000,
|
||||
"updateHandler.autoCommit.openSearcher":false
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
# fixing volume permission
|
||||
[[ -d /opt/solr/server/solr/dovecot/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot/data
|
||||
[[ -d /opt/solr/server/solr/dovecot-fts/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot-fts/data
|
||||
if [[ "${1}" != "--bootstrap" ]]; then
|
||||
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="'${SOLR_HEAP:-1024}'m"' /opt/solr/bin/solr.in.sh
|
||||
else
|
||||
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="256m"' /opt/solr/bin/solr.in.sh
|
||||
fi
|
||||
|
||||
# start a Solr so we can use the Schema API, but only on localhost,
|
||||
# so that clients don't see Solr until we have configured it.
|
||||
echo "Starting local Solr instance to setup configuration"
|
||||
su-exec solr start-local-solr
|
||||
if [[ "${1}" == "--bootstrap" ]]; then
|
||||
echo "Creating initial configuration"
|
||||
echo "Modifying default config set"
|
||||
cp /solr-config-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml
|
||||
cp /solr-schema-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/schema.xml
|
||||
rm /opt/solr/server/solr/configsets/_default/conf/managed-schema
|
||||
|
||||
# keep a sentinel file so we don't try to create the core a second time
|
||||
# for example when we restart a container.
|
||||
SENTINEL=/opt/docker-solr/core_created
|
||||
if [[ -f ${SENTINEL} ]]; then
|
||||
echo "skipping core creation"
|
||||
else
|
||||
echo "Creating core \"dovecot\""
|
||||
su-exec solr /opt/solr/bin/solr create -c "dovecot"
|
||||
echo "Starting local Solr instance to setup configuration"
|
||||
su-exec solr start-local-solr
|
||||
|
||||
echo "Creating core \"dovecot-fts\""
|
||||
su-exec solr /opt/solr/bin/solr create -c "dovecot-fts"
|
||||
|
||||
# See https://github.com/docker-solr/docker-solr/issues/27
|
||||
echo "Checking core"
|
||||
while ! wget -O - 'http://localhost:8983/solr/admin/cores?action=STATUS' | grep -q instanceDir; do
|
||||
echo "Could not find any cores, waiting..."
|
||||
sleep 5
|
||||
sleep 3
|
||||
done
|
||||
echo "Created core \"dovecot\""
|
||||
touch ${SENTINEL}
|
||||
|
||||
echo "Created core \"dovecot-fts\""
|
||||
|
||||
echo "Stopping local Solr"
|
||||
su-exec solr stop-local-solr
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Starting configuration"
|
||||
solr_config
|
||||
echo "Stopping local Solr"
|
||||
su-exec solr stop-local-solr
|
||||
if [[ "${1}" == "--bootstrap" ]]; then
|
||||
exit 0
|
||||
else
|
||||
exec su-exec solr solr-foreground
|
||||
fi
|
||||
exec su-exec solr solr-foreground
|
||||
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!-- This is the default config with stuff non-essential to Dovecot removed. -->
|
||||
|
||||
<config>
|
||||
<!-- Controls what version of Lucene various components of Solr
|
||||
adhere to. Generally, you want to use the latest version to
|
||||
get all bug fixes and improvements. It is highly recommended
|
||||
that you fully re-index after changing this setting as it can
|
||||
affect both how text is indexed and queried.
|
||||
-->
|
||||
<luceneMatchVersion>7.7.0</luceneMatchVersion>
|
||||
|
||||
<!-- A 'dir' option by itself adds any files found in the directory
|
||||
to the classpath, this is useful for including all jars in a
|
||||
directory.
|
||||
|
||||
When a 'regex' is specified in addition to a 'dir', only the
|
||||
files in that directory which completely match the regex
|
||||
(anchored on both ends) will be included.
|
||||
|
||||
If a 'dir' option (with or without a regex) is used and nothing
|
||||
is found that matches, a warning will be logged.
|
||||
|
||||
The examples below can be used to load some solr-contribs along
|
||||
with their external dependencies.
|
||||
-->
|
||||
<lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" />
|
||||
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" />
|
||||
|
||||
<lib dir="${solr.install.dir:../../../..}/contrib/clustering/lib/" regex=".*\.jar" />
|
||||
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-clustering-\d.*\.jar" />
|
||||
|
||||
<lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" />
|
||||
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-\d.*\.jar" />
|
||||
|
||||
<lib dir="${solr.install.dir:../../../..}/contrib/velocity/lib" regex=".*\.jar" />
|
||||
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-velocity-\d.*\.jar" />
|
||||
|
||||
<!-- Data Directory
|
||||
|
||||
Used to specify an alternate directory to hold all index data
|
||||
other than the default ./data under the Solr home. If
|
||||
replication is in use, this should match the replication
|
||||
configuration.
|
||||
-->
|
||||
<dataDir>${solr.data.dir:}</dataDir>
|
||||
|
||||
<!-- The default high-performance update handler -->
|
||||
<updateHandler class="solr.DirectUpdateHandler2">
|
||||
|
||||
<!-- Enables a transaction log, used for real-time get, durability, and
|
||||
and solr cloud replica recovery. The log can grow as big as
|
||||
uncommitted changes to the index, so use of a hard autoCommit
|
||||
is recommended (see below).
|
||||
"dir" - the target directory for transaction logs, defaults to the
|
||||
solr data directory.
|
||||
"numVersionBuckets" - sets the number of buckets used to keep
|
||||
track of max version values when checking for re-ordered
|
||||
updates; increase this value to reduce the cost of
|
||||
synchronizing access to version buckets during high-volume
|
||||
indexing, this requires 8 bytes (long) * numVersionBuckets
|
||||
of heap space per Solr core.
|
||||
-->
|
||||
<updateLog>
|
||||
<str name="dir">${solr.ulog.dir:}</str>
|
||||
<int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int>
|
||||
</updateLog>
|
||||
|
||||
<!-- AutoCommit
|
||||
|
||||
Perform a hard commit automatically under certain conditions.
|
||||
Instead of enabling autoCommit, consider using "commitWithin"
|
||||
when adding documents.
|
||||
|
||||
http://wiki.apache.org/solr/UpdateXmlMessages
|
||||
|
||||
maxDocs - Maximum number of documents to add since the last
|
||||
commit before automatically triggering a new commit.
|
||||
|
||||
maxTime - Maximum amount of time in ms that is allowed to pass
|
||||
since a document was added before automatically
|
||||
triggering a new commit.
|
||||
openSearcher - if false, the commit causes recent index changes
|
||||
to be flushed to stable storage, but does not cause a new
|
||||
searcher to be opened to make those changes visible.
|
||||
|
||||
If the updateLog is enabled, then it's highly recommended to
|
||||
have some sort of hard autoCommit to limit the log size.
|
||||
-->
|
||||
<autoCommit>
|
||||
<maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
|
||||
<openSearcher>false</openSearcher>
|
||||
</autoCommit>
|
||||
|
||||
<!-- softAutoCommit is like autoCommit except it causes a
|
||||
'soft' commit which only ensures that changes are visible
|
||||
but does not ensure that data is synced to disk. This is
|
||||
faster and more near-realtime friendly than a hard commit.
|
||||
-->
|
||||
<autoSoftCommit>
|
||||
<maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime>
|
||||
</autoSoftCommit>
|
||||
|
||||
<!-- Update Related Event Listeners
|
||||
|
||||
Various IndexWriter related events can trigger Listeners to
|
||||
take actions.
|
||||
|
||||
postCommit - fired after every commit or optimize command
|
||||
postOptimize - fired after every optimize command
|
||||
-->
|
||||
|
||||
</updateHandler>
|
||||
|
||||
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Query section - these settings control query time things like caches
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
|
||||
<query>
|
||||
<!-- Solr Internal Query Caches
|
||||
|
||||
There are two implementations of cache available for Solr,
|
||||
LRUCache, based on a synchronized LinkedHashMap, and
|
||||
FastLRUCache, based on a ConcurrentHashMap.
|
||||
|
||||
FastLRUCache has faster gets and slower puts in single
|
||||
threaded operation and thus is generally faster than LRUCache
|
||||
when the hit ratio of the cache is high (> 75%), and may be
|
||||
faster under other scenarios on multi-cpu systems.
|
||||
-->
|
||||
|
||||
<!-- Filter Cache
|
||||
|
||||
Cache used by SolrIndexSearcher for filters (DocSets),
|
||||
unordered sets of *all* documents that match a query. When a
|
||||
new searcher is opened, its caches may be prepopulated or
|
||||
"autowarmed" using data from caches in the old searcher.
|
||||
autowarmCount is the number of items to prepopulate. For
|
||||
LRUCache, the autowarmed items will be the most recently
|
||||
accessed items.
|
||||
|
||||
Parameters:
|
||||
class - the SolrCache implementation LRUCache or
|
||||
(LRUCache or FastLRUCache)
|
||||
size - the maximum number of entries in the cache
|
||||
initialSize - the initial capacity (number of entries) of
|
||||
the cache. (see java.util.HashMap)
|
||||
autowarmCount - the number of entries to prepopulate from
|
||||
and old cache.
|
||||
maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
|
||||
to occupy. Note that when this option is specified, the size
|
||||
and initialSize parameters are ignored.
|
||||
-->
|
||||
<filterCache class="solr.FastLRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- Query Result Cache
|
||||
|
||||
Caches results of searches - ordered lists of document ids
|
||||
(DocList) based on a query, a sort, and the range of documents requested.
|
||||
Additional supported parameter by LRUCache:
|
||||
maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
|
||||
to occupy
|
||||
-->
|
||||
<queryResultCache class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- Document Cache
|
||||
|
||||
Caches Lucene Document objects (the stored fields for each
|
||||
document). Since Lucene internal document ids are transient,
|
||||
this cache will not be autowarmed.
|
||||
-->
|
||||
<documentCache class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- custom cache currently used by block join -->
|
||||
<cache name="perSegFilter"
|
||||
class="solr.search.LRUCache"
|
||||
size="10"
|
||||
initialSize="0"
|
||||
autowarmCount="10"
|
||||
regenerator="solr.NoOpRegenerator" />
|
||||
|
||||
<!-- Lazy Field Loading
|
||||
|
||||
If true, stored fields that are not requested will be loaded
|
||||
lazily. This can result in a significant speed improvement
|
||||
if the usual case is to not load all stored fields,
|
||||
especially if the skipped fields are large compressed text
|
||||
fields.
|
||||
-->
|
||||
<enableLazyFieldLoading>true</enableLazyFieldLoading>
|
||||
|
||||
<!-- Result Window Size
|
||||
|
||||
An optimization for use with the queryResultCache. When a search
|
||||
is requested, a superset of the requested number of document ids
|
||||
are collected. For example, if a search for a particular query
|
||||
requests matching documents 10 through 19, and queryWindowSize is 50,
|
||||
then documents 0 through 49 will be collected and cached. Any further
|
||||
requests in that range can be satisfied via the cache.
|
||||
-->
|
||||
<queryResultWindowSize>20</queryResultWindowSize>
|
||||
|
||||
<!-- Maximum number of documents to cache for any entry in the
|
||||
queryResultCache.
|
||||
-->
|
||||
<queryResultMaxDocsCached>200</queryResultMaxDocsCached>
|
||||
|
||||
<!-- Use Cold Searcher
|
||||
|
||||
If a search request comes in and there is no current
|
||||
registered searcher, then immediately register the still
|
||||
warming searcher and use it. If "false" then all requests
|
||||
will block until the first searcher is done warming.
|
||||
-->
|
||||
<useColdSearcher>false</useColdSearcher>
|
||||
|
||||
</query>
|
||||
|
||||
|
||||
<!-- Request Dispatcher
|
||||
|
||||
This section contains instructions for how the SolrDispatchFilter
|
||||
should behave when processing requests for this SolrCore.
|
||||
|
||||
-->
|
||||
<requestDispatcher>
|
||||
<httpCaching never304="true" />
|
||||
</requestDispatcher>
|
||||
|
||||
<!-- Request Handlers
|
||||
|
||||
http://wiki.apache.org/solr/SolrRequestHandler
|
||||
|
||||
Incoming queries will be dispatched to a specific handler by name
|
||||
based on the path specified in the request.
|
||||
|
||||
If a Request Handler is declared with startup="lazy", then it will
|
||||
not be initialized until the first request that uses it.
|
||||
|
||||
-->
|
||||
<!-- SearchHandler
|
||||
|
||||
http://wiki.apache.org/solr/SearchHandler
|
||||
|
||||
For processing Search Queries, the primary Request Handler
|
||||
provided with Solr is "SearchHandler" It delegates to a sequent
|
||||
of SearchComponents (see below) and supports distributed
|
||||
queries across multiple shards
|
||||
-->
|
||||
<requestHandler name="/select" class="solr.SearchHandler">
|
||||
<!-- default values for query parameters can be specified, these
|
||||
will be overridden by parameters in the request
|
||||
-->
|
||||
<lst name="defaults">
|
||||
<str name="echoParams">explicit</str>
|
||||
<int name="rows">10</int>
|
||||
</lst>
|
||||
</requestHandler>
|
||||
|
||||
<initParams path="/update/**,/select">
|
||||
<lst name="defaults">
|
||||
<str name="df">_text_</str>
|
||||
</lst>
|
||||
</initParams>
|
||||
|
||||
<!-- Response Writers
|
||||
|
||||
http://wiki.apache.org/solr/QueryResponseWriter
|
||||
|
||||
Request responses will be written using the writer specified by
|
||||
the 'wt' request parameter matching the name of a registered
|
||||
writer.
|
||||
|
||||
The "default" writer is the default and will be used if 'wt' is
|
||||
not specified in the request.
|
||||
-->
|
||||
<queryResponseWriter name="xml"
|
||||
default="true"
|
||||
class="solr.XMLResponseWriter" />
|
||||
</config>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema name="dovecot-fts" version="2.0">
|
||||
<fieldType name="string" class="solr.StrField" omitNorms="true" sortMissingLast="true"/>
|
||||
<fieldType name="long" class="solr.LongPointField" positionIncrementGap="0"/>
|
||||
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
|
||||
|
||||
<fieldType name="text" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100">
|
||||
<analyzer type="index">
|
||||
<tokenizer class="solr.StandardTokenizerFactory"/>
|
||||
<filter class="solr.EdgeNGramFilterFactory" minGramSize="3" maxGramSize="20"/>
|
||||
<filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
|
||||
<filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
|
||||
<filter class="solr.FlattenGraphFilterFactory"/>
|
||||
<filter class="solr.LowerCaseFilterFactory"/>
|
||||
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
|
||||
<filter class="solr.PorterStemFilterFactory"/>
|
||||
</analyzer>
|
||||
<analyzer type="query">
|
||||
<tokenizer class="solr.StandardTokenizerFactory"/>
|
||||
<filter class="solr.SynonymGraphFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
|
||||
<filter class="solr.FlattenGraphFilterFactory"/>
|
||||
<filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
|
||||
<filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
|
||||
<filter class="solr.LowerCaseFilterFactory"/>
|
||||
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
|
||||
<filter class="solr.PorterStemFilterFactory"/>
|
||||
</analyzer>
|
||||
</fieldType>
|
||||
|
||||
<field name="id" type="string" indexed="true" required="true" stored="true"/>
|
||||
<field name="uid" type="long" indexed="true" required="true" stored="true"/>
|
||||
<field name="box" type="string" indexed="true" required="true" stored="true"/>
|
||||
<field name="user" type="string" indexed="true" required="true" stored="true"/>
|
||||
|
||||
<field name="hdr" type="text" indexed="true" stored="false"/>
|
||||
<field name="body" type="text" indexed="true" stored="false"/>
|
||||
|
||||
<field name="from" type="text" indexed="true" stored="false"/>
|
||||
<field name="to" type="text" indexed="true" stored="false"/>
|
||||
<field name="cc" type="text" indexed="true" stored="false"/>
|
||||
<field name="bcc" type="text" indexed="true" stored="false"/>
|
||||
<field name="subject" type="text" indexed="true" stored="false"/>
|
||||
|
||||
<!-- Used by Solr internally: -->
|
||||
<field name="_version_" type="long" indexed="true" stored="true"/>
|
||||
|
||||
<uniqueKey>id</uniqueKey>
|
||||
</schema>
|
|
@ -5,6 +5,8 @@ trap "kill 0" EXIT
|
|||
|
||||
# Prepare
|
||||
BACKGROUND_TASKS=()
|
||||
echo "Waiting for containers to settle..."
|
||||
sleep 10
|
||||
|
||||
if [[ "${USE_WATCHDOG}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
||||
echo -e "$(date) - USE_WATCHDOG=n, skipping watchdog..."
|
||||
|
@ -37,7 +39,7 @@ progress() {
|
|||
log_msg() {
|
||||
if [[ ${2} != "no_redis" ]]; then
|
||||
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \
|
||||
tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null
|
||||
tr '\r\n%&;$"_[]{}-' ' ')\"}" > /dev/null
|
||||
fi
|
||||
echo $(date) $(printf '%s\n' "${1}")
|
||||
}
|
||||
|
@ -115,7 +117,7 @@ nginx_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/nginx-mailcow
|
||||
touch /tmp/nginx-mailcow; echo "$(tail -50 /tmp/nginx-mailcow)" > /tmp/nginx-mailcow
|
||||
host_ip=$(get_container_ip nginx-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_http -4 -H ${host_ip} -u / -p 8081 2>> /tmp/nginx-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -140,7 +142,7 @@ unbound_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/unbound-mailcow
|
||||
touch /tmp/unbound-mailcow; echo "$(tail -50 /tmp/unbound-mailcow)" > /tmp/unbound-mailcow
|
||||
host_ip=$(get_container_ip unbound-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H stackoverflow.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -172,7 +174,7 @@ mysql_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/mysql-mailcow
|
||||
touch /tmp/mysql-mailcow; echo "$(tail -50 /tmp/mysql-mailcow)" > /tmp/mysql-mailcow
|
||||
host_ip=$(get_container_ip mysql-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_mysql -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -198,7 +200,7 @@ sogo_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/sogo-mailcow
|
||||
touch /tmp/sogo-mailcow; echo "$(tail -50 /tmp/sogo-mailcow)" > /tmp/sogo-mailcow
|
||||
host_ip=$(get_container_ip sogo-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_http -4 -H ${host_ip} -u /SOGo.index/ -p 20000 -R "SOGo\.MainUI" 2>> /tmp/sogo-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -223,7 +225,7 @@ postfix_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/postfix-mailcow
|
||||
touch /tmp/postfix-mailcow; echo "$(tail -50 /tmp/postfix-mailcow)" > /tmp/postfix-mailcow
|
||||
host_ip=$(get_container_ip postfix-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 589 -f "watchdog@invalid" -C "RCPT TO:null@localhost" -C DATA -C . -R 250 2>> /tmp/postfix-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -249,7 +251,7 @@ clamd_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/clamd-mailcow
|
||||
touch /tmp/clamd-mailcow; echo "$(tail -50 /tmp/clamd-mailcow)" > /tmp/clamd-mailcow
|
||||
host_ip=$(get_container_ip clamd-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_clamd -4 -H ${host_ip} 2>> /tmp/clamd-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -274,7 +276,7 @@ dovecot_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/dovecot-mailcow
|
||||
touch /tmp/dovecot-mailcow; echo "$(tail -50 /tmp/dovecot-mailcow)" > /tmp/dovecot-mailcow
|
||||
host_ip=$(get_container_ip dovecot-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 24 -f "watchdog@invalid" -C "RCPT TO:<watchdog@invalid>" -L -R "User doesn't exist" 2>> /tmp/dovecot-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -303,7 +305,7 @@ phpfpm_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/php-fpm-mailcow
|
||||
touch /tmp/php-fpm-mailcow; echo "$(tail -50 /tmp/php-fpm-mailcow)" > /tmp/php-fpm-mailcow
|
||||
host_ip=$(get_container_ip php-fpm-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
/usr/lib/nagios/plugins/check_tcp -H ${host_ip} -p 9001 2>> /tmp/php-fpm-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||
|
@ -350,6 +352,38 @@ ratelimit_checks() {
|
|||
return 1
|
||||
}
|
||||
|
||||
acme_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
THRESHOLD=1
|
||||
ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME)
|
||||
if [[ -z "${ACME_LOG_STATUS}" ]]; then
|
||||
redis-cli -h redis SET ACME_FAIL_TIME 0
|
||||
ACME_LOG_STATUS=0
|
||||
fi
|
||||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
ACME_LOG_STATUS_PREV=${ACME_LOG_STATUS}
|
||||
ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME)
|
||||
if [[ ${ACME_LOG_STATUS_PREV} != ${ACME_LOG_STATUS} ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
fi
|
||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||
progress "ACME" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||
if [[ $? == 10 ]]; then
|
||||
diff_c=0
|
||||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep $(( ( RANDOM % 30 ) + 10 ))
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ipv6nat_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
|
@ -358,10 +392,11 @@ ipv6nat_checks() {
|
|||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
IPV6NAT_CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\")) | .id")
|
||||
CONTAINERS=$(curl --silent --insecure https://dockerapi/containers/json)
|
||||
IPV6NAT_CONTAINER_ID=$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\")) | .id")
|
||||
if [[ ! -z ${IPV6NAT_CONTAINER_ID} ]]; then
|
||||
LATEST_STARTED="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
LATEST_IPV6NAT="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
LATEST_STARTED="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
LATEST_IPV6NAT="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
DIFFERENCE_START_TIME=$(expr ${LATEST_IPV6NAT} - ${LATEST_STARTED} 2>/dev/null)
|
||||
if [[ "${DIFFERENCE_START_TIME}" -lt 30 ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
|
@ -375,12 +410,13 @@ ipv6nat_checks() {
|
|||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep 3600
|
||||
sleep 300
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
rspamd_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
|
@ -388,15 +424,14 @@ rspamd_checks() {
|
|||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
cat /dev/null > /tmp/rspamd-mailcow
|
||||
touch /tmp/rspamd-mailcow; echo "$(tail -50 /tmp/rspamd-mailcow)" > /tmp/rspamd-mailcow
|
||||
host_ip=$(get_container_ip rspamd-mailcow)
|
||||
err_c_cur=${err_count}
|
||||
SCORE=$(/usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/scan -d '
|
||||
To: null@localhost
|
||||
SCORE=$(echo 'To: null@localhost
|
||||
From: watchdog@localhost
|
||||
|
||||
Empty
|
||||
' | jq -rc .required_score)
|
||||
' | usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/scan | jq -rc .required_score)
|
||||
if [[ ${SCORE} != "9999" ]]; then
|
||||
echo "Rspamd settings check failed" 2>> /tmp/rspamd-mailcow 1>&2
|
||||
err_count=$(( ${err_count} + 1))
|
||||
|
@ -517,11 +552,21 @@ done
|
|||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! acme_checks; then
|
||||
log_msg "ACME client hit error limit"
|
||||
echo acme-tiny > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! ipv6nat_checks; then
|
||||
log_msg "IPv6 NAT warning: ipv6nat container was not started at least 30s after siblings (not an error)"
|
||||
echo ipv6nat > /tmp/com_pipe
|
||||
log_msg "IPv6 NAT warning: ipv6nat-mailcow container was not started at least 30s after siblings (not an error)"
|
||||
echo ipv6nat-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
|
@ -561,10 +606,16 @@ while true; do
|
|||
CONTAINER_ID=
|
||||
HAS_INITDB=
|
||||
read com_pipe_answer </tmp/com_pipe
|
||||
if [ -s "/tmp/${com_pipe_answer}" ]; then
|
||||
cat "/tmp/${com_pipe_answer}"
|
||||
fi
|
||||
if [[ ${com_pipe_answer} == "ratelimit" ]]; then
|
||||
log_msg "At least one ratelimit was applied"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}" "No further information available."
|
||||
elif [[ ${com_pipe_answer} =~ .+-mailcow ]] || [[ ${com_pipe_answer} == "ipv6nat" ]]; then
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}" "Please see mailcow UI logs for further information."
|
||||
elif [[ ${com_pipe_answer} == "acme-tiny" ]]; then
|
||||
log_msg "acme-tiny client returned non-zero exit code"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}" "Please check acme-mailcow for ruther information."
|
||||
elif [[ ${com_pipe_answer} =~ .+-mailcow ]] || [[ ${com_pipe_answer} == "ipv6nat-mailcow" ]]; then
|
||||
kill -STOP ${BACKGROUND_TASKS[*]}
|
||||
sleep 3
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | .id")
|
||||
|
@ -581,7 +632,7 @@ while true; do
|
|||
else
|
||||
log_msg "Sending restart command to ${CONTAINER_ID}..."
|
||||
curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/restart
|
||||
if [[ ${com_pipe_answer} != "ipv6nat" ]]; then
|
||||
if [[ ${com_pipe_answer} != "ipv6nat-mailcow" ]]; then
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
|
||||
fi
|
||||
log_msg "Wait for restarted container to settle and continue watching..."
|
||||
|
|
|
@ -308,7 +308,7 @@ plugin {
|
|||
acl = vfile
|
||||
fts = solr
|
||||
fts_autoindex = yes
|
||||
fts_solr = url=http://solr:8983/solr/dovecot/
|
||||
fts_solr = url=http://solr:8983/solr/dovecot-fts/
|
||||
quota = dict:Userquota::proxy::sqlquota
|
||||
quota_rule2 = Trash:storage=+100%%
|
||||
sieve = /var/vmail/sieve/%u.sieve
|
||||
|
@ -328,7 +328,8 @@ plugin {
|
|||
quota_warning = storage=95%% quota-warning 95 %u
|
||||
quota_warning2 = storage=80%% quota-warning 80 %u
|
||||
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
|
||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute +vacation-seconds
|
||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
|
||||
sieve_extensions = +notify +imapflags +vacation-seconds
|
||||
sieve_max_script_size = 1M
|
||||
sieve_max_redirects = 30
|
||||
sieve_quota_max_scripts = 0
|
||||
|
@ -383,9 +384,10 @@ service stats {
|
|||
}
|
||||
}
|
||||
imap_max_line_length = 2 M
|
||||
auth_cache_verify_password_with_worker = yes
|
||||
auth_cache_negative_ttl = 0
|
||||
auth_cache_ttl = 30 s
|
||||
auth_cache_size = 2 M
|
||||
#auth_cache_verify_password_with_worker = yes
|
||||
#auth_cache_negative_ttl = 0
|
||||
#auth_cache_ttl = 30 s
|
||||
#auth_cache_size = 2 M
|
||||
!include_try /usr/local/etc/dovecot/extra.conf
|
||||
!include_try /usr/local/etc/dovecot/sogo-sso.conf
|
||||
default_client_limit = 10400
|
||||
|
|
|
@ -34,6 +34,7 @@ server {
|
|||
|
||||
client_max_body_size 0;
|
||||
|
||||
listen 127.0.0.1:65510;
|
||||
include /etc/nginx/conf.d/listen_plain.active;
|
||||
include /etc/nginx/conf.d/listen_ssl.active;
|
||||
include /etc/nginx/conf.d/server_name.active;
|
||||
|
@ -142,7 +143,19 @@ server {
|
|||
try_files /autoconfig.php =404;
|
||||
}
|
||||
|
||||
# auth_request endpoint if ALLOW_ADMIN_EMAIL_LOGIN is set
|
||||
location /sogo-auth-verify {
|
||||
internal;
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_pass http://127.0.0.1:65510/sogo-auth;
|
||||
proxy_pass_request_body off;
|
||||
}
|
||||
|
||||
location ^~ /Microsoft-Server-ActiveSync {
|
||||
include /etc/nginx/conf.d/sogo_proxy_auth.active;
|
||||
include /etc/nginx/conf.d/sogo_eas.active;
|
||||
proxy_connect_timeout 4000;
|
||||
proxy_next_upstream timeout error;
|
||||
|
@ -165,6 +178,7 @@ server {
|
|||
}
|
||||
|
||||
location ^~ /SOGo {
|
||||
include /etc/nginx/conf.d/sogo_proxy_auth.active;
|
||||
include /etc/nginx/conf.d/sogo.active;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
|
||||
echo 'auth_request /sogo-auth-verify;
|
||||
auth_request_set $user $upstream_http_x_user;
|
||||
auth_request_set $auth $upstream_http_x_auth;
|
||||
auth_request_set $auth_type $upstream_http_x_auth_type;
|
||||
proxy_set_header x-webobjects-remote-user "$user";
|
||||
proxy_set_header Authorization "$auth";
|
||||
proxy_set_header x-webobjects-auth-type "$auth_type";
|
||||
'
|
||||
fi
|
|
@ -0,0 +1 @@
|
|||
/localhost$/ local:
|
|
@ -94,12 +94,16 @@ 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
|
||||
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
|
||||
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||
smtp_tls_protocols = !SSLv2, !SSLv3
|
||||
lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
lmtp_tls_protocols = !SSLv2, !SSLv2, !SSLv3
|
||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
|
||||
lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||
lmtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||
|
||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||
smtpd_tls_protocols = !SSLv2, !SSLv3
|
||||
|
||||
smtpd_tls_security_level = may
|
||||
tls_preempt_cipherlist = yes
|
||||
tls_ssl_options = NO_COMPRESSION
|
||||
|
@ -134,5 +138,5 @@ smtp_sasl_mechanism_filter = plain, login
|
|||
smtp_tls_policy_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf
|
||||
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
|
||||
mail_name = Postcow
|
||||
transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
|
||||
transport_maps = pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
|
||||
smtp_sasl_auth_soft_bounce = no
|
||||
|
|
|
@ -2,14 +2,17 @@ smtp inet n - n - 1 postscreen
|
|||
smtpd pass - - n - - smtpd
|
||||
-o smtpd_helo_restrictions=permit_mynetworks,reject_non_fqdn_helo_hostname
|
||||
-o smtpd_sasl_auth_enable=no
|
||||
-o smtpd_sender_restrictions=permit_mynetworks,reject_unlisted_sender,reject_unknown_sender_domain
|
||||
smtps inet n - n - - smtpd
|
||||
-o smtpd_tls_wrappermode=yes
|
||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||
-o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
|
||||
-o tls_preempt_cipherlist=yes
|
||||
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=!SSLv2,!SSLv3
|
||||
-o tls_preempt_cipherlist=yes
|
||||
588 inet n - n - - smtpd
|
||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||
|
|
|
@ -6,6 +6,8 @@ then any of these will trigger the rule. If a rule is triggered then no more rul
|
|||
*/
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
// Getting headers sent by the client.
|
||||
//$headers = apache_request_headers();
|
||||
|
||||
ini_set('error_reporting', 0);
|
||||
|
||||
|
@ -25,6 +27,23 @@ catch (PDOException $e) {
|
|||
exit;
|
||||
}
|
||||
|
||||
// Check if db changed and return header
|
||||
/*$stmt = $pdo->prepare("SELECT UNIX_TIMESTAMP(UPDATE_TIME) AS `db_update_time` FROM information_schema.tables
|
||||
WHERE `TABLE_NAME` = 'filterconf'
|
||||
AND TABLE_SCHEMA = :dbname;");
|
||||
$stmt->execute(array(
|
||||
':dbname' => $database_name
|
||||
));
|
||||
$db_update_time = $stmt->fetch(PDO::FETCH_ASSOC)['db_update_time'];
|
||||
|
||||
if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $db_update_time)) {
|
||||
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 304);
|
||||
exit;
|
||||
} else {
|
||||
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 200);
|
||||
}
|
||||
*/
|
||||
|
||||
function parse_email($email) {
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
|
||||
$a = strrpos($email, '@');
|
||||
|
@ -107,8 +126,8 @@ function ucl_rcpts($object, $type) {
|
|||
settings {
|
||||
watchdog {
|
||||
priority = 10;
|
||||
rcpt = "/null@localhost/i";
|
||||
from = "/watchdog@localhost/i";
|
||||
rcpt_mime = "/null@localhost/i";
|
||||
from_mime = "/watchdog@localhost/i";
|
||||
apply "default" {
|
||||
actions {
|
||||
reject = 9999.0;
|
||||
|
@ -179,7 +198,7 @@ foreach (wl_by_sogo() as $user => $contacts) {
|
|||
}
|
||||
?>
|
||||
apply "default" {
|
||||
SOGO_CONTACT = -999.0;
|
||||
SOGO_CONTACT = -99.0;
|
||||
}
|
||||
symbols [
|
||||
"SOGO_CONTACT"
|
||||
|
@ -199,12 +218,13 @@ while ($row = array_shift($rows)) {
|
|||
?>
|
||||
whitelist_<?=$username_sane;?> {
|
||||
<?php
|
||||
$list_items = array();
|
||||
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
AND `option` = 'whitelist_from'");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($item = array_shift($list_items)) {
|
||||
foreach ($list_items as $item) {
|
||||
?>
|
||||
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
|
||||
<?php
|
||||
|
@ -237,24 +257,13 @@ while ($row = array_shift($rows)) {
|
|||
"MAILCOW_WHITE"
|
||||
]
|
||||
}
|
||||
whitelist_header_<?=$username_sane;?> {
|
||||
whitelist_mime_<?=$username_sane;?> {
|
||||
<?php
|
||||
$header_from = array();
|
||||
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
AND `option` = 'whitelist_from'");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($list_items as $item) {
|
||||
?>
|
||||
header = {
|
||||
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
|
||||
<?php
|
||||
while ($item = array_shift($list_items)) {
|
||||
$header_from[] = str_replace('\*', '.*', preg_quote($item['value'], '/'));
|
||||
}
|
||||
?>
|
||||
"From" = "/(<?=implode('|', $header_from);?>)/i";
|
||||
}
|
||||
<?php
|
||||
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
|
||||
?>
|
||||
priority = 5;
|
||||
|
@ -297,13 +306,13 @@ while ($row = array_shift($rows)) {
|
|||
?>
|
||||
blacklist_<?=$username_sane;?> {
|
||||
<?php
|
||||
$items[] = array();
|
||||
$list_items = array();
|
||||
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
AND `option` = 'blacklist_from'");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($item = array_shift($list_items)) {
|
||||
foreach ($list_items as $item) {
|
||||
?>
|
||||
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
|
||||
<?php
|
||||
|
@ -338,22 +347,11 @@ while ($row = array_shift($rows)) {
|
|||
}
|
||||
blacklist_header_<?=$username_sane;?> {
|
||||
<?php
|
||||
$header_from = array();
|
||||
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
AND `option` = 'blacklist_from'");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($list_items as $item) {
|
||||
?>
|
||||
header = {
|
||||
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
|
||||
<?php
|
||||
while ($item = array_shift($list_items)) {
|
||||
$header_from[] = str_replace('\*', '.*', preg_quote($item['value'], '/'));
|
||||
}
|
||||
?>
|
||||
"From" = "/(<?=implode('|', $header_from);?>)/i";
|
||||
}
|
||||
<?php
|
||||
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
|
||||
?>
|
||||
priority = 5;
|
||||
|
@ -425,4 +423,4 @@ while ($row = array_shift($rows)) {
|
|||
<?php
|
||||
}
|
||||
?>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# rspamd.conf.local
|
||||
|
||||
worker "fuzzy" {
|
||||
# Socket to listen on (UDP and TCP from rspamd 1.3)
|
||||
bind_socket = "*:11445";
|
||||
allow_update = ["127.0.0.1", "::1"];
|
||||
# Number of processes to serve this storage (useful for read scaling)
|
||||
count = 2;
|
||||
# Backend ("sqlite" or "redis" - default "sqlite")
|
||||
backend = "redis";
|
||||
# Hashes storage time (3 months)
|
||||
expire = 90d;
|
||||
# Synchronize updates to the storage each minute
|
||||
sync = 1min;
|
||||
}
|
||||
|
|
@ -84,6 +84,9 @@ $rcpt_final_mailboxes = array();
|
|||
|
||||
// Loop through all rcpts
|
||||
foreach (json_decode($rcpts, true) as $rcpt) {
|
||||
// Remove tag
|
||||
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
|
||||
|
||||
// Break rcpt into local part and domain part
|
||||
$parsed_rcpt = parse_email($rcpt);
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Socket to listen on (UDP and TCP from rspamd 1.3)
|
||||
bind_socket = "*:11445";
|
||||
allow_update = ["127.0.0.1", "::1"];
|
||||
# Number of processes to serve this storage (useful for read scaling)
|
||||
count = 2;
|
||||
# Backend ("sqlite" or "redis" - default "sqlite")
|
||||
backend = "redis";
|
||||
# Hashes storage time (3 months)
|
||||
expire = 90d;
|
||||
# Synchronize updates to the storage each minute
|
||||
sync = 1min;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
bind_socket = "rspamd:9900";
|
||||
milter = true;
|
||||
upstream {
|
||||
upstream "local" {
|
||||
name = "localhost";
|
||||
default = true;
|
||||
hosts = "rspamd:11333"
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
// (domain3.tld, domain2.tld)
|
||||
// );
|
||||
|
||||
SOGoIMAPServer = "imap://dovecot:143/?tls=YES";
|
||||
SOGoSieveServer = "sieve://dovecot:4190/?tls=YES";
|
||||
SOGoSMTPServer = "postfix:588";
|
||||
WOPort = "0.0.0.0:20000";
|
||||
|
|
|
@ -746,6 +746,7 @@ $tfa_data = get_tfa();
|
|||
<div id="active_settings_map" class="collapse" >
|
||||
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control textarea-code" rows="20" name="settings_map" readonly><?=file_get_contents('http://nginx:8081/settings.php');?></textarea>
|
||||
</div>
|
||||
<br>
|
||||
<?php $rsettings = rsettings('get'); ?>
|
||||
<form class="form" data-id="rsettings" role="form" method="post">
|
||||
<div class="row">
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
/*! =======================================================
|
||||
VERSION 10.6.0
|
||||
VERSION 10.6.1
|
||||
========================================================= */
|
||||
/*! =========================================================
|
||||
* bootstrap-slider.js
|
||||
|
|
|
@ -36,4 +36,7 @@ table.footable>tbody>tr.footable-empty>td {
|
|||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
body {
|
||||
overflow-y:scroll;
|
||||
}
|
|
@ -521,7 +521,8 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<br /><span id="quotaBadge" class="badge">max. <?=intval($result['max_new_quota'] / 1048576)?> MiB</span>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="quota" style="width:100%" min="1" max="<?=intval($result['max_new_quota'] / 1048576);?>" value="<?=intval($result['quota']) / 1048576;?>" class="form-control">
|
||||
<input type="number" name="quota" style="width:100%" min="0" max="<?=intval($result['max_new_quota'] / 1048576);?>" value="<?=intval($result['quota']) / 1048576;?>" class="form-control">
|
||||
<small class="help-block">0 = ∞</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
header('Content-Type: text/plain');
|
||||
if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||
exit();
|
||||
}
|
||||
|
||||
if (isset($_GET['token']) && ctype_alnum($_GET['token'])) {
|
||||
echo $tfa->getQRCodeImageAsDataUri($_SESSION['mailcow_cc_username'], $_GET['token']);
|
||||
}
|
||||
|
||||
?>
|
|
@ -58,6 +58,9 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
|
|||
)
|
||||
);
|
||||
$mail->SMTPDebug = 3;
|
||||
if ($port == 465) {
|
||||
$mail->SMTPSecure = "ssl";
|
||||
}
|
||||
$mail->Debugoutput = function($str, $level) {
|
||||
foreach(preg_split("/((\r?\n)|(\r\n?)|\n)/", $str) as $line){
|
||||
if (empty($line)) { continue; }
|
||||
|
|
|
@ -26,6 +26,10 @@ $(window).load(function() {
|
|||
$(".overlay").hide();
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$(document).on('shown.bs.modal', function(e) {
|
||||
modal_id = $(e.relatedTarget).data('target');
|
||||
$(modal_id).attr("aria-hidden","false");
|
||||
});
|
||||
// TFA, CSRF, Alerts in footer.inc.php
|
||||
// Other general functions in mailcow.js
|
||||
<?php
|
||||
|
@ -93,6 +97,15 @@ $(document).ready(function() {
|
|||
}
|
||||
if ($(this).val() == "totp") {
|
||||
$('#TOTPModal').modal('show');
|
||||
request_token = $('#tfa-qr-img').data('totp-secret');
|
||||
$.ajax({
|
||||
url: '/inc/ajax/qr_gen.php',
|
||||
data: {
|
||||
token: request_token,
|
||||
},
|
||||
}).done(function (result) {
|
||||
$("#tfa-qr-img").attr("src", result);
|
||||
});
|
||||
$("option:selected").prop("selected", false);
|
||||
}
|
||||
if ($(this).val() == "u2f") {
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
<?php
|
||||
function isset_has_content($var) {
|
||||
if (isset($var) && $var != "") {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function hash_password($password) {
|
||||
$salt_str = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str);
|
||||
|
@ -1477,7 +1485,7 @@ function solr_status() {
|
|||
$endpoint = 'http://solr:8983/solr/admin/cores';
|
||||
$params = array(
|
||||
'action' => 'STATUS',
|
||||
'core' => 'dovecot',
|
||||
'core' => 'dovecot-fts',
|
||||
'indexInfo' => 'true'
|
||||
);
|
||||
$url = $endpoint . '?' . http_build_query($params);
|
||||
|
@ -1494,7 +1502,7 @@ function solr_status() {
|
|||
else {
|
||||
curl_close($curl);
|
||||
$status = json_decode($response, true);
|
||||
return (!empty($status['status']['dovecot'])) ? $status['status']['dovecot'] : false;
|
||||
return (!empty($status['status']['dovecot-fts'])) ? $status['status']['dovecot-fts'] : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -561,7 +561,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('is_alias_or_mailbox', htmlspecialchars($address))
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain`= :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)");
|
||||
|
@ -573,7 +573,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('domain_not_found', htmlspecialchars($domain))
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `spamalias`
|
||||
WHERE `address`= :address");
|
||||
|
@ -585,7 +585,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('is_spam_alias', htmlspecialchars($address))
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
|
||||
$_SESSION['return'][] = array(
|
||||
|
@ -593,7 +593,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'alias_invalid'
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
|
@ -601,7 +601,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `public_comment`, `private_comment`, `goto`, `domain`, `active`)
|
||||
VALUES (:address, :public_comment, :private_comment, :goto, :domain, :active)");
|
||||
|
@ -755,8 +755,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
}
|
||||
$password = $_data['password'];
|
||||
$password2 = $_data['password2'];
|
||||
$name = $_data['name'];
|
||||
$quota_m = filter_var($_data['quota'], FILTER_SANITIZE_NUMBER_FLOAT);
|
||||
$name = ltrim(rtrim($_data['name'], '>'), '<');
|
||||
$quota_m = intval($_data['quota']);
|
||||
if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['quarantine_notification'] != "1") && $quota_m === 0) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'unlimited_quota_acl'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($name)) {
|
||||
$name = $local_part;
|
||||
}
|
||||
|
@ -844,14 +852,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
);
|
||||
return false;
|
||||
}
|
||||
if (!is_numeric($quota_m) || $quota_m == "0") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'quota_not_0_not_numeric'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!empty($password) && !empty($password2)) {
|
||||
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
|
||||
$_SESSION['return'][] = array(
|
||||
|
@ -1993,9 +1993,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
(int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']);
|
||||
(int)$sogo_access = (isset($_data['sogo_access'])) ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']);
|
||||
$name = (!empty($_data['name'])) ? $_data['name'] : $is_now['name'];
|
||||
(int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
|
||||
$name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
|
||||
$domain = $is_now['domain'];
|
||||
$quota_m = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['quota'] / 1048576);
|
||||
$quota_b = $quota_m * 1048576;
|
||||
$password = (!empty($_data['password'])) ? $_data['password'] : null;
|
||||
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
|
||||
|
@ -2008,6 +2008,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
);
|
||||
continue;
|
||||
}
|
||||
// if already 0 == ok
|
||||
if ((!isset($_SESSION['acl']['unlimited_quota']) || $_SESSION['acl']['unlimited_quota'] != "1") && ($quota_m == 0 && $is_now['quota'] != 0)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'unlimited_quota_acl'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `quota`, `maxquota`
|
||||
FROM `domain`
|
||||
WHERE `domain` = :domain");
|
||||
|
@ -2021,14 +2030,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
);
|
||||
continue;
|
||||
}
|
||||
if (!is_numeric($quota_m) || $quota_m == "0") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('quota_not_0_not_numeric', htmlspecialchars($quota_m))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if ($quota_m > $DomainData['maxquota']) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
|
@ -3016,15 +3017,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
$mailboxdata['quota'] = $row['quota'];
|
||||
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
|
||||
$mailboxdata['quota_used'] = intval($row['bytes']);
|
||||
$mailboxdata['percent_in_use'] = round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||
$mailboxdata['messages'] = $row['messages'];
|
||||
$mailboxdata['spam_aliases'] = $SpamaliasUsage['sa_count'];
|
||||
if ($mailboxdata['percent_in_use'] >= 90) {
|
||||
$mailboxdata['percent_class'] = "danger";
|
||||
if ($mailboxdata['percent_in_use'] === '- ') {
|
||||
$mailboxdata['percent_class'] = "info";
|
||||
}
|
||||
elseif ($mailboxdata['percent_in_use'] >= 75) {
|
||||
$mailboxdata['percent_class'] = "warning";
|
||||
}
|
||||
elseif ($mailboxdata['percent_in_use'] >= 90) {
|
||||
$mailboxdata['percent_class'] = "danger";
|
||||
}
|
||||
else {
|
||||
$mailboxdata['percent_class'] = "success";
|
||||
}
|
||||
|
@ -3525,7 +3529,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
}
|
||||
if (strtolower(getenv('SKIP_SOLR')) == 'n') {
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, 'http://solr:8983/solr/dovecot/update?commit=true');
|
||||
curl_setopt($curl, CURLOPT_URL, 'http://solr:8983/solr/dovecot-fts/update?commit=true');
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: text/xml'));
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
|
@ -3714,7 +3718,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox'))) {
|
||||
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource'))) {
|
||||
update_sogo_static_view();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
|||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "27012019_1217";
|
||||
$db_version = "30032019_1905";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
@ -464,6 +464,7 @@ function init_db_schema() {
|
|||
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"ratelimit" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"unlimited_quota" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"alias_domains" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
),
|
||||
"keys" => array(
|
||||
|
|
|
@ -36,7 +36,8 @@ foreach ($css_dir as $css_file) {
|
|||
|
||||
// U2F API + T/HOTP API
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']);
|
||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL);
|
||||
$qrprovider = new RobThree\Auth\Providers\Qr\QRServerProvider();
|
||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
||||
|
||||
// Redis
|
||||
$redis = new Redis();
|
||||
|
|
|
@ -75,6 +75,24 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
|||
// Update session cookie
|
||||
// setcookie(session_name() ,session_id(), time() + $SESSION_LIFETIME);
|
||||
|
||||
// Handle logouts
|
||||
if (isset($_POST["logout"])) {
|
||||
if (isset($_SESSION["dual-login"])) {
|
||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
header("Location: /mailbox");
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
session_regenerate_id(true);
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
header("Location: /");
|
||||
}
|
||||
}
|
||||
|
||||
// Check session
|
||||
function session_check() {
|
||||
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
|
||||
|
@ -106,21 +124,3 @@ if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
|
|||
$_POST = array();
|
||||
$_FILES = array();
|
||||
}
|
||||
|
||||
// Handle logouts
|
||||
if (isset($_POST["logout"])) {
|
||||
if (isset($_SESSION["dual-login"])) {
|
||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
header("Location: /mailbox");
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
session_regenerate_id(true);
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
header("Location: /");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ $MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'] = false;
|
|||
// Force password change on next login (only allows login to mailcow UI)
|
||||
$MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'] = false;
|
||||
|
||||
// Force password change on next login (only allows login to mailcow UI)
|
||||
// Enable SOGo access (set to false to disable access by default)
|
||||
$MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true;
|
||||
|
||||
// Send notification when quarantine is not empty (never, hourly, daily, weekly)
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,13 @@
|
|||
// Base64 functions
|
||||
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
|
||||
|
||||
jQuery(function($){
|
||||
acl_data = JSON.parse(acl);
|
||||
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
||||
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
||||
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||
|
||||
function draw_quarantine_table() {
|
||||
ft_quarantinetable = FooTable.init('#quarantinetable', {
|
||||
"columns": [
|
||||
|
@ -56,54 +58,52 @@ jQuery(function($){
|
|||
"empty": lang.empty,
|
||||
"paging": {"enabled": true,"limit": 5,"size": pagination_size},
|
||||
"sorting": {"enabled": true},
|
||||
"on": {
|
||||
"ready.ft.table": btn_group_quarantine,
|
||||
"after.ft.paging": btn_group_quarantine
|
||||
},
|
||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||
});
|
||||
}
|
||||
|
||||
btn_group_quarantine = function(ev, ft){
|
||||
$('.show_qid_info').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
var qitem = $(this).data('item');
|
||||
$('#qidDetailModal').modal('show');
|
||||
$( "#qid_error" ).hide();
|
||||
$.ajax({
|
||||
url: '/inc/ajax/qitem_details.php',
|
||||
data: { id: qitem },
|
||||
dataType: 'json',
|
||||
success: function(data){
|
||||
if (typeof data.error !== 'undefined') {
|
||||
$( "#qid_error" ).text(data.error);
|
||||
$( "#qid_error" ).show();
|
||||
}
|
||||
$( "li" ).each(function( index ) {
|
||||
console.log( index + ": " + $( this ).text() );
|
||||
});
|
||||
$('[data-id="qitems_single"]').each(function( index ) {
|
||||
$(this).attr("data-item", qitem);
|
||||
});
|
||||
$('#qid_detail_subj').text(data.subject);
|
||||
$('#qid_detail_text').text(data.text_plain);
|
||||
$('#qid_detail_text_from_html').text(data.text_html);
|
||||
if (typeof data.attachments !== 'undefined') {
|
||||
$( "#qid_detail_atts" ).text('');
|
||||
$.each(data.attachments, function( index, value ) {
|
||||
$( "#qid_detail_atts" ).append(
|
||||
'<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>'
|
||||
);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$( "#qid_detail_atts" ).text('-');
|
||||
}
|
||||
$('body').on('click', '.show_qid_info', function (e) {
|
||||
e.preventDefault();
|
||||
var qitem = $(this).data('item');
|
||||
var qError = $("#qid_error");
|
||||
|
||||
$('#qidDetailModal').modal('show');
|
||||
qError.hide();
|
||||
|
||||
$.ajax({
|
||||
url: '/inc/ajax/qitem_details.php',
|
||||
data: { id: qitem },
|
||||
dataType: 'json',
|
||||
success: function(data){
|
||||
if (typeof data.error !== 'undefined') {
|
||||
qError.text(data.error);
|
||||
qError.show();
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
$('[data-id="qitems_single"]').each(function(index) {
|
||||
$(this).attr("data-item", qitem);
|
||||
});
|
||||
|
||||
$('#qid_detail_subj').text(data.subject);
|
||||
$('#qid_detail_text').text(data.text_plain);
|
||||
$('#qid_detail_text_from_html').text(data.text_html);
|
||||
|
||||
if (typeof data.attachments !== 'undefined') {
|
||||
qAtts = $("#qid_detail_atts");
|
||||
qAtts.text('');
|
||||
$.each(data.attachments, function(index, value) {
|
||||
qAtts.append(
|
||||
'<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>'
|
||||
);
|
||||
});
|
||||
}
|
||||
else {
|
||||
qAtts.text('-');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Initial table drawings
|
||||
draw_quarantine_table();
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ $lang['footer']['cancel'] = 'Abbrechen';
|
|||
$lang['footer']['hibp_nok'] = 'Übereinstimmung gefunden! Dieses Passwort ist potentiell gefährlich!';
|
||||
$lang['footer']['hibp_ok'] = 'Keine Übereinstimmung gefunden.';
|
||||
|
||||
$lang['danger']['unlimited_quota_acl'] = "Unendliche Quota untersagt durch ACL";
|
||||
$lang['danger']['mysql_error'] = "MySQL Fehler: %s";
|
||||
$lang['danger']['redis_error'] = "Redis Fehler: %s";
|
||||
$lang['danger']['unknown_tfa_method'] = "Unbekannte TFA Methode";
|
||||
|
@ -38,7 +39,7 @@ $lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI Passwort muss mindestens 6 Z
|
|||
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI Passwort wurde gesetzt";
|
||||
$lang['success']['queue_command_success'] = "Queue-Aufgabe erfolgreich ausgeführt";
|
||||
$lang['danger']['unknown'] = "Ein unbekannter Fehler trat auf";
|
||||
$lang['danger']['malformed_username'] = "Benutzername hat falsches Format";
|
||||
$lang['danger']['malformed_username'] = "Benutzername hat ein falsches Format";
|
||||
$lang['info']['awaiting_tfa_confirmation'] = "Warte auf TFA Verifizierung";
|
||||
$lang['success']['logged_in_as'] = "Eingeloggt als %s";
|
||||
$lang['danger']['login_failed'] = "Anmeldung fehlgeschlagen";
|
||||
|
@ -49,7 +50,7 @@ $lang['danger']['sieve_error'] = "Sieve Parser: %s";
|
|||
$lang['danger']['value_missing'] = "Bitte alle Felder ausfüllen";
|
||||
$lang['danger']['filter_type'] = "Falscher Filtertyp";
|
||||
$lang['danger']['domain_cannot_match_hostname'] = "Domain darf nicht dem Hostnamen entsprechen";
|
||||
$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt aber SOGo konnte nicht neugestartet werden";
|
||||
$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt, aber SOGo konnte nicht neugestartet werden";
|
||||
$lang['danger']['rl_timeframe'] = "Ratelimit Zeitraum ist inkorrekt";
|
||||
$lang['success']['rl_saved'] = "Ratelimit für Objekt %s wurde gesetzt";
|
||||
$lang['success']['acl_saved'] = "ACL für Objekt %s wurde gesetzt";
|
||||
|
@ -87,7 +88,7 @@ $lang['success']['item_deleted'] = "Objekt %s wurde entfernt";
|
|||
$lang['danger']['alias_empty'] = 'Alias-Adresse darf nicht leer sein';
|
||||
$lang['danger']['goto_empty'] = 'Ziel-Adresse darf nicht leer sein';
|
||||
$lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existiert bereits';
|
||||
$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
|
||||
$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ein ungültiges Format';
|
||||
$lang['danger']['alias_invalid'] = 'Alias-Adresse %s ist ungültig';
|
||||
$lang['danger']['goto_invalid'] = 'Ziel-Adresse %s ist ungültig';
|
||||
$lang['danger']['last_key'] = 'Letzter Key kann nicht gelöscht werden';
|
||||
|
@ -104,7 +105,7 @@ $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden ges
|
|||
$lang['success']['mailbox_modified'] = 'Änderungen an Mailbox %s wurden gespeichert';
|
||||
$lang['success']['resource_modified'] = "Änderungen an Ressource %s wurden gespeichert";
|
||||
$lang['success']['object_modified'] = "Änderungen an Objekt %s wurden gespeichert";
|
||||
$lang['success']['f2b_modified'] = "Änderungen an Fail2ban Parametern wurden gespeichert";
|
||||
$lang['success']['f2b_modified'] = "Änderungen an Fail2ban-Parametern wurden gespeichert";
|
||||
$lang['danger']['targetd_not_found'] = 'Ziel-Domain %s nicht gefunden';
|
||||
$lang['success']['aliasd_added'] = 'Alias-Domain %s wurde angelegt';
|
||||
$lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
|
||||
|
@ -139,8 +140,8 @@ $lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt';
|
|||
$lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt';
|
||||
$lang['success']['admin_removed'] = 'Administrator %s wurde entfernt';
|
||||
$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
|
||||
$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
|
||||
$lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
|
||||
$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurde zurückgesetzt";
|
||||
$lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wurde zurückgesetzt";
|
||||
$lang['success']['resource_removed'] = 'Ressource %s wurde entfernt';
|
||||
$lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen';
|
||||
$lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren';
|
||||
|
@ -171,7 +172,7 @@ $lang['user']['new_password_description'] = 'Mindestanforderung: 6 Zeichen lang,
|
|||
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
|
||||
$lang['user']['alias'] = 'Alias';
|
||||
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen';
|
||||
$lang['user']['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.';
|
||||
$lang['user']['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.';
|
||||
$lang['user']['direct_aliases'] = 'Direkte Alias-Adressen';
|
||||
$lang['user']['direct_aliases_desc'] = 'Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.';
|
||||
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
|
||||
|
@ -200,7 +201,7 @@ $lang['user']['spamfilter_bl_desc'] = 'Für E-Mail-Adressen, die vom Spamfilter
|
|||
$lang['user']['spamfilter_table_rule'] = 'Regel';
|
||||
$lang['user']['spamfilter_table_action'] = 'Aktion';
|
||||
$lang['user']['spamfilter_table_empty'] = 'Keine Einträge vorhanden';
|
||||
$lang['user']['spamfilter_table_remove'] = 'entfernen';
|
||||
$lang['user']['spamfilter_table_remove'] = 'Entfernen';
|
||||
$lang['user']['spamfilter_table_add'] = 'Eintrag hinzufügen';
|
||||
$lang['user']['spamfilter_behavior'] = 'Bewertung';
|
||||
$lang['user']['spamfilter_green'] = 'Grün: Die Nachricht ist kein Spam';
|
||||
|
@ -247,7 +248,7 @@ $lang['user']['edit'] = 'Bearbeiten';
|
|||
$lang['user']['remove'] = 'Entfernen';
|
||||
$lang['user']['create_syncjob'] = 'Neuen Sync-Job erstellen';
|
||||
|
||||
$lang['start']['mailcow_apps_detail'] = 'Verwenden Sie mailcow Apps, um E-Mails abzurufen, Kalender- und Kontakte zu verwalten und vieles mehr.';
|
||||
$lang['start']['mailcow_apps_detail'] = 'Verwenden Sie mailcow Apps, um E-Mails abzurufen, Kalender und Kontakte zu verwalten und vieles mehr.';
|
||||
$lang['start']['mailcow_panel_detail'] = '<b>Domain-Administratoren</b> erstellen, verändern oder löschen Mailboxen, verwalten die Domäne und sehen sonstige Einstellungen ein.<br>
|
||||
Als <b>Mailbox-Benutzer</b> erstellen Sie hier zeitlich limitierte Aliasse, ändern das Verhalten des Spamfilters, setzen ein neues Passwort und vieles mehr.';
|
||||
$lang['start']['imap_smtp_server_auth_info'] = 'Bitte verwenden Sie Ihre vollständige E-Mail-Adresse sowie das PLAIN-Authentifizierungsverfahren.<br>
|
||||
|
@ -390,7 +391,7 @@ $lang['add']['comment_info'] = 'Ein privater Kommentar ist für den Benutzer nic
|
|||
|
||||
$lang['acl']['spam_alias'] = 'Temporäre E-Mail Aliasse';
|
||||
$lang['acl']['tls_policy'] = 'Verschlüsselungsrichtlinie';
|
||||
$lang['acl']['spam_score'] = 'Spam Bewertung';
|
||||
$lang['acl']['spam_score'] = 'Spam-Bewertung';
|
||||
$lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
|
||||
$lang['acl']['delimiter_action'] = 'Delimiter Aktionen (tags)';
|
||||
$lang['acl']['syncjobs'] = 'Sync Jobs';
|
||||
|
@ -405,6 +406,7 @@ $lang['acl']['bcc_maps'] = 'BCC Maps';
|
|||
$lang['acl']['filters'] = 'Filter';
|
||||
$lang['acl']['ratelimit'] = 'Rate limit';
|
||||
$lang['acl']['recipient_maps'] = 'Empfängerumschreibungen';
|
||||
$lang['acl']['unlimited_quota'] = 'Unendliche Quota für Mailboxen';
|
||||
$lang['acl']['prohibited'] = 'Untersagt durch Richtlinie';
|
||||
|
||||
$lang['mailbox']['quarantine_notification'] = 'Quarantäne-Benachrichtigung';
|
||||
|
@ -466,7 +468,7 @@ $lang['add']['select'] = 'Bitte auswählen';
|
|||
$lang['add']['target_domain'] = 'Ziel-Domain';
|
||||
$lang['add']['kind'] = 'Art';
|
||||
$lang['add']['mailbox_username'] = 'Benutzername (linker Teil der E-Mail-Adresse)';
|
||||
$lang['add']['full_name'] = 'Vor- und Zuname';
|
||||
$lang['add']['full_name'] = 'Vor- und Nachname';
|
||||
$lang['add']['quota_mb'] = 'Speicherplatz (MiB)';
|
||||
$lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen';
|
||||
$lang['add']['password'] = 'Passwort';
|
||||
|
@ -526,12 +528,12 @@ $lang['admin']['import'] = 'Importieren';
|
|||
$lang['admin']['duplicate'] = 'Duplizieren';
|
||||
$lang['admin']['import_private_key'] = 'Private Key importieren';
|
||||
$lang['admin']['duplicate_dkim'] = 'DKIM duplizieren';
|
||||
$lang['admin']['f2b_parameters'] = 'Fail2ban Parameter';
|
||||
$lang['admin']['f2b_ban_time'] = 'Banzeit (s)';
|
||||
$lang['admin']['f2b_parameters'] = 'Fail2ban-Parameter';
|
||||
$lang['admin']['f2b_ban_time'] = 'Bannzeit (s)';
|
||||
$lang['admin']['f2b_max_attempts'] = 'Max. Versuche';
|
||||
$lang['admin']['f2b_retry_window'] = 'Wiederholungen im Zeitraum von (s)';
|
||||
$lang['admin']['f2b_netban_ipv4'] = 'Netzbereich für IPv4 Bans (8-32)';
|
||||
$lang['admin']['f2b_netban_ipv6'] = 'Netzbereich für IPv6 Bans (8-128)';
|
||||
$lang['admin']['f2b_netban_ipv4'] = 'Netzbereich für IPv4-Bans (8-32)';
|
||||
$lang['admin']['f2b_netban_ipv6'] = 'Netzbereich für IPv6-Bans (8-128)';
|
||||
$lang['admin']['f2b_whitelist'] = 'Whitelist für Netzwerke und Hosts';
|
||||
$lang['admin']['r_inactive'] = 'Inaktive Restriktionen';
|
||||
$lang['admin']['r_active'] = 'Aktive Restriktionen';
|
||||
|
@ -607,11 +609,11 @@ $lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den
|
|||
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
|
||||
$lang['admin']['relayhosts_hint'] = 'Erstellen Sie senderabhängige Transporte, um diese im Einstellungsdialog einer Domain auszuwählen.<br>
|
||||
Der Transporttyp lautet immer "smtp:". Benutzereinstellungen bezüglich Verschlüsselungsrichtlinie werden beim Transport berücksichtigt.';
|
||||
$lang['admin']['transports_hint'] = 'Transport Maps <b>überwiegen</b> senderabhängige Transport Maps und ignorieren die individuellen Einstellungen eines Benutzers bezüglich Verschlüsselungsrichtlinie, da der Absender bei Ermittlung der Transportregel nicht berücksichtigt wird.<br>
|
||||
Der Transport erfolgt immer via "smtp:".<br>
|
||||
Ein Eintrag in der TLS Policy Map kann eine Verschlüsselung erzwingen.<br>
|
||||
Die Authentifizierung wird anhand des Host Parameters ermittelt, hierbei würde bei einem beispielhaften Next Hop "[host]:25" immer zuerst "host" abfragt und <b>erst im Anschluss</b> "[host]:25".<br>
|
||||
Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art "host" sowie "[host]:25" aus.';
|
||||
$lang['admin']['transports_hint'] = '→ Transport Maps <b>überwiegen</b> senderabhängige Transport Maps.
|
||||
→ Transport Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.<br>
|
||||
→ Der Transport erfolgt immer via "smtp:".<br>
|
||||
→ Adressen, die mit "/localhost$/" übereinstimmen, werden immer via "local:" transportiert, daher sind sie von einer Zieldefinition "*" ausgeschlossen.<br>
|
||||
→ Die Authentifizierung wird anhand des "Next hop" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert "[host]:25" immer zuerst "host" abfragt und <b>erst im Anschluss</b> "[host]:25". Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art "host" sowie "[host]:25" aus.';
|
||||
$lang['admin']['add_relayhost_hint'] = 'Bitte beachten Sie, dass Anmeldedaten unverschlüsselt gespeichert werden.<br>
|
||||
Angelegte Transporte dieser Art sind <b>senderabhängig</b> und müssen erst einer Domain zugewiesen werden, bevor sie als Transport verwendet werden.<br>
|
||||
Diese Einstellungen entsprechen demach <i>nicht</i> dem "relayhost" Parameter in Postfix.';
|
||||
|
@ -769,8 +771,8 @@ $lang['mailbox']['bcc_type'] = "BCC Typ";
|
|||
$lang['mailbox']['bcc_sender_map'] = "Senderabhängig";
|
||||
$lang['mailbox']['bcc_rcpt_map'] = "Empfängerabhängig";
|
||||
$lang['mailbox']['bcc_local_dest'] = "Lokales Ziel";
|
||||
$lang['mailbox']['bcc_destinations'] = "BCC Ziel";
|
||||
$lang['mailbox']['bcc_destination'] = "BCC Ziel";
|
||||
$lang['mailbox']['bcc_destinations'] = "BCC-Ziel";
|
||||
$lang['mailbox']['bcc_destination'] = "BCC-Ziel";
|
||||
$lang['edit']['bcc_dest_format'] = 'BCC-Ziel muss eine gültige E-Mail-Adresse sein.';
|
||||
|
||||
$lang['mailbox']['bcc'] = "BCC";
|
||||
|
@ -809,7 +811,7 @@ $lang['oauth2']['authorize_app'] = 'Anwendung authorisieren';
|
|||
$lang['oauth2']['deny'] = 'Ablehnen';
|
||||
$lang['oauth2']['access_denied'] = 'Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.';
|
||||
|
||||
$lang['admin']['sys_mails'] = 'System E-Mails';
|
||||
$lang['admin']['sys_mails'] = 'System-E-Mails';
|
||||
$lang['admin']['subject'] = 'Betreff';
|
||||
$lang['admin']['from'] = 'Absender';
|
||||
$lang['admin']['include_exclude'] = 'Ein- und Ausschlüsse';
|
||||
|
|
|
@ -20,6 +20,7 @@ $lang['footer']['cancel'] = 'Cancel';
|
|||
$lang['footer']['hibp_nok'] = 'Matched! This is a potentially dangerous password!';
|
||||
$lang['footer']['hibp_ok'] = 'No match found.';
|
||||
|
||||
$lang['danger']['unlimited_quota_acl'] = "Unlimited quota prohibited by ACL";
|
||||
$lang['danger']['mysql_error'] = "MySQL error: %s";
|
||||
$lang['danger']['redis_error'] = "Redis error: %s";
|
||||
$lang['danger']['unknown_tfa_method'] = "Unknown TFA method";
|
||||
|
@ -418,6 +419,7 @@ $lang['acl']['bcc_maps'] = 'BCC maps';
|
|||
$lang['acl']['filters'] = 'Filters';
|
||||
$lang['acl']['ratelimit'] = 'Rate limit';
|
||||
$lang['acl']['recipient_maps'] = 'Recipient maps';
|
||||
$lang['acl']['unlimited_quota'] = 'Unlimited quota for mailboxes';
|
||||
$lang['acl']['prohibited'] = 'Prohibited by ACL';
|
||||
|
||||
$lang['mailbox']['quarantine_notification'] = 'Quarantine notifications';
|
||||
|
@ -631,9 +633,11 @@ $lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally
|
|||
$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).';
|
||||
$lang['admin']['relayhosts_hint'] = 'Define sender-dependent transports to be able to select them in a domains configuration dialog.<br>
|
||||
The transport service is always "smtp:". A users individual outbound TLS policy setting is taken into account.';
|
||||
$lang['admin']['transports_hint'] = 'A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>
|
||||
Outbound TLS policy settings per-user are ignored and can only be enfored by TLS policy map entries. The transport service is always "smtp:".<br>
|
||||
To determine credentials for an exemplary next hop "[host]:25", Postfix <b>always</b> queries for "nexthop" before searching for "[nexthop]:25". This behavior makes it impossible to use "nexthop" and "[nexthop]:25" at the same time.';
|
||||
$lang['admin']['transports_hint'] = '→ A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>
|
||||
→ Outbound TLS policy settings per-user are ignored and can only be enfored by TLS policy map entries.<br>
|
||||
→ The transport service for defined transports is always "smtp:".<br>
|
||||
→ Adresses matching "/localhost$/" will always be transported via "local:", therefore a "*" destination will not apply to those addresses.<br>
|
||||
→ To determine credentials for an exemplary next hop "[host]:25", Postfix <b>always</b> queries for "host" before searching for "[host]:25". This behavior makes it impossible to use "host" and "[host]:25" at the same time.';
|
||||
$lang['admin']['add_relayhost_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
|
||||
$lang['admin']['add_transports_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
|
||||
$lang['admin']['host'] = 'Host';
|
||||
|
|
|
@ -348,6 +348,11 @@ $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false';
|
|||
echo "var role = '". $role . "';\n";
|
||||
echo "var is_dual = " . $is_dual . ";\n";
|
||||
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
|
||||
$ALLOW_ADMIN_EMAIL_LOGIN = (preg_match(
|
||||
"/^([yY][eE][sS]|[yY])+$/",
|
||||
$_ENV["ALLOW_ADMIN_EMAIL_LOGIN"]
|
||||
)) ? "true" : "false";
|
||||
echo "var ALLOW_ADMIN_EMAIL_LOGIN = " . $ALLOW_ADMIN_EMAIL_LOGIN . ";\n";
|
||||
?>
|
||||
</script>
|
||||
<?php
|
||||
|
|
|
@ -81,7 +81,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<ol>
|
||||
<li>
|
||||
<p><?=$lang['tfa']['scan_qr_code'];?></p>
|
||||
<img src="<?=$tfa->getQRCodeImageAsDataUri($_SESSION['mailcow_cc_username'], $totp_secret);?>">
|
||||
<img id="tfa-qr-img" data-totp-secret="<?=$totp_secret;?>" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
|
||||
<p class="help-block"><?=$lang['tfa']['enter_qr_code'];?>:<br />
|
||||
<code><?=$totp_secret;?></code>
|
||||
</p>
|
||||
|
|
|
@ -43,8 +43,8 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<br /><span id="quotaBadge" class="badge">max. - MiB</span>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="quota" min="1" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
|
||||
<small class="help-block">min. 1</small>
|
||||
<input type="text" class="form-control" name="quota" min="0" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
|
||||
<small class="help-block">0 = ∞</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -785,24 +785,3 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div>
|
||||
</div><!-- DNS info modal -->
|
||||
<script>
|
||||
$('#addResourceModal').on('shown.bs.modal', function() {
|
||||
$("#multiple_bookings").val($("#multiple_bookings_select").val());
|
||||
if ($("#multiple_bookings").val() == "custom") {
|
||||
$("#multiple_bookings_custom_div").show();
|
||||
$("#multiple_bookings").val($("#multiple_bookings_custom").val());
|
||||
}
|
||||
})
|
||||
$("#multiple_bookings_select").change(function() {
|
||||
$("#multiple_bookings").val($("#multiple_bookings_select").val());
|
||||
if ($("#multiple_bookings").val() == "custom") {
|
||||
$("#multiple_bookings_custom_div").show();
|
||||
}
|
||||
else {
|
||||
$("#multiple_bookings_custom_div").hide();
|
||||
}
|
||||
});
|
||||
$("#multiple_bookings_custom").bind ("change keypress keyup blur", function () {
|
||||
$("#multiple_bookings").val($("#multiple_bookings_custom").val());
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
$ALLOW_ADMIN_EMAIL_LOGIN = (preg_match(
|
||||
"/^([yY][eE][sS]|[yY])+$/",
|
||||
$_ENV["ALLOW_ADMIN_EMAIL_LOGIN"]
|
||||
));
|
||||
|
||||
$session_var_user_allowed = 'sogo-sso-user-allowed';
|
||||
$session_var_pass = 'sogo-sso-pass';
|
||||
|
||||
// prevent if feature is disabled
|
||||
if (!$ALLOW_ADMIN_EMAIL_LOGIN) {
|
||||
header('HTTP/1.0 403 Forbidden');
|
||||
echo "this feature is disabled";
|
||||
exit;
|
||||
}
|
||||
// validate credentials for basic auth requests
|
||||
elseif (isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
// load prerequisites only when required
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
$username = $_SERVER['PHP_AUTH_USER'];
|
||||
$password = $_SERVER['PHP_AUTH_PW'];
|
||||
$login_check = check_login($username, $password);
|
||||
if ($login_check === 'user') {
|
||||
header("X-User: $username");
|
||||
header("X-Auth: Basic ".base64_encode("$username:$password"));
|
||||
header("X-Auth-Type: Basic");
|
||||
exit;
|
||||
} else {
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo 'Invalid login';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
// check permissions and redirect for direct GET ?login=xy requests
|
||||
elseif (isset($_GET['login'])) {
|
||||
// load prerequisites only when required
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
// check permissions
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1") {
|
||||
$login = html_entity_decode(rawurldecode($_GET["login"]));
|
||||
if (filter_var($login, FILTER_VALIDATE_EMAIL)) {
|
||||
if (!empty(mailbox('get', 'mailbox_details', $login))) {
|
||||
// load master password
|
||||
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
|
||||
// register username and password in session
|
||||
$_SESSION[$session_var_user_allowed][] = $login;
|
||||
$_SESSION[$session_var_pass] = $sogo_sso_pass;
|
||||
// redirect to sogo (sogo will get the correct credentials via nginx auth_request
|
||||
header("Location: /SOGo/so/${login}");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
header('HTTP/1.0 403 Forbidden');
|
||||
exit;
|
||||
}
|
||||
// only check for admin-login on sogo GUI requests
|
||||
elseif (
|
||||
strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0
|
||||
) {
|
||||
// this is an nginx auth_request call, we check for existing sogo-sso session variables
|
||||
session_start();
|
||||
// extract email address from "/SOGo/so/user@domain/xy"
|
||||
$url_parts = explode("/", $_SERVER['HTTP_X_ORIGINAL_URI']);
|
||||
$email = $url_parts[3];
|
||||
// check if this email is in session allowed list
|
||||
if (
|
||||
!empty($email) &&
|
||||
filter_var($email, FILTER_VALIDATE_EMAIL) &&
|
||||
is_array($_SESSION[$session_var_user_allowed]) &&
|
||||
in_array($email, $_SESSION[$session_var_user_allowed])
|
||||
) {
|
||||
$username = $email;
|
||||
$password = $_SESSION[$session_var_pass];
|
||||
header("X-User: $username");
|
||||
header("X-Auth: Basic ".base64_encode("$username:$password"));
|
||||
header("X-Auth-Type: Basic");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// if username is empty, SOGo will use the normal login methods / login form
|
||||
header("X-User: ");
|
||||
header("X-Auth: ");
|
||||
header("X-Auth-Type: ");
|
|
@ -63,6 +63,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
||||
|
@ -92,227 +93,227 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
}
|
||||
?>
|
||||
<div class="container">
|
||||
<h3><?=$lang['user']['user_settings'];?></h3>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<?php if ($mailboxdata['attributes']['force_pw_update'] == "1"): ?>
|
||||
<div class="alert alert-danger"><?=$lang['user']['force_pw_update'];?></div>
|
||||
<?php endif; ?>
|
||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/#<?=$clientconfigstr;?>">[<?=$lang['user']['client_configuration'];?>]</a></p>
|
||||
<p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
|
||||
<p><small>
|
||||
<?php
|
||||
if ($_SESSION['mailcow_cc_last_login']['remote']):
|
||||
?>
|
||||
<span style="margin-right:10px" class="glyphicon glyphicon-log-in"></span> <span data-time="<?=$_SESSION['mailcow_cc_last_login']['time'];?>" class="last_login_date"></span> (<?=$_SESSION['mailcow_cc_last_login']['remote'];?>)
|
||||
<?php
|
||||
else: echo "Last login: -"; endif;
|
||||
?>
|
||||
</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#userSettings" aria-controls="userSettings" role="tab" data-toggle="tab"><?=$lang['user']['mailbox_details'];?></a></li>
|
||||
<li role="presentation"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
|
||||
<li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
|
||||
<li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<?php // Get user information about aliases
|
||||
$user_get_alias_details = user_get_alias_details($username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:
|
||||
<p class="small"><?=$lang['user']['direct_aliases_desc'];?></p>
|
||||
</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<?php
|
||||
if ($user_get_alias_details['direct_aliases'] === false) {
|
||||
echo '✘';
|
||||
}
|
||||
else {
|
||||
foreach (array_filter($user_get_alias_details['direct_aliases']) as $direct_alias => $direct_alias_meta) {
|
||||
(!empty($direct_alias_meta['public_comment'])) ?
|
||||
printf('%s — <span class="bg-info">%s</span><br>', $direct_alias, $direct_alias_meta['public_comment']) :
|
||||
printf('%s<br>', $direct_alias);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:
|
||||
<p class="small"><?=$lang['user']['shared_aliases_desc'];?></p>
|
||||
</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<?php
|
||||
if ($user_get_alias_details['shared_aliases'] === false) {
|
||||
echo '✘';
|
||||
}
|
||||
else {
|
||||
foreach (array_filter($user_get_alias_details['shared_aliases']) as $shared_alias => $shared_alias_meta) {
|
||||
(!empty($shared_alias_meta['public_comment'])) ?
|
||||
printf('%s — <span class="bg-info">%s</span><br>', $shared_alias, $shared_alias_meta['public_comment']) :
|
||||
|
||||
printf('%s<br>', $shared_alias);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : $user_get_alias_details['aliases_also_send_as'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_send_as_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$user_get_alias_details['aliases_send_as_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$user_get_alias_details['is_catch_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
|
||||
<div class="col-md-5 col-xs-7">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
|
||||
<?=$mailboxdata['percent_in_use'];?>%
|
||||
<div class="tab-content">
|
||||
|
||||
<div role="tabpanel" class="tab-pane active" id="userSettings">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<?php if ($mailboxdata['attributes']['force_pw_update'] == "1"): ?>
|
||||
<div class="alert alert-danger"><?=$lang['user']['force_pw_update'];?></div>
|
||||
<?php endif; ?>
|
||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||
<p><a target="_blank" href="https://mailcow.github.io/mailcow-dockerized-docs/client/#<?=$clientconfigstr;?>">[<?=$lang['user']['client_configuration'];?>]</a></p>
|
||||
<p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
|
||||
<p><small>
|
||||
<?php
|
||||
if ($_SESSION['mailcow_cc_last_login']['remote']):
|
||||
?>
|
||||
<span style="margin-right:10px" class="glyphicon glyphicon-log-in"></span> <span data-time="<?=$_SESSION['mailcow_cc_last_login']['time'];?>" class="last_login_date"></span> (<?=$_SESSION['mailcow_cc_last_login']['remote'];?>)
|
||||
<?php
|
||||
else: echo "Last login: -"; endif;
|
||||
?>
|
||||
</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<?php // Get user information about aliases
|
||||
$user_get_alias_details = user_get_alias_details($username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:
|
||||
<p class="small"><?=$lang['user']['direct_aliases_desc'];?></p>
|
||||
</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<?php
|
||||
if ($user_get_alias_details['direct_aliases'] === false) {
|
||||
echo '✘';
|
||||
}
|
||||
else {
|
||||
foreach (array_filter($user_get_alias_details['direct_aliases']) as $direct_alias => $direct_alias_meta) {
|
||||
(!empty($direct_alias_meta['public_comment'])) ?
|
||||
printf('%s — <span class="bg-info">%s</span><br>', $direct_alias, $direct_alias_meta['public_comment']) :
|
||||
printf('%s<br>', $direct_alias);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:
|
||||
<p class="small"><?=$lang['user']['shared_aliases_desc'];?></p>
|
||||
</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<?php
|
||||
if ($user_get_alias_details['shared_aliases'] === false) {
|
||||
echo '✘';
|
||||
}
|
||||
else {
|
||||
foreach (array_filter($user_get_alias_details['shared_aliases']) as $shared_alias => $shared_alias_meta) {
|
||||
(!empty($shared_alias_meta['public_comment'])) ?
|
||||
printf('%s — <span class="bg-info">%s</span><br>', $shared_alias, $shared_alias_meta['public_comment']) :
|
||||
|
||||
printf('%s<br>', $shared_alias);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : $user_get_alias_details['aliases_also_send_as'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_send_as_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$user_get_alias_details['aliases_send_as_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$user_get_alias_details['is_catch_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
|
||||
<div class="col-md-5 col-xs-7">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
|
||||
<?=$mailboxdata['percent_in_use'];?>%
|
||||
</div>
|
||||
</div>
|
||||
<p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=($mailboxdata['quota'] == 0) ? '∞' : formatBytes($mailboxdata['quota'], 2);?><br><?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<?php
|
||||
// Show tagging options
|
||||
$get_tagging_options = mailbox('get', 'delimiter_action', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['delimiter_action'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subfolder") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"subfolder"}'><?=$lang['user']['tag_in_subfolder'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subject") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"subject"}'><?=$lang['user']['tag_in_subject'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "none") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"none"}'><?=$lang['user']['tag_in_none'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['tag_help_explain'];?></p>
|
||||
<p class="help-block"><?=$lang['user']['tag_help_example'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Show TLS policy options
|
||||
$get_tls_policy = mailbox('get', 'tls_policy', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tls_policy'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['tls_policy'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_in'] == "1") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="tls_policy"
|
||||
data-api-url='edit/tls_policy'
|
||||
data-api-attr='{"tls_enforce_in":<?=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_in'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_out'] == "1") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="tls_policy"
|
||||
data-api-url='edit/tls_policy'
|
||||
data-api-attr='{"tls_enforce_out":<?=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_out'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Show quarantine_notification options
|
||||
$quarantine_notification = mailbox('get', 'quarantine_notification', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_notification'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_notification'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "never") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"never"}'><?=$lang['user']['never'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "hourly") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"hourly"}'><?=$lang['user']['hourly'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "daily") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"daily"}'><?=$lang['user']['daily'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "weekly") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"weekly"}'><?=$lang['user']['weekly'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['quarantine_notification_info'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['eas_reset'];?>" data-action="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="eas_cache" data-api-url='delete/eas_cache' href="#"><?=$lang['user']['eas_reset_now'];?></button>
|
||||
<p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['sogo_profile_reset'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['sogo_profile_reset'];?>" data-action="delete_selected" data-text="<?=$lang['user']['sogo_profile_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="sogo_profile" data-api-url='delete/sogo_profile' href="#"><?=$lang['user']['sogo_profile_reset_now'];?></button>
|
||||
<p class="help-block"><?=$lang['user']['sogo_profile_reset_help'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?>, <?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<?php
|
||||
// Show tagging options
|
||||
$get_tagging_options = mailbox('get', 'delimiter_action', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['delimiter_action'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subfolder") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"subfolder"}'><?=$lang['user']['tag_in_subfolder'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subject") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"subject"}'><?=$lang['user']['tag_in_subject'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "none") ? 'active' : null; ?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="delimiter_action"
|
||||
data-api-url='edit/delimiter_action'
|
||||
data-api-attr='{"tagged_mail_handler":"none"}'><?=$lang['user']['tag_in_none'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['tag_help_explain'];?></p>
|
||||
<p class="help-block"><?=$lang['user']['tag_help_example'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Show TLS policy options
|
||||
$get_tls_policy = mailbox('get', 'tls_policy', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tls_policy'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['tls_policy'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_in'] == "1") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="tls_policy"
|
||||
data-api-url='edit/tls_policy'
|
||||
data-api-attr='{"tls_enforce_in":<?=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_in'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_out'] == "1") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="tls_policy"
|
||||
data-api-url='edit/tls_policy'
|
||||
data-api-attr='{"tls_enforce_out":<?=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_out'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Show quarantine_notification options
|
||||
$quarantine_notification = mailbox('get', 'quarantine_notification', $username);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['quarantine_notification'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['quarantine_notification'];?>">
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "never") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"never"}'><?=$lang['user']['never'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "hourly") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"hourly"}'><?=$lang['user']['hourly'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "daily") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"daily"}'><?=$lang['user']['daily'];?></button>
|
||||
<button type="button" class="btn btn-sm btn-default <?=($quarantine_notification == "weekly") ? "active" : null;?>"
|
||||
data-action="edit_selected"
|
||||
data-item="<?= htmlentities($username); ?>"
|
||||
data-id="quarantine_notification"
|
||||
data-api-url='edit/quarantine_notification'
|
||||
data-api-attr='{"quarantine_notification":"weekly"}'><?=$lang['user']['weekly'];?></button>
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['user']['quarantine_notification_info'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['eas_reset'];?>" data-action="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="eas_cache" data-api-url='delete/eas_cache' href="#"><?=$lang['user']['eas_reset_now'];?></button>
|
||||
<p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['sogo_profile_reset'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['sogo_profile_reset'];?>" data-action="delete_selected" data-text="<?=$lang['user']['sogo_profile_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="sogo_profile" data-api-url='delete/sogo_profile' href="#"><?=$lang['user']['sogo_profile_reset_now'];?></button>
|
||||
<p class="help-block"><?=$lang['user']['sogo_profile_reset_help'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-pills nav-justified" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
|
||||
<li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
|
||||
<li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="SpamAliases">
|
||||
<div role="tabpanel" class="tab-pane" id="SpamAliases">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="table-responsive">
|
||||
|
@ -320,7 +321,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mass-actions-user">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_alias'];?>">
|
||||
<div class="btn-group">
|
||||
|
@ -348,7 +348,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="Spamfilter">
|
||||
|
@ -376,10 +375,8 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
<p><?=$lang['user']['spamfilter_hint'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
|
||||
</div>
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['spam_policy'];?>">
|
||||
<a data-acl="<?=$_SESSION['acl']['spam_score'];?>" type="button" class="btn btn-sm btn-success" data-action="edit_selected"
|
||||
|
@ -394,7 +391,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
data-api-attr='{"spam_score":"default"}'><?=$lang['user']['spam_score_reset'];?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<hr>
|
||||
<div class="row">
|
||||
|
@ -419,7 +415,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h4><?=$lang['user']['spamfilter_bl'];?></h4>
|
||||
|
@ -451,7 +446,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="sync_job_table"></table>
|
||||
</div>
|
||||
|
||||
<div class="mass-actions-user">
|
||||
<div class="btn-group" data-acl="<?=$_SESSION['acl']['syncjobs'];?>">
|
||||
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="syncjob" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
|
||||
|
@ -465,10 +459,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addSyncJobModal"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['create_syncjob'];?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div><!-- /container -->
|
||||
<div style="margin-bottom:200px;"></div>
|
||||
<?php
|
||||
|
|
|
@ -55,7 +55,7 @@ services:
|
|||
- redis
|
||||
|
||||
clamd-mailcow:
|
||||
image: mailcow/clamd:1.21
|
||||
image: mailcow/clamd:1.22
|
||||
build: ./data/Dockerfiles/clamd
|
||||
restart: always
|
||||
environment:
|
||||
|
@ -71,7 +71,7 @@ services:
|
|||
- clamd
|
||||
|
||||
rspamd-mailcow:
|
||||
image: mailcow/rspamd:1.34
|
||||
image: mailcow/rspamd:1.38
|
||||
build: ./data/Dockerfiles/rspamd
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
|
@ -94,7 +94,7 @@ services:
|
|||
- rspamd
|
||||
|
||||
php-fpm-mailcow:
|
||||
image: mailcow/phpfpm:1.34
|
||||
image: mailcow/phpfpm:1.36
|
||||
build: ./data/Dockerfiles/phpfpm
|
||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||
depends_on:
|
||||
|
@ -106,6 +106,7 @@ services:
|
|||
- mysql-socket-vol-1:/var/run/mysqld/
|
||||
- ./data/conf/sogo/:/etc/sogo/
|
||||
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
|
||||
- ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/
|
||||
- ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf
|
||||
- ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini
|
||||
- ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini
|
||||
|
@ -130,6 +131,7 @@ services:
|
|||
- API_ALLOW_FROM=${API_ALLOW_FROM:-invalid}
|
||||
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
|
||||
- SKIP_SOLR=${SKIP_SOLR:-y}
|
||||
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
|
||||
restart: always
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
|
@ -139,7 +141,7 @@ services:
|
|||
- phpfpm
|
||||
|
||||
sogo-mailcow:
|
||||
image: mailcow/sogo:1.51
|
||||
image: mailcow/sogo:1.54
|
||||
build: ./data/Dockerfiles/sogo
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
|
@ -149,11 +151,14 @@ services:
|
|||
- LOG_LINES=${LOG_LINES:-9999}
|
||||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||
- ACL_ANYONE=${ACL_ANYONE:-disallow}
|
||||
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
|
||||
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
|
||||
volumes:
|
||||
- ./data/conf/sogo/:/etc/sogo/
|
||||
- ./data/web/inc/init_db.inc.php:/init_db.inc.php
|
||||
- ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js
|
||||
- mysql-socket-vol-1:/var/run/mysqld/
|
||||
- sogo-web-vol-1:/sogo_web
|
||||
restart: always
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
|
@ -164,7 +169,7 @@ services:
|
|||
- sogo
|
||||
|
||||
dovecot-mailcow:
|
||||
image: mailcow/dovecot:1.62
|
||||
image: mailcow/dovecot:1.67
|
||||
build: ./data/Dockerfiles/dovecot
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
|
@ -172,6 +177,7 @@ services:
|
|||
- ./data/conf/dovecot:/usr/local/etc/dovecot
|
||||
- ./data/assets/ssl:/etc/ssl/mail/:ro
|
||||
- ./data/conf/sogo/:/etc/sogo/
|
||||
- ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/
|
||||
- vmail-vol-1:/var/vmail
|
||||
- vmail-attachments-vol-1:/var/attachments
|
||||
- crypt-vol-1:/mail_crypt/
|
||||
|
@ -185,9 +191,12 @@ services:
|
|||
- DBUSER=${DBUSER}
|
||||
- DBPASS=${DBPASS}
|
||||
- TZ=${TZ}
|
||||
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
|
||||
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
|
||||
- MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-1440}
|
||||
- ACL_ANYONE=${ACL_ANYONE:-disallow}
|
||||
- SKIP_SOLR=${SKIP_SOLR:-y}
|
||||
- MAILDIR_SUB=${MAILDIR_SUB:-}
|
||||
ports:
|
||||
- "${DOVEADM_PORT:-127.0.0.1:19991}:12345"
|
||||
- "${IMAP_PORT:-143}:143"
|
||||
|
@ -207,11 +216,12 @@ services:
|
|||
hostname: ${MAILCOW_HOSTNAME}
|
||||
networks:
|
||||
mailcow-network:
|
||||
ipv4_address: ${IPV4_NETWORK:-172.22.1}.250
|
||||
aliases:
|
||||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: mailcow/postfix:1.29
|
||||
image: mailcow/postfix:1.31
|
||||
build: ./data/Dockerfiles/postfix
|
||||
volumes:
|
||||
- ./data/conf/postfix:/opt/postfix/conf
|
||||
|
@ -262,6 +272,7 @@ services:
|
|||
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active &&
|
||||
envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
|
||||
envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active &&
|
||||
. /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active &&
|
||||
nginx -qt &&
|
||||
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
|
||||
until ping sogo -c1 > /dev/null; do sleep 1; done &&
|
||||
|
@ -274,14 +285,14 @@ services:
|
|||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
|
||||
- TZ=${TZ}
|
||||
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
|
||||
volumes:
|
||||
- ./data/web:/web:ro
|
||||
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
||||
- ./data/assets/ssl/:/etc/ssl/mail/:ro
|
||||
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
|
||||
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
|
||||
volumes_from:
|
||||
- sogo-mailcow
|
||||
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
|
||||
ports:
|
||||
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
||||
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
||||
|
@ -296,7 +307,7 @@ services:
|
|||
acme-mailcow:
|
||||
depends_on:
|
||||
- nginx-mailcow
|
||||
image: mailcow/acme:1.48
|
||||
image: mailcow/acme:1.51
|
||||
build: ./data/Dockerfiles/acme
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
|
@ -309,6 +320,7 @@ services:
|
|||
- DBPASS=${DBPASS}
|
||||
- SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n}
|
||||
- SKIP_IP_CHECK=${SKIP_IP_CHECK:-n}
|
||||
- SKIP_HTTP_VERIFICATION=${SKIP_HTTP_VERIFICATION:-n}
|
||||
- LE_STAGING=${LE_STAGING:-n}
|
||||
- TZ=${TZ}
|
||||
volumes:
|
||||
|
@ -323,7 +335,7 @@ services:
|
|||
- acme
|
||||
|
||||
netfilter-mailcow:
|
||||
image: mailcow/netfilter:1.22
|
||||
image: mailcow/netfilter:1.23
|
||||
build: ./data/Dockerfiles/netfilter
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
|
@ -347,7 +359,7 @@ services:
|
|||
- /lib/modules:/lib/modules:ro
|
||||
|
||||
watchdog-mailcow:
|
||||
image: mailcow/watchdog:1.34
|
||||
image: mailcow/watchdog:1.39
|
||||
# Debug
|
||||
#command: /watchdog.sh
|
||||
build: ./data/Dockerfiles/watchdog
|
||||
|
@ -395,11 +407,11 @@ services:
|
|||
- dockerapi
|
||||
|
||||
solr-mailcow:
|
||||
image: mailcow/solr:1.1
|
||||
image: mailcow/solr:1.4
|
||||
build: ./data/Dockerfiles/solr
|
||||
restart: always
|
||||
volumes:
|
||||
- solr-vol-1:/opt/solr/server/solr/dovecot/data
|
||||
- solr-vol-1:/opt/solr/server/solr/dovecot-fts/data
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
environment:
|
||||
|
@ -411,7 +423,7 @@ services:
|
|||
aliases:
|
||||
- solr
|
||||
|
||||
ipv6nat:
|
||||
ipv6nat-mailcow:
|
||||
depends_on:
|
||||
- unbound-mailcow
|
||||
- mysql-mailcow
|
||||
|
@ -440,6 +452,8 @@ services:
|
|||
networks:
|
||||
mailcow-network:
|
||||
driver: bridge
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: br-mailcow
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
driver: default
|
||||
|
@ -459,3 +473,4 @@ volumes:
|
|||
solr-vol-1:
|
||||
postfix-vol-1:
|
||||
crypt-vol-1:
|
||||
sogo-web-vol-1:
|
||||
|
|
|
@ -16,6 +16,7 @@ if [ -f mailcow.conf ]; then
|
|||
case $response in
|
||||
[yY][eE][sS]|[yY])
|
||||
mv mailcow.conf mailcow.conf_backup
|
||||
chmod 600 mailcow.conf_backup
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
|
@ -185,6 +186,10 @@ SKIP_LETS_ENCRYPT=n
|
|||
|
||||
SKIP_IP_CHECK=n
|
||||
|
||||
# Skip HTTP verification in ACME container - y/n
|
||||
|
||||
SKIP_HTTP_VERIFICATION=n
|
||||
|
||||
# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n
|
||||
|
||||
SKIP_CLAMD=${SKIP_CLAMD}
|
||||
|
@ -200,6 +205,10 @@ SOLR_HEAP=1024
|
|||
|
||||
USE_WATCHDOG=n
|
||||
|
||||
# Allow admins to log into SOGo as email user (without any password)
|
||||
|
||||
ALLOW_ADMIN_EMAIL_LOGIN=n
|
||||
|
||||
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
|
||||
# Can by multiple rcpts, NO quotation marks
|
||||
|
||||
|
@ -233,9 +242,14 @@ IPV6_NETWORK=fd4d:6169:6c63:6f77::/64
|
|||
#API_KEY=
|
||||
#API_ALLOW_FROM=127.0.0.1,1.2.3.4
|
||||
|
||||
# mail_home is ~/Maildir
|
||||
MAILDIR_SUB=Maildir
|
||||
|
||||
EOF
|
||||
|
||||
mkdir -p data/assets/ssl
|
||||
|
||||
chmod 600 mailcow.conf
|
||||
|
||||
# copy but don't overwrite existing certificate
|
||||
cp -n data/assets/ssl-example/*.pem data/assets/ssl/
|
||||
|
|
|
@ -76,12 +76,10 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
|
|||
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/latest-15.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
|
||||
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
||||
&& rm nextcloud.tar.bz2 \
|
||||
&& rm -rf ./data/web/nextcloud/updater \
|
||||
&& mkdir -p ./data/web/nextcloud/data \
|
||||
&& mkdir -p ./data/web/nextcloud/custom_apps \
|
||||
&& chmod +x ./data/web/nextcloud/occ
|
||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) bash -c "chown www-data:www-data -R /web/nextcloud"
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings upgrade"
|
||||
&& chmod +x ./data/web/nextcloud/occ \
|
||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) bash -c "chown www-data:www-data -R /web/nextcloud" \
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings upgrade"
|
||||
fi
|
||||
|
||||
elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||
|
@ -106,12 +104,10 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
|
|||
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/latest-15.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
|
||||
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
||||
&& rm nextcloud.tar.bz2 \
|
||||
&& rm -rf ./data/web/nextcloud/updater \
|
||||
&& mkdir -p ./data/web/nextcloud/data \
|
||||
&& mkdir -p ./data/web/nextcloud/custom_apps \
|
||||
&& chmod +x ./data/web/nextcloud/occ
|
||||
|
||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud/data /web/nextcloud/config /web/nextcloud/apps /web/nextcloud/custom_apps"
|
||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud/data /web/nextcloud/config /web/nextcloud/apps"
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ --no-warnings maintenance:install \
|
||||
--database mysql \
|
||||
--database-host mysql \
|
||||
|
|
27
update.sh
27
update.sh
|
@ -6,9 +6,12 @@ if [ "$(id -u)" -ne "0" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
#exit on error and pipefail
|
||||
# Exit on error and pipefail
|
||||
set -o pipefail
|
||||
|
||||
# Add /opt/bin to PATH
|
||||
PATH=$PATH:/opt/bin
|
||||
|
||||
umask 0022
|
||||
|
||||
for bin in curl docker-compose docker git awk sha1sum; do
|
||||
|
@ -68,8 +71,12 @@ while (($#)); do
|
|||
case "${1}" in
|
||||
--check|-c)
|
||||
echo "Checking remote code for updates..."
|
||||
git fetch origin #${BRANCH}
|
||||
if [[ -z $(git log HEAD --pretty=format:"%H" | grep $(git rev-parse origin/${BRANCH})) ]]; then
|
||||
LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "A problem occurred while trying to fetch the latest revision from github."
|
||||
exit 99
|
||||
fi
|
||||
if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
|
||||
echo "Updated code is available."
|
||||
exit 0
|
||||
else
|
||||
|
@ -98,6 +105,7 @@ while (($#)); do
|
|||
done
|
||||
|
||||
[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
|
||||
chmod 600 mailcow.conf
|
||||
source mailcow.conf
|
||||
DOTS=${MAILCOW_HOSTNAME//[^.]};
|
||||
if [ ${#DOTS} -lt 2 ]; then
|
||||
|
@ -127,9 +135,12 @@ CONFIG_ARRAY=(
|
|||
"API_KEY"
|
||||
"API_ALLOW_FROM"
|
||||
"MAILDIR_GC_TIME"
|
||||
"MAILDIR_SUB"
|
||||
"ACL_ANYONE"
|
||||
"SOLR_HEAP"
|
||||
"SKIP_SOLR"
|
||||
"ALLOW_ADMIN_EMAIL_LOGIN"
|
||||
"SKIP_HTTP_VERIFICATION"
|
||||
)
|
||||
|
||||
sed -i '$a\' mailcow.conf
|
||||
|
@ -235,6 +246,13 @@ for option in ${CONFIG_ARRAY[@]}; do
|
|||
echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
|
||||
echo "SKIP_SOLR=y" >> mailcow.conf
|
||||
fi
|
||||
elif [[ ${option} == "MAILDIR_SUB" ]]; then
|
||||
if ! grep -q ${option} mailcow.conf; then
|
||||
echo "Adding new option \"${option}\" to mailcow.conf"
|
||||
echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
|
||||
echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
|
||||
echo "MAILDIR_SUB=" >> mailcow.conf
|
||||
fi
|
||||
elif ! grep -q ${option} mailcow.conf; then
|
||||
echo "Adding new option \"${option}\" to mailcow.conf"
|
||||
echo "${option}=n" >> mailcow.conf
|
||||
|
@ -351,9 +369,8 @@ if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then
|
|||
read -p "Press any key to continue..." < /dev/tty
|
||||
fi
|
||||
|
||||
echo -e "Fixing project name... "
|
||||
# Checking for old project name bug
|
||||
sed -i 's#COMPOSEPROJECT_NAME#COMPOSE_PROJECT_NAME#g' mailcow.conf
|
||||
sed -i '/COMPOSE_PROJECT_NAME=/s/-//g' mailcow.conf
|
||||
|
||||
echo -e "Fixing PHP-FPM worker ports for Nginx sites..."
|
||||
sed -i 's#phpfpm:9000#phpfpm:9002#g' data/conf/nginx/*.conf
|
||||
|
|
Loading…
Reference in New Issue