commit
079170682c
|
@ -17,8 +17,9 @@ data/conf/rspamd/local.d/*
|
||||||
data/conf/rspamd/override.d/*
|
data/conf/rspamd/override.d/*
|
||||||
!data/conf/nginx/dynmaps.conf
|
!data/conf/nginx/dynmaps.conf
|
||||||
!data/conf/nginx/site.conf
|
!data/conf/nginx/site.conf
|
||||||
|
!data/conf/nginx/meta_exporter.conf
|
||||||
data/conf/nginx/*.conf
|
data/conf/nginx/*.conf
|
||||||
data/conf/nginx/*.custom
|
data/conf/nginx/*.custom
|
||||||
data/conf/nginx/*.bak
|
data/conf/nginx/*.bak
|
||||||
data/conf/dovecot/extra.conf
|
data/conf/dovecot/extra.conf
|
||||||
data/conf/rspamd/custom/*
|
data/conf/rspamd/override.d/worker-controller-password.inc
|
||||||
|
|
|
@ -9,7 +9,7 @@ script:
|
||||||
- docker-compose push
|
- docker-compose push
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- dev
|
- master
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: MpxpTwD7f0CNEVLitSpVmocK7O9r+BwFE1deEHK4AlQo/oc9cOlhGe1EL3mx9zbglPmjlDg/8kMUGv6vSirIabfBo9Szjps76bHckFr9lr2Ykkg0e29oC8pgPpSXD1eY/1ZIN/FvIkxpUFLETo1okS/j9q/A0DCGFmti0n3EoMORsgRz9CpNAiEh0zpSd6+euPAGHuczuCrDuO84my9bIOCjA/+aPunHNeXiuM8yIM2SxCSyGtIKT0+jvquIvLF58VxivysXBlRfhDn8fhB09nXA2Ru/derYQACfcmNSn9Pd4bDpebPJW5B9H/XA8xjb58uKinUlncbAMB/QnxoT75j9YRWJZRSQ+34XNYP6ZgK9soZ2TC6djQyEKTUu45Kp/1s+poSn42m9jytJJTmmK0KxsZTRcC8JD5nrjIMZWPUNNTwC5L4+I7ZRWg2WooK3LNyq1Ng8Hn6W77wSgsvAJw2HD3Lx58AprGUhHuBeaIZRuSN9aKwZrl9vKQJLqPnOp/nF2EC6kot5HYYtcotGtETXPUDih21gWD5ZM2BqVqYfQQnJnNMgeYmMdj6QQuTFqhuNJf7hXRIRkTnD3j1gDOLKQZazW0+N2JE8XWDFwi6fKScDsxT85lJti9HmzHa7+k4RVHmUYuDgRoPuzUgjWHvPsiz3/Z8WQ9JYpH84S8w=
|
- secure: MpxpTwD7f0CNEVLitSpVmocK7O9r+BwFE1deEHK4AlQo/oc9cOlhGe1EL3mx9zbglPmjlDg/8kMUGv6vSirIabfBo9Szjps76bHckFr9lr2Ykkg0e29oC8pgPpSXD1eY/1ZIN/FvIkxpUFLETo1okS/j9q/A0DCGFmti0n3EoMORsgRz9CpNAiEh0zpSd6+euPAGHuczuCrDuO84my9bIOCjA/+aPunHNeXiuM8yIM2SxCSyGtIKT0+jvquIvLF58VxivysXBlRfhDn8fhB09nXA2Ru/derYQACfcmNSn9Pd4bDpebPJW5B9H/XA8xjb58uKinUlncbAMB/QnxoT75j9YRWJZRSQ+34XNYP6ZgK9soZ2TC6djQyEKTUu45Kp/1s+poSn42m9jytJJTmmK0KxsZTRcC8JD5nrjIMZWPUNNTwC5L4+I7ZRWg2WooK3LNyq1Ng8Hn6W77wSgsvAJw2HD3Lx58AprGUhHuBeaIZRuSN9aKwZrl9vKQJLqPnOp/nF2EC6kot5HYYtcotGtETXPUDih21gWD5ZM2BqVqYfQQnJnNMgeYmMdj6QQuTFqhuNJf7hXRIRkTnD3j1gDOLKQZazW0+N2JE8XWDFwi6fKScDsxT85lJti9HmzHa7+k4RVHmUYuDgRoPuzUgjWHvPsiz3/Z8WQ9JYpH84S8w=
|
||||||
|
|
|
@ -3,20 +3,21 @@ FROM alpine:3.6
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
RUN apk add --update --no-cache \
|
RUN apk add --update --no-cache \
|
||||||
bash \
|
bash \
|
||||||
curl \
|
curl \
|
||||||
openssl \
|
openssl \
|
||||||
bind-tools \
|
bind-tools \
|
||||||
jq \
|
jq \
|
||||||
libressl-dev \
|
libressl-dev \
|
||||||
libbsd-dev \
|
libbsd-dev \
|
||||||
libseccomp-dev \
|
libseccomp-dev \
|
||||||
mariadb-client \
|
mariadb-client \
|
||||||
tini \
|
tini \
|
||||||
make \
|
make \
|
||||||
gcc \
|
gcc \
|
||||||
libressl \
|
libressl \
|
||||||
libc-dev \
|
libc-dev \
|
||||||
|
redis \
|
||||||
linux-headers \
|
linux-headers \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
&& curl -s https://kristaps.bsd.lv/acme-client/snapshots/acme-client-portable.tgz | tar xfvz - \
|
&& curl -s https://kristaps.bsd.lv/acme-client/snapshots/acme-client-portable.tgz | tar xfvz - \
|
||||||
|
|
|
@ -2,16 +2,30 @@
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
exec 5>&1
|
exec 5>&1
|
||||||
|
|
||||||
|
log_f() {
|
||||||
|
if [[ ${2} == "no_nl" ]]; then
|
||||||
|
echo -n "$(date) - ${1}"
|
||||||
|
elif [[ ${2} == "no_date" ]]; then
|
||||||
|
echo "${1}"
|
||||||
|
elif [[ ${2} != "redis_only" ]]; then
|
||||||
|
echo "$(date) - ${1}"
|
||||||
|
fi
|
||||||
|
redis-cli -h redis LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \
|
||||||
|
tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null
|
||||||
|
redis-cli -h redis LTRIM ACME_LOG 0 ${LOG_LINES} > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
log_f "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..."
|
log_f "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..."
|
||||||
sleep 365d
|
sleep 365d
|
||||||
exec $(readlink -f "$0")
|
exec $(readlink -f "$0")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Waiting for Docker API..."
|
log_f "Waiting for Docker API..." no_nl
|
||||||
until ping dockerapi -c1 > /dev/null; do
|
until ping dockerapi -c1 > /dev/null; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
log_f "Found Docker API" no_date
|
||||||
|
|
||||||
ACME_BASE=/var/lib/acme
|
ACME_BASE=/var/lib/acme
|
||||||
SSL_EXAMPLE=/var/lib/ssl-example
|
SSL_EXAMPLE=/var/lib/ssl-example
|
||||||
|
@ -20,21 +34,12 @@ mkdir -p ${ACME_BASE}/acme/private
|
||||||
|
|
||||||
restart_containers(){
|
restart_containers(){
|
||||||
for container in $*; do
|
for container in $*; do
|
||||||
echo "Restarting ${container}..."
|
log_f "Restarting ${container}..." no_nl
|
||||||
curl -X POST http://dockerapi:8080/containers/${container}/restart
|
C_REST_OUT=$(curl -X POST http://dockerapi:8080/containers/${container}/restart | jq -r '.msg')
|
||||||
|
log_f "${C_REST_OUT}" no_date
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
log_f() {
|
|
||||||
if [[ ${2} == "no_nl" ]]; then
|
|
||||||
echo -n "$(date) - ${1}"
|
|
||||||
elif [[ ${2} == "no_date" ]]; then
|
|
||||||
echo "${1}"
|
|
||||||
else
|
|
||||||
echo "$(date) - ${1}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
array_diff() {
|
array_diff() {
|
||||||
# https://stackoverflow.com/questions/2312762, Alex Offshore
|
# https://stackoverflow.com/questions/2312762, Alex Offshore
|
||||||
eval local ARR1=\(\"\${$2[@]}\"\)
|
eval local ARR1=\(\"\${$2[@]}\"\)
|
||||||
|
@ -127,12 +132,13 @@ while true; do
|
||||||
# Container ids may have changed
|
# Container ids may have changed
|
||||||
CONTAINERS_RESTART=($(curl --silent http://dockerapi:8080/containers/json | jq -r '.[] | {name: .Config.Labels["com.docker.compose.service"], id: .Id}' | jq -rc 'select( .name | tostring | contains("nginx-mailcow") or contains("postfix-mailcow") or contains("dovecot-mailcow")) | .id' | tr "\n" " "))
|
CONTAINERS_RESTART=($(curl --silent http://dockerapi:8080/containers/json | jq -r '.[] | {name: .Config.Labels["com.docker.compose.service"], id: .Id}' | jq -rc 'select( .name | tostring | contains("nginx-mailcow") or contains("postfix-mailcow") or contains("dovecot-mailcow")) | .id' | tr "\n" " "))
|
||||||
|
|
||||||
log_f "Waiting for domain tables... " no_nl
|
log_f "Waiting for domain table... " no_nl
|
||||||
while [[ -z ${DOMAIN_TABLE} ]]; do
|
while [[ -z ${DOMAIN_TABLE} ]]; do
|
||||||
|
curl --silent http://nginx/ >/dev/null 2>&1
|
||||||
DOMAIN_TABLE=$(mysql -h mysql-mailcow -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
|
DOMAIN_TABLE=$(mysql -h mysql-mailcow -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
|
||||||
[[ -z ${DOMAIN_TABLE} ]] && sleep 10
|
[[ -z ${DOMAIN_TABLE} ]] && sleep 10
|
||||||
done
|
done
|
||||||
log_f "OK" no_date
|
log_f "Found domain tables." no_date
|
||||||
|
|
||||||
while read domains; do
|
while read domains; do
|
||||||
SQL_DOMAIN_ARR+=("${domains}")
|
SQL_DOMAIN_ARR+=("${domains}")
|
||||||
|
@ -226,6 +232,7 @@ while true; do
|
||||||
|
|
||||||
case "$?" in
|
case "$?" in
|
||||||
0) # new certs
|
0) # new certs
|
||||||
|
log_f "${ACME_RESPONSE}" redis_only
|
||||||
# cp the new certificates and keys
|
# cp the new certificates and keys
|
||||||
cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
|
cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
|
||||||
cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
|
cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
|
||||||
|
@ -239,6 +246,7 @@ while true; do
|
||||||
restart_containers ${CONTAINERS_RESTART[*]}
|
restart_containers ${CONTAINERS_RESTART[*]}
|
||||||
;;
|
;;
|
||||||
1) # failure
|
1) # failure
|
||||||
|
log_f "${ACME_RESPONSE}" redis_only
|
||||||
if [[ $ACME_RESPONSE =~ "No registration exists" ]]; then
|
if [[ $ACME_RESPONSE =~ "No registration exists" ]]; then
|
||||||
log_f "Registration keys are invalid, deleting old keys and restarting..."
|
log_f "Registration keys are invalid, deleting old keys and restarting..."
|
||||||
rm ${ACME_BASE}/acme/private/account.key
|
rm ${ACME_BASE}/acme/private/account.key
|
||||||
|
@ -268,6 +276,7 @@ while true; do
|
||||||
exec $(readlink -f "$0")
|
exec $(readlink -f "$0")
|
||||||
;;
|
;;
|
||||||
2) # no change
|
2) # no change
|
||||||
|
log_f "${ACME_RESPONSE}" redis_only
|
||||||
if ! diff ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem; then
|
if ! diff ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem; then
|
||||||
log_f "Certificate was not changed, but active certificate does not match the verified certificate, fixing and restarting containers..."
|
log_f "Certificate was not changed, but active certificate does not match the verified certificate, fixing and restarting containers..."
|
||||||
cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
|
cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem
|
||||||
|
@ -280,9 +289,11 @@ while true; do
|
||||||
cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
|
cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem
|
||||||
TRIGGER_RESTART=1
|
TRIGGER_RESTART=1
|
||||||
fi
|
fi
|
||||||
|
log_f "Certificate was not changed"
|
||||||
[[ ${TRIGGER_RESTART} == 1 ]] && restart_containers ${CONTAINERS_RESTART[*]}
|
[[ ${TRIGGER_RESTART} == 1 ]] && restart_containers ${CONTAINERS_RESTART[*]}
|
||||||
;;
|
;;
|
||||||
*) # unspecified
|
*) # unspecified
|
||||||
|
log_f "${ACME_RESPONSE}" redis_only
|
||||||
if [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ]] && [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ]]; then
|
if [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ]] && [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ]]; then
|
||||||
log_f "Error requesting certificate, restoring previous certificate from backup and restarting containers...."
|
log_f "Error requesting certificate, restoring previous certificate from backup and restarting containers...."
|
||||||
cp ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ${ACME_BASE}/cert.pem
|
cp ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ${ACME_BASE}/cert.pem
|
||||||
|
|
|
@ -6,8 +6,11 @@ from threading import Thread
|
||||||
import docker
|
import docker
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
api = Api(app)
|
api = Api(app)
|
||||||
|
|
||||||
|
@ -76,6 +79,18 @@ class container_post(Resource):
|
||||||
return container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"], user='vmail')
|
return container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"], user='vmail')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify(type='danger', msg=e)
|
return jsonify(type='danger', msg=e)
|
||||||
|
elif request.json['cmd'] == 'worker_password' and request.json['raw']:
|
||||||
|
try:
|
||||||
|
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
hash = container.exec_run(["/bin/bash", "-c", "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "'"], user='_rspamd')
|
||||||
|
f = open("/access.inc", "w")
|
||||||
|
f.write('enable_password = "' + re.sub('[^0-9a-zA-Z\$]+', '', hash.rstrip()) + '";\n')
|
||||||
|
f.close()
|
||||||
|
container.restart()
|
||||||
|
return jsonify(type='success', msg='command completed successfully')
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify(type='danger', msg=e)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return jsonify(type='danger', msg='Unknown command')
|
return jsonify(type='danger', msg='Unknown command')
|
||||||
|
|
||||||
|
@ -95,7 +110,7 @@ class GracefulKiller:
|
||||||
self.kill_now = True
|
self.kill_now = True
|
||||||
|
|
||||||
def startFlaskAPI():
|
def startFlaskAPI():
|
||||||
app.run(debug=False, host='0.0.0.0', port='8080', threaded=True)
|
app.run(debug=False, host='0.0.0.0', port=8080, threaded=True)
|
||||||
|
|
||||||
api.add_resource(containers_get, '/containers/json')
|
api.add_resource(containers_get, '/containers/json')
|
||||||
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
||||||
|
@ -111,3 +126,4 @@ if __name__ == '__main__':
|
||||||
if killer.kill_now:
|
if killer.kill_now:
|
||||||
break
|
break
|
||||||
print "Stopping dockerapi-mailcow"
|
print "Stopping dockerapi-mailcow"
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV DOVECOT_VERSION 2.2.33.2
|
ENV DOVECOT_VERSION 2.3.0
|
||||||
ENV PIGEONHOLE_VERSION 0.4.21
|
ENV PIGEONHOLE_VERSION 0.5.0.1
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install \
|
RUN apt-get update && apt-get -y --no-install-recommends install \
|
||||||
automake \
|
automake \
|
||||||
autotools-dev \
|
autotools-dev \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
@ -14,6 +14,16 @@ RUN apt-get update && apt-get -y install \
|
||||||
cpanminus \
|
cpanminus \
|
||||||
curl \
|
curl \
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
|
libjson-webtoken-perl \
|
||||||
|
libcgi-pm-perl \
|
||||||
|
libcrypt-openssl-rsa-perl \
|
||||||
|
libdata-uniqid-perl \
|
||||||
|
libhtml-parser-perl \
|
||||||
|
libmail-imapclient-perl \
|
||||||
|
libparse-recdescent-perl \
|
||||||
|
libsys-meminfo-perl \
|
||||||
|
libtest-mockobject-perl \
|
||||||
|
libwww-perl \
|
||||||
libauthen-ntlm-perl \
|
libauthen-ntlm-perl \
|
||||||
libbz2-dev \
|
libbz2-dev \
|
||||||
libcrypt-ssleay-perl \
|
libcrypt-ssleay-perl \
|
||||||
|
@ -46,27 +56,28 @@ RUN apt-get update && apt-get -y install \
|
||||||
make \
|
make \
|
||||||
procps \
|
procps \
|
||||||
supervisor \
|
supervisor \
|
||||||
|
cron \
|
||||||
syslog-ng \
|
syslog-ng \
|
||||||
syslog-ng-core \
|
syslog-ng-core \
|
||||||
syslog-ng-mod-redis \
|
syslog-ng-mod-redis \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
RUN curl https://www.dovecot.org/releases/2.2/dovecot-$DOVECOT_VERSION.tar.gz | tar xvz \
|
RUN curl https://www.dovecot.org/releases/2.3/dovecot-$DOVECOT_VERSION.tar.gz | tar xvz \
|
||||||
&& cd dovecot-$DOVECOT_VERSION \
|
&& cd dovecot-ce-$DOVECOT_VERSION \
|
||||||
&& ./configure --with-mysql --with-lzma --with-lz4 --with-ssl=openssl --with-notify=inotify --with-storages=mdbox,sdbox,maildir,mbox,imapc,pop3c --with-bzlib --with-zlib \
|
&& ./configure --with-mysql --with-lzma --with-lz4 --with-ssl=openssl --with-notify=inotify --with-storages=mdbox,sdbox,maildir,mbox,imapc,pop3c --with-bzlib --with-zlib \
|
||||||
&& make -j3 \
|
&& make -j3 \
|
||||||
&& make install \
|
&& make install \
|
||||||
&& make clean \
|
&& make clean \
|
||||||
&& cd .. && rm -rf dovecot-$DOVECOT_VERSION \
|
&& cd .. && rm -rf dovecot-ce-$DOVECOT_VERSION \
|
||||||
&& curl https://pigeonhole.dovecot.org/releases/2.2/dovecot-2.2-pigeonhole-$PIGEONHOLE_VERSION.tar.gz | tar xvz \
|
&& curl https://pigeonhole.dovecot.org/releases/2.3/dovecot-2.3-pigeonhole-$PIGEONHOLE_VERSION.tar.gz | tar xvz \
|
||||||
&& cd dovecot-2.2-pigeonhole-$PIGEONHOLE_VERSION \
|
&& cd dovecot-2.3-pigeonhole-$PIGEONHOLE_VERSION \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
&& make -j3 \
|
&& make -j3 \
|
||||||
&& make install \
|
&& make install \
|
||||||
&& make clean \
|
&& make clean \
|
||||||
&& cd .. \
|
&& cd .. \
|
||||||
&& rm -rf dovecot-2.2-pigeonhole-$PIGEONHOLE_VERSION
|
&& rm -rf dovecot-2.3-pigeonhole-$PIGEONHOLE_VERSION
|
||||||
|
|
||||||
RUN cpanm Data::Uniqid Mail::IMAPClient String::Util
|
RUN cpanm Data::Uniqid Mail::IMAPClient String::Util
|
||||||
RUN echo '* * * * * root /usr/local/bin/imapsync_cron.pl' > /etc/cron.d/imapsync
|
RUN echo '* * * * * root /usr/local/bin/imapsync_cron.pl' > /etc/cron.d/imapsync
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,7 @@ destination d_redis_cleanup {
|
||||||
host("redis-mailcow")
|
host("redis-mailcow")
|
||||||
persist-name("redis3")
|
persist-name("redis3")
|
||||||
port(6379)
|
port(6379)
|
||||||
command("LTRIM" "DOVECOT_MAILLOG" "0" "9999")
|
command("LTRIM" "DOVECOT_MAILLOG" "0" "`LOG_LINES`")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
filter f_mail { facility(mail); };
|
filter f_mail { facility(mail); };
|
||||||
|
|
|
@ -41,8 +41,8 @@ RUN apk add -U --no-cache libxml2-dev \
|
||||||
Net_Sieve \
|
Net_Sieve \
|
||||||
NET_SMTP \
|
NET_SMTP \
|
||||||
Mail_mime \
|
Mail_mime \
|
||||||
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} \
|
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse \
|
||||||
&& docker-php-ext-enable redis apcu memcached imagick \
|
&& docker-php-ext-enable redis apcu memcached imagick mailparse \
|
||||||
&& pecl clear-cache \
|
&& pecl clear-cache \
|
||||||
&& docker-php-ext-configure intl \
|
&& docker-php-ext-configure intl \
|
||||||
&& docker-php-ext-install -j 4 intl gettext ldap sockets soap pdo pdo_mysql xmlrpc gd zip pcntl opcache \
|
&& docker-php-ext-install -j 4 intl gettext ldap sockets soap pdo pdo_mysql xmlrpc gd zip pcntl opcache \
|
||||||
|
|
|
@ -83,7 +83,11 @@ query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
|
||||||
WHERE id IN (
|
WHERE id IN (
|
||||||
SELECT relayhost FROM domain
|
SELECT relayhost FROM domain
|
||||||
WHERE CONCAT('@', domain) = '%s'
|
WHERE CONCAT('@', domain) = '%s'
|
||||||
);
|
OR '%s' IN (
|
||||||
|
SELECT CONCAT('@', alias_domain) FROM alias_domain
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND username != '';
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
||||||
|
@ -119,6 +123,28 @@ query = SELECT goto FROM alias
|
||||||
AND active='1';
|
AND active='1';
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > /opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
|
||||||
|
user = ${DBUSER}
|
||||||
|
password = ${DBPASS}
|
||||||
|
hosts = mysql
|
||||||
|
dbname = ${DBNAME}
|
||||||
|
query = SELECT bcc_dest FROM bcc_maps
|
||||||
|
WHERE local_dest='%s'
|
||||||
|
AND type='rcpt'
|
||||||
|
AND active='1';
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > /opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
|
||||||
|
user = ${DBUSER}
|
||||||
|
password = ${DBPASS}
|
||||||
|
hosts = mysql
|
||||||
|
dbname = ${DBNAME}
|
||||||
|
query = SELECT bcc_dest FROM bcc_maps
|
||||||
|
WHERE local_dest='%s'
|
||||||
|
AND type='sender'
|
||||||
|
AND active='1';
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
||||||
user = ${DBUSER}
|
user = ${DBUSER}
|
||||||
password = ${DBPASS}
|
password = ${DBPASS}
|
||||||
|
|
|
@ -35,7 +35,7 @@ destination d_redis_cleanup {
|
||||||
host("redis-mailcow")
|
host("redis-mailcow")
|
||||||
persist-name("redis3")
|
persist-name("redis3")
|
||||||
port(6379)
|
port(6379)
|
||||||
command("LTRIM" "POSTFIX_MAILLOG" "0" "9999")
|
command("LTRIM" "POSTFIX_MAILLOG" "0" "`LOG_LINES`")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
filter f_mail { facility(mail); };
|
filter f_mail { facility(mail); };
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Wait for MySQL to warm-up
|
# Wait for MySQL to warm-up
|
||||||
while mysqladmin ping --host mysql -u${DBUSER} -p${DBPASS}${DBPASS} --silent; do
|
while ! mysqladmin ping --host mysql -u${DBUSER} -p${DBPASS} --silent; do
|
||||||
|
echo "Waiting for database to come up..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
# Wait until port becomes free and send sig
|
# Wait until port becomes free and send sig
|
||||||
until ! nc -z sogo-mailcow 20000;
|
until ! nc -z sogo-mailcow 20000;
|
||||||
|
@ -101,5 +104,3 @@ chown sogo:sogo -R /var/lib/sogo/
|
||||||
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||||
|
|
||||||
exec gosu sogo /usr/sbin/sogod
|
exec gosu sogo /usr/sbin/sogod
|
||||||
|
|
||||||
done
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ destination d_redis_cleanup {
|
||||||
host("redis-mailcow")
|
host("redis-mailcow")
|
||||||
persist-name("redis3")
|
persist-name("redis3")
|
||||||
port(6379)
|
port(6379)
|
||||||
command("LTRIM" "SOGO_LOG" "0" "9999")
|
command("LTRIM" "SOGO_LOG" "0" "`LOG_LINES`")
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
log {
|
log {
|
||||||
|
|
|
@ -28,21 +28,19 @@ progress() {
|
||||||
[[ ${CURRENT} -gt ${TOTAL} ]] && return
|
[[ ${CURRENT} -gt ${TOTAL} ]] && return
|
||||||
[[ ${CURRENT} -lt 0 ]] && CURRENT=0
|
[[ ${CURRENT} -lt 0 ]] && CURRENT=0
|
||||||
PERCENT=$(( 200 * ${CURRENT} / ${TOTAL} % 2 + 100 * ${CURRENT} / ${TOTAL} ))
|
PERCENT=$(( 200 * ${CURRENT} / ${TOTAL} % 2 + 100 * ${CURRENT} / ${TOTAL} ))
|
||||||
log_msg "${SERVICE} health level: ${PERCENT}% (${CURRENT}/${TOTAL}), health trend: ${DIFF}"
|
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"service\":\"${SERVICE}\",\"lvl\":\"${PERCENT}\",\"hpnow\":\"${CURRENT}\",\"hptotal\":\"${TOTAL}\",\"hpdiff\":\"${DIFF}\"}" > /dev/null
|
||||||
log_data "$(printf "%d,%d,%d,%d" ${PERCENT} ${CURRENT} ${TOTAL} ${DIFF})" "${SERVICE}"
|
log_msg "${SERVICE} health level: ${PERCENT}% (${CURRENT}/${TOTAL}), health trend: ${DIFF}" no_redis
|
||||||
}
|
}
|
||||||
|
|
||||||
log_msg() {
|
log_msg() {
|
||||||
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}")\"}" > /dev/null
|
if [[ ${2} != "no_redis" ]]; then
|
||||||
|
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \
|
||||||
|
tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null
|
||||||
|
fi
|
||||||
|
redis-cli -h redis LTRIM WATCHDOG_LOG 0 ${LOG_LINES} > /dev/null
|
||||||
echo $(date) $(printf '%s\n' "${1}")
|
echo $(date) $(printf '%s\n' "${1}")
|
||||||
}
|
}
|
||||||
|
|
||||||
log_data() {
|
|
||||||
[[ -z ${1} ]] && return 1
|
|
||||||
[[ -z ${2} ]] && return 2
|
|
||||||
redis-cli -h redis LPUSH WATCHDOG_DATA "{\"time\":\"$(date +%s)\",\"service\":\"data\",\"$(printf '%s' "${2}")\":\"$(printf '%s' "${1}")\"}" > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
function mail_error() {
|
function mail_error() {
|
||||||
[[ -z ${1} ]] && return 1
|
[[ -z ${1} ]] && return 1
|
||||||
[[ -z ${2} ]] && return 2
|
[[ -z ${2} ]] && return 2
|
||||||
|
@ -149,7 +147,7 @@ postfix_checks() {
|
||||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||||
host_ip=$(get_container_ip postfix-mailcow)
|
host_ip=$(get_container_ip postfix-mailcow)
|
||||||
err_c_cur=${err_count}
|
err_c_cur=${err_count}
|
||||||
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 589 -f watchdog -C "RCPT TO:null@localhost" -C DATA -C . -R 250 1>&2; err_count=$(( ${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 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 589 -S 1>&2; err_count=$(( ${err_count} + $? ))
|
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 589 -S 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
[ ${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} ))
|
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||||
|
@ -169,7 +167,7 @@ dovecot_checks() {
|
||||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||||
host_ip=$(get_container_ip dovecot-mailcow)
|
host_ip=$(get_container_ip dovecot-mailcow)
|
||||||
err_c_cur=${err_count}
|
err_c_cur=${err_count}
|
||||||
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 24 -f "watchdog" -C "RCPT TO:<watchdog@invalid>" -L -R "User doesn't exist" 1>&2; err_count=$(( ${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" 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
/usr/lib/nagios/plugins/check_imap -4 -H ${host_ip} -p 993 -S -e "OK " 1>&2; err_count=$(( ${err_count} + $? ))
|
/usr/lib/nagios/plugins/check_imap -4 -H ${host_ip} -p 993 -S -e "OK " 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
/usr/lib/nagios/plugins/check_imap -4 -H ${host_ip} -p 143 -e "OK " 1>&2; err_count=$(( ${err_count} + $? ))
|
/usr/lib/nagios/plugins/check_imap -4 -H ${host_ip} -p 143 -e "OK " 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
/usr/lib/nagios/plugins/check_tcp -4 -H ${host_ip} -p 10001 -e "VERSION" 1>&2; err_count=$(( ${err_count} + $? ))
|
/usr/lib/nagios/plugins/check_tcp -4 -H ${host_ip} -p 10001 -e "VERSION" 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
|
@ -234,27 +232,6 @@ Empty
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
dns_checks() {
|
|
||||||
err_count=0
|
|
||||||
diff_c=0
|
|
||||||
THRESHOLD=28
|
|
||||||
# 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
|
|
||||||
host_ip=$(get_container_ip unbound-mailcow)
|
|
||||||
err_c_cur=${err_count}
|
|
||||||
/usr/lib/nagios/plugins/check_dns -H google.com 1>&2; err_count=$(( ${err_count} + ($? * 2)))
|
|
||||||
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H google.com 1>&2; err_count=$(( ${err_count} + ($? * 2)))
|
|
||||||
dig +dnssec org. @${host_ip} | grep -E 'flags:.+ad' 1>&2; err_count=$(( ${err_count} + ($? * 2)))
|
|
||||||
[ ${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 "Unbound" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
|
||||||
diff_c=0
|
|
||||||
sleep $(( ( RANDOM % 30 ) + 10 ))
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create watchdog agents
|
# Create watchdog agents
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
|
@ -322,17 +299,6 @@ done
|
||||||
) &
|
) &
|
||||||
BACKGROUND_TASKS+=($!)
|
BACKGROUND_TASKS+=($!)
|
||||||
|
|
||||||
(
|
|
||||||
while true; do
|
|
||||||
if ! dns_checks; then
|
|
||||||
log_msg "Unbound hit error limit"
|
|
||||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "unbound-mailcow"
|
|
||||||
#echo unbound-mailcow > /tmp/com_pipe
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
) &
|
|
||||||
BACKGROUND_TASKS+=($!)
|
|
||||||
|
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
if ! rspamd_checks; then
|
if ! rspamd_checks; then
|
||||||
|
|
|
@ -14,12 +14,16 @@ login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
|
||||||
mail_home = /var/vmail/%d/%n
|
mail_home = /var/vmail/%d/%n
|
||||||
mail_location = maildir:~/
|
mail_location = maildir:~/
|
||||||
mail_plugins = quota acl zlib listescape #mail_crypt
|
mail_plugins = quota acl zlib listescape #mail_crypt
|
||||||
ssl_protocols = !SSLv3
|
# Dovecot 2.2
|
||||||
|
#ssl_protocols = !SSLv3
|
||||||
|
# Dovecot 2.3
|
||||||
|
ssl_min_protocol = TLSv1
|
||||||
ssl_prefer_server_ciphers = yes
|
ssl_prefer_server_ciphers = yes
|
||||||
ssl_cipher_list = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
|
ssl_cipher_list = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
|
||||||
|
# Default in Dovecot 2.3
|
||||||
ssl_options = no_compression
|
ssl_options = no_compression
|
||||||
# Automatically regenerates every week
|
# New in Dovecot 2.3
|
||||||
ssl_dh_parameters_length = 2048
|
ssl_dh=</etc/ssl/mail/dhparams.pem
|
||||||
log_timestamp = "%Y-%m-%d %H:%M:%S "
|
log_timestamp = "%Y-%m-%d %H:%M:%S "
|
||||||
recipient_delimiter = +
|
recipient_delimiter = +
|
||||||
auth_master_user_separator = *
|
auth_master_user_separator = *
|
||||||
|
@ -55,6 +59,9 @@ namespace inbox {
|
||||||
mailbox "Deleted Items" {
|
mailbox "Deleted Items" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
|
mailbox "Rubbish" {
|
||||||
|
special_use = \Trash
|
||||||
|
}
|
||||||
mailbox "Gelöschte Objekte" {
|
mailbox "Gelöschte Objekte" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
|
@ -163,7 +170,7 @@ namespace {
|
||||||
prefix = Shared/%%u/
|
prefix = Shared/%%u/
|
||||||
location = maildir:%%h/:INDEXPVT=~/Shared/%%u
|
location = maildir:%%h/:INDEXPVT=~/Shared/%%u
|
||||||
subscriptions = no
|
subscriptions = no
|
||||||
list = yes
|
list = children
|
||||||
}
|
}
|
||||||
protocols = imap sieve lmtp pop3
|
protocols = imap sieve lmtp pop3
|
||||||
service dict {
|
service dict {
|
||||||
|
@ -257,6 +264,7 @@ plugin {
|
||||||
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
|
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
|
||||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
|
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
|
||||||
sieve_max_script_size = 1M
|
sieve_max_script_size = 1M
|
||||||
|
sieve_max_redirects = 30
|
||||||
sieve_quota_max_scripts = 0
|
sieve_quota_max_scripts = 0
|
||||||
sieve_quota_max_storage = 0
|
sieve_quota_max_storage = 0
|
||||||
listescape_char = "\\"
|
listescape_char = "\\"
|
||||||
|
|
|
@ -6,6 +6,13 @@ innodb_file_per_table = TRUE
|
||||||
innodb_file_format = barracuda
|
innodb_file_format = barracuda
|
||||||
innodb_large_prefix = TRUE
|
innodb_large_prefix = TRUE
|
||||||
#sql_mode=IGNORE_SPACE,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
#sql_mode=IGNORE_SPACE,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
||||||
|
max_allowed_packet=192M
|
||||||
|
max-connections=1500
|
||||||
|
innodb-strict-mode=0
|
||||||
|
skip-host-cache
|
||||||
|
skip-name-resolve
|
||||||
|
log-warnings=0
|
||||||
|
event_scheduler=1
|
||||||
|
|
||||||
[client]
|
[client]
|
||||||
default-character-set = utf8mb4
|
default-character-set = utf8mb4
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
server {
|
||||||
|
listen 9081;
|
||||||
|
index index.php index.html;
|
||||||
|
server_name _;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
root /meta_exporter;
|
||||||
|
client_max_body_size 10M;
|
||||||
|
location ~ \.php$ {
|
||||||
|
client_max_body_size 10M;
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass phpfpm:9000;
|
||||||
|
fastcgi_index pipe.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,7 +39,27 @@ postscreen_greet_ttl = 2d
|
||||||
postscreen_greet_wait = 3s
|
postscreen_greet_wait = 3s
|
||||||
postscreen_non_smtp_command_enable = no
|
postscreen_non_smtp_command_enable = no
|
||||||
postscreen_pipelining_enable = no
|
postscreen_pipelining_enable = no
|
||||||
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_maps
|
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf,
|
||||||
|
$local_recipient_maps,
|
||||||
|
$mydestination,
|
||||||
|
$virtual_alias_maps,
|
||||||
|
$virtual_alias_domains,
|
||||||
|
$virtual_mailbox_maps,
|
||||||
|
$virtual_mailbox_domains,
|
||||||
|
$relay_recipient_maps,
|
||||||
|
$relay_domains,
|
||||||
|
$canonical_maps,
|
||||||
|
$sender_canonical_maps,
|
||||||
|
$recipient_canonical_maps,
|
||||||
|
$relocated_maps,
|
||||||
|
$transport_maps,
|
||||||
|
$mynetworks,
|
||||||
|
$smtpd_sender_login_maps
|
||||||
queue_run_delay = 300s
|
queue_run_delay = 300s
|
||||||
relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
|
relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
|
||||||
relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
|
relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
|
||||||
|
@ -79,10 +99,15 @@ smtpd_tls_mandatory_ciphers = high
|
||||||
smtpd_tls_security_level = may
|
smtpd_tls_security_level = may
|
||||||
tls_ssl_options = NO_COMPRESSION
|
tls_ssl_options = NO_COMPRESSION
|
||||||
tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
|
tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
|
||||||
virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf,
|
||||||
|
proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
||||||
virtual_gid_maps = static:5000
|
virtual_gid_maps = static:5000
|
||||||
virtual_mailbox_base = /var/vmail/
|
virtual_mailbox_base = /var/vmail/
|
||||||
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
||||||
|
recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
|
||||||
|
sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
|
||||||
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
|
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
|
||||||
virtual_minimum_uid = 104
|
virtual_minimum_uid = 104
|
||||||
virtual_transport = lmtp:inet:dovecot:24
|
virtual_transport = lmtp:inet:dovecot:24
|
||||||
|
|
|
@ -12,6 +12,11 @@ submission inet n - n - - smtpd
|
||||||
588 inet n - n - - smtpd
|
588 inet n - n - - smtpd
|
||||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||||
-o smtpd_tls_auth_only=no
|
-o smtpd_tls_auth_only=no
|
||||||
|
590 inet n - n - - smtpd
|
||||||
|
-o smtpd_client_restrictions=permit_mynetworks,reject
|
||||||
|
-o smtpd_tls_auth_only=no
|
||||||
|
-o smtpd_milters=
|
||||||
|
-o non_smtpd_milters=
|
||||||
smtp_enforced_tls unix - - n - - smtp
|
smtp_enforced_tls unix - - n - - smtp
|
||||||
-o smtp_tls_security_level=encrypt
|
-o smtp_tls_security_level=encrypt
|
||||||
-o syslog_name=enforced-tls-smtp
|
-o syslog_name=enforced-tls-smtp
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
1
|
|
|
@ -17,7 +17,7 @@ $opt = [
|
||||||
];
|
];
|
||||||
try {
|
try {
|
||||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||||
$stmt = $pdo->query("SELECT * FROM `filterconf`");
|
$stmt = $pdo->query("SELECT '1' FROM `filterconf`");
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
echo 'settings { }';
|
echo 'settings { }';
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
MX_IMPLICIT {
|
MX_IMPLICIT {
|
||||||
expression = "MX_GOOD and MX_MISSING";
|
expression = "MX_GOOD and MX_MISSING";
|
||||||
score = -0.01;
|
score = -0.01;
|
||||||
|
}
|
||||||
|
VIRUS_FOUND {
|
||||||
|
expression = "CLAM_VIRUS & !MAILCOW_WHITE";
|
||||||
|
score = 2000;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
rules {
|
rules {
|
||||||
DKIM_FAIL {
|
|
||||||
action = "add header";
|
|
||||||
expression = "R_DKIM_REJECT & !MAILLIST & !MAILCOW_WHITE & !MAILCOW_BLACK";
|
|
||||||
require_action = ["no action", "greylist", "soft reject"];
|
|
||||||
}
|
|
||||||
VIRUS_FOUND {
|
|
||||||
action = "reject";
|
|
||||||
expression = "CLAM_VIRUS & !MAILCOW_WHITE";
|
|
||||||
honor_action = ["reject"];
|
|
||||||
}
|
|
||||||
WHITELIST_FORWARDING_HOST_NO_REJECT {
|
WHITELIST_FORWARDING_HOST_NO_REJECT {
|
||||||
action = "add header";
|
action = "add header";
|
||||||
expression = "WHITELISTED_FWD_HOST";
|
expression = "WHITELISTED_FWD_HOST";
|
||||||
|
@ -19,9 +9,4 @@ rules {
|
||||||
expression = "WHITELISTED_FWD_HOST";
|
expression = "WHITELISTED_FWD_HOST";
|
||||||
require_action = ["greylist", "soft reject"];
|
require_action = ["greylist", "soft reject"];
|
||||||
}
|
}
|
||||||
ADD_UNAUTH_SUBJ {
|
|
||||||
action = "rewrite subject";
|
|
||||||
subject = "[Unauth] %s";
|
|
||||||
expression = "SPOOFED_SENDER";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,7 @@ classifier "bayes" {
|
||||||
servers = "redis:6379";
|
servers = "redis:6379";
|
||||||
min_tokens = 11;
|
min_tokens = 11;
|
||||||
min_learns = 20;
|
min_learns = 20;
|
||||||
autolearn = true;
|
autolearn = [-20, 50];
|
||||||
|
|
||||||
per_user = <<EOD
|
per_user = <<EOD
|
||||||
return function(task)
|
return function(task)
|
||||||
local rcpt = task:get_recipients(1)
|
local rcpt = task:get_recipients(1)
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?php
|
||||||
|
// File size is limited by Nginx site to 10M
|
||||||
|
// To speed things up, we do not include prerequisites
|
||||||
|
header('Content-Type: text/plain');
|
||||||
|
require_once "vars.inc.php";
|
||||||
|
// Do not show errors, we log to using error_log
|
||||||
|
ini_set('error_reporting', 0);
|
||||||
|
// Init database
|
||||||
|
$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
|
||||||
|
$opt = [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
http_response_code(501);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
// Init Redis
|
||||||
|
$redis = new Redis();
|
||||||
|
$redis->connect('redis-mailcow', 6379);
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
function parse_email($email) {
|
||||||
|
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
|
||||||
|
$a = strrpos($email, '@');
|
||||||
|
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
|
||||||
|
}
|
||||||
|
if (!function_exists('getallheaders')) {
|
||||||
|
function getallheaders() {
|
||||||
|
if (!is_array($_SERVER)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
$headers = array();
|
||||||
|
foreach ($_SERVER as $name => $value) {
|
||||||
|
if (substr($name, 0, 5) == 'HTTP_') {
|
||||||
|
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw_data = file_get_contents('php://input');
|
||||||
|
$headers = getallheaders();
|
||||||
|
|
||||||
|
$qid = $headers['X-Rspamd-Qid'];
|
||||||
|
$score = $headers['X-Rspamd-Score'];
|
||||||
|
$rcpts = $headers['X-Rspamd-Rcpt'];
|
||||||
|
$user = $headers['X-Rspamd-User'];
|
||||||
|
$ip = $headers['X-Rspamd-Ip'];
|
||||||
|
$action = $headers['X-Rspamd-Action'];
|
||||||
|
$sender = $headers['X-Rspamd-From'];
|
||||||
|
$symbols = $headers['X-Rspamd-Symbols'];
|
||||||
|
|
||||||
|
$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($max_size = $redis->Get('Q_MAX_SIZE')) {
|
||||||
|
if (!empty($max_size) && ($max_size * 1048576) < $raw_size) {
|
||||||
|
error_log(sprintf("Message too large: %d exceeds %d", $raw_size, ($max_size * 1048576)));
|
||||||
|
http_response_code(505);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($exclude_domains = $redis->Get('Q_EXCLUDE_DOMAINS')) {
|
||||||
|
$exclude_domains = json_decode($exclude_domains, true);
|
||||||
|
}
|
||||||
|
$retention_size = (int)$redis->Get('Q_RETENTION_SIZE');
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
error_log($e);
|
||||||
|
http_response_code(504);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filtered_rcpts = array();
|
||||||
|
foreach (json_decode($rcpts, true) as $rcpt) {
|
||||||
|
$parsed_mail = parse_email($rcpt);
|
||||||
|
if (in_array($parsed_mail['domain'], $exclude_domains)) {
|
||||||
|
error_log(sprintf("Skipped domain %s", $parsed_mail['domain']));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT `goto` FROM `alias`
|
||||||
|
WHERE
|
||||||
|
(
|
||||||
|
`address` = :rcpt
|
||||||
|
OR
|
||||||
|
`address` IN (
|
||||||
|
SELECT username FROM mailbox, alias_domain
|
||||||
|
WHERE (alias_domain.alias_domain = :domain_part
|
||||||
|
AND mailbox.username = CONCAT(:local_part, '@', alias_domain.target_domain)
|
||||||
|
AND mailbox.active = '1'
|
||||||
|
AND alias_domain.active='1')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND `active`= '1';");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':rcpt' => $rcpt,
|
||||||
|
':local_part' => $parsed_mail['local'],
|
||||||
|
':domain_part' => $parsed_mail['domain']
|
||||||
|
));
|
||||||
|
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
|
||||||
|
if (!empty($gotos)) {
|
||||||
|
$filtered_rcpts = array_unique(array_merge($filtered_rcpts, explode(',', $gotos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
error_log($e->getMessage());
|
||||||
|
http_response_code(502);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($filtered_rcpts as $rcpt) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `quarantaine` (`qid`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`)
|
||||||
|
VALUES (:qid, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':qid' => $qid,
|
||||||
|
':score' => $score,
|
||||||
|
':sender' => $sender,
|
||||||
|
':rcpt' => $rcpt,
|
||||||
|
':symbols' => $symbols,
|
||||||
|
':user' => $user,
|
||||||
|
':ip' => $ip,
|
||||||
|
':msg' => $raw_data,
|
||||||
|
':action' => $action
|
||||||
|
));
|
||||||
|
$stmt = $pdo->prepare('DELETE FROM `quarantaine` WHERE `id` NOT IN (
|
||||||
|
SELECT `id`
|
||||||
|
FROM (
|
||||||
|
SELECT `id`
|
||||||
|
FROM `quarantaine`
|
||||||
|
WHERE `rcpt` = :rcpt
|
||||||
|
ORDER BY id DESC
|
||||||
|
LIMIT :retention_size
|
||||||
|
) x
|
||||||
|
);');
|
||||||
|
$stmt->execute(array(
|
||||||
|
':rcpt' => $rcpt,
|
||||||
|
':retention_size' => $retention_size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
error_log($e->getMessage());
|
||||||
|
http_response_code(503);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?php
|
||||||
|
require_once('../../../web/inc/vars.inc.php');
|
||||||
|
?>
|
|
@ -1,3 +1,4 @@
|
||||||
type = "console";
|
type = "console";
|
||||||
systemd = false;
|
systemd = false;
|
||||||
.include "$CONFDIR/logging.inc"
|
.include "$CONFDIR/logging.inc"
|
||||||
|
.include(try=true; priority=20) "$CONFDIR/override.d/logging.custom.inc"
|
||||||
|
|
|
@ -9,6 +9,6 @@ rates {
|
||||||
}
|
}
|
||||||
whitelisted_rcpts = "postmaster,mailer-daemon";
|
whitelisted_rcpts = "postmaster,mailer-daemon";
|
||||||
max_rcpt = 5;
|
max_rcpt = 5;
|
||||||
custom_keywords = "/etc/rspamd/custom/ratelimit.lua";
|
custom_keywords = "/etc/rspamd/lua/ratelimit.lua";
|
||||||
user_keywords = ["user", "customrl"];
|
user_keywords = ["user", "customrl"];
|
||||||
dynamic_rates = { customrl = "customrl"}
|
dynamic_rates = { customrl = "customrl"}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
# Placeholder
|
|
@ -1,8 +1,9 @@
|
||||||
bind_socket = "*:11334";
|
bind_socket = "*:11334";
|
||||||
enable_password = "$2$pppq86q9uns51zd5ekfxecj7bxwaefo3$p7f9xdhamydjhtypcr639it3kqeiknx3dk9on7skjypyi8uwwcmy";
|
|
||||||
secure_ip = "192.168.0.0/16";
|
secure_ip = "192.168.0.0/16";
|
||||||
secure_ip = "172.16.0.0/12";
|
secure_ip = "172.16.0.0/12";
|
||||||
secure_ip = "10.0.0.0/8";
|
secure_ip = "10.0.0.0/8";
|
||||||
secure_ip = "127.0.0.1";
|
secure_ip = "127.0.0.1";
|
||||||
secure_ip = "::1";
|
secure_ip = "::1";
|
||||||
secure_ip = "fd4d:6169:6c63:6f77::/64"
|
secure_ip = "fd4d:6169:6c63:6f77::/64"
|
||||||
|
.include(try=true; priority=10) "$CONFDIR/override.d/worker-controller-password.inc"
|
||||||
|
.include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc"
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
bind_socket = "*:11333";
|
bind_socket = "*:11333";
|
||||||
task_timeout = 12s;
|
task_timeout = 12s;
|
||||||
|
.include(try=true; priority=20) "$CONFDIR/override.d/worker-normal.custom.inc"
|
||||||
|
|
|
@ -5,3 +5,4 @@ upstream {
|
||||||
default = true;
|
default = true;
|
||||||
hosts = "rspamd:11333"
|
hosts = "rspamd:11333"
|
||||||
}
|
}
|
||||||
|
.include(try=true; priority=20) "$CONFDIR/override.d/worker-proxy.custom.inc"
|
||||||
|
|
|
@ -7,26 +7,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
$tfa_data = get_tfa();
|
$tfa_data = get_tfa();
|
||||||
?>
|
?>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="active">
|
<li role="presentation" class="active"><a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a></li>
|
||||||
<a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a>
|
<li role="presentation"><a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a></li>
|
||||||
</li>
|
|
||||||
<li role="presentation">
|
|
||||||
<a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a>
|
|
||||||
</li>
|
|
||||||
<li class="dropdown">
|
|
||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Logs
|
|
||||||
<span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li role="presentation"><a href="#tab-postfix-logs" aria-controls="tab-postfix-logs" role="tab" data-toggle="tab">Postfix</a></li>
|
|
||||||
<li role="presentation"><a href="#tab-dovecot-logs" aria-controls="tab-dovecot-logs" role="tab" data-toggle="tab">Dovecot</a></li>
|
|
||||||
<li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
|
|
||||||
<li role="presentation"><a href="#tab-fail2ban-logs" aria-controls="tab-fail2ban-logs" role="tab" data-toggle="tab">Fail2ban</a></li>
|
|
||||||
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
|
|
||||||
<li role="presentation"><a href="#tab-autodiscover-logs" aria-controls="tab-autodiscover-logs" role="tab" data-toggle="tab">Autodiscover</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content" style="padding-top:20px">
|
<div class="tab-content" style="padding-top:20px">
|
||||||
|
@ -57,7 +40,7 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-3 col-sm-9">
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
<button class="btn btn-default" id="edit_selected" data-id="admin" data-item="null" data-api-url='edit/self' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
|
<button class="btn btn-default" id="edit_selected" data-id="admin" data-item="null" data-api-url='edit/self' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -95,6 +78,42 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden panel panel-primary">
|
||||||
|
<div class="panel-heading">API</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-3" for="allow_from"><?=$lang['admin']['api_allow_from'];?>:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea class="form-control" rows="5" name="allow_from" id="allow_from" required><?=htmlspecialchars($admindetails['allow_from']);?></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-3" for="admin_api_key"><?=$lang['admin']['api_key'];?>:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" placeholder="-" value="<?=htmlspecialchars($admindetails['api_key']);?>" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="active" <?=($admindetails['api_active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-default" name="admin_api" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
|
<button class="btn btn-info" name="admin_api_regen_key" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><?=$lang['admin']['domain_admins'];?></div>
|
<div class="panel-heading"><?=$lang['admin']['domain_admins'];?></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
@ -120,7 +139,6 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-config">
|
<div role="tabpanel" class="tab-pane" id="tab-config">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="sidebar-admin" class="col-sm-2 hidden-xs">
|
<div id="sidebar-admin" class="col-sm-2 hidden-xs">
|
||||||
|
@ -129,6 +147,7 @@ $tfa_data = get_tfa();
|
||||||
<a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a>
|
<a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a>
|
||||||
<a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a>
|
<a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a>
|
||||||
<a href="#relayhosts" class="list-group-item">Relayhosts</a>
|
<a href="#relayhosts" class="list-group-item">Relayhosts</a>
|
||||||
|
<a href="#quarantaine" class="list-group-item">Quarantaine</a>
|
||||||
<a href="#customize" class="list-group-item"><?=$lang['admin']['customize'];?></a>
|
<a href="#customize" class="list-group-item"><?=$lang['admin']['customize'];?></a>
|
||||||
<a href="#top" class="list-group-item" style="border-top:1px dashed #dadada">↸ <?=$lang['admin']['to_top'];?></a>
|
<a href="#top" class="list-group-item" style="border-top:1px dashed #dadada">↸ <?=$lang['admin']['to_top'];?></a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -230,7 +249,7 @@ $tfa_data = get_tfa();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<legend style="margin-top:40px"><?=$lang['admin']['dkim_add_key'];?></legend>
|
<legend style="margin-top:40px"><?=$lang['admin']['dkim_add_key'];?></legend>
|
||||||
<form class="form-inline" data-id="dkim" role="form" method="post">
|
<form class="form" data-id="dkim" role="form" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="domain">Domain</label>
|
<label for="domain">Domain</label>
|
||||||
<input class="form-control" id="domain" name="domain" placeholder="example.org" required>
|
<input class="form-control" id="domain" name="domain" placeholder="example.org" required>
|
||||||
|
@ -291,7 +310,7 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
<legend><?=$lang['admin']['add_forwarding_host'];?></legend>
|
<legend><?=$lang['admin']['add_forwarding_host'];?></legend>
|
||||||
<p class="help-block"><?=$lang['admin']['forwarding_hosts_add_hint'];?></p>
|
<p class="help-block"><?=$lang['admin']['forwarding_hosts_add_hint'];?></p>
|
||||||
<form class="form-inline" data-id="fwdhost" role="form" method="post">
|
<form class="form" data-id="fwdhost" role="form" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
||||||
<input class="form-control" id="hostname" name="hostname" placeholder="example.org" required>
|
<input class="form-control" id="hostname" name="hostname" placeholder="example.org" required>
|
||||||
|
@ -328,7 +347,7 @@ $tfa_data = get_tfa();
|
||||||
<input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required>
|
<input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="retry_window"><?=$lang['admin']['f2b_whitelist'];?>:</label>
|
<label for="whitelist"><?=$lang['admin']['f2b_whitelist'];?>:</label>
|
||||||
<textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea>
|
<textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-default" id="add_item" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
<button class="btn btn-default" id="add_item" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
|
@ -358,7 +377,7 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
<legend><?=$lang['admin']['add_relayhost'];?></legend>
|
<legend><?=$lang['admin']['add_relayhost'];?></legend>
|
||||||
<p class="help-block"><?=$lang['admin']['add_relayhost_add_hint'];?></p>
|
<p class="help-block"><?=$lang['admin']['add_relayhost_add_hint'];?></p>
|
||||||
<form class="form-inline" data-id="rlyhost" role="form" method="post">
|
<form class="form" data-id="rlyhost" role="form" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
||||||
<input class="form-control" id="hostname" name="hostname" required>
|
<input class="form-control" id="hostname" name="hostname" required>
|
||||||
|
@ -376,6 +395,43 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<span class="anchor" id="quarantaine"></span>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Quarantäne</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<?php $q_data = quarantaine('settings'); ?>
|
||||||
|
<form class="form" data-id="quarantaine" role="form" method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="retention_size">Rückhaltungen pro Mailbox:</label>
|
||||||
|
<input type="number" class="form-control" id="retention_size" name="retention_size" value="<?=$q_data['retention_size'];?>" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="max_size">Maximale Größe in MiB (größere Elemente werden verworfen):</label>
|
||||||
|
<input type="number" class="form-control" id="max_size" name="max_size" value="<?=$q_data['max_size'];?>" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="exclude_domains">Domains und Alias-Domains ausschließen:</label><br />
|
||||||
|
<select data-width="100%" id="exclude_domains" name="exclude_domains" class="selectpicker" title="<?=$lang['tfa']['select'];?>" multiple>
|
||||||
|
<?php
|
||||||
|
foreach (array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')) as $domain):
|
||||||
|
?>
|
||||||
|
<option <?=(in_array($domain, $q_data['exclude_domains'])) ? 'selected' : null;?>><?=htmlspecialchars($domain);?></option>
|
||||||
|
<?php
|
||||||
|
endforeach;
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success" id="edit_selected" data-item="self" data-id="quarantaine" data-api-url='edit/quarantaine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="anchor" id="customize"></span>
|
<span class="anchor" id="customize"></span>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><?=$lang['admin']['customize'];?></div>
|
<div class="panel-heading"><?=$lang['admin']['customize'];?></div>
|
||||||
|
@ -444,10 +500,29 @@ $tfa_data = get_tfa();
|
||||||
endforeach;
|
endforeach;
|
||||||
?>
|
?>
|
||||||
</table>
|
</table>
|
||||||
<div class="btn-group">
|
<p><div class="btn-group">
|
||||||
<button class="btn btn-success" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
|
<button class="btn btn-sm btn-success" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
<button class="btn btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
|
<button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
|
||||||
|
</div></p>
|
||||||
|
</form>
|
||||||
|
<legend><?=$lang['admin']['ui_texts'];?></legend>
|
||||||
|
<?php
|
||||||
|
$ui_texts = customize('get', 'ui_texts');
|
||||||
|
?>
|
||||||
|
<form class="form" data-id="uitexts" role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="main_name"><?=$lang['admin']['main_name'];?>:</label>
|
||||||
|
<input type="text" class="form-control" id="main_name" name="main_name" placeholder="mailcow UI" value="<?=$ui_texts['main_name'];?>">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="apps_name"><?=$lang['admin']['apps_name'];?>:</label>
|
||||||
|
<input type="text" class="form-control" id="apps_name" name="apps_name" placeholder="mailcow Apps" value="<?=$ui_texts['apps_name'];?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="help_text"><?=$lang['admin']['help_text'];?>:</label>
|
||||||
|
<textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success" id="edit_selected" data-item="null" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -455,108 +530,6 @@ $tfa_data = get_tfa();
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Postfix <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed" id="postfix_log"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Dovecot <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed" id="dovecot_log"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">SOGo <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_sogo_log"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed" id="sogo_log"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-fail2ban-logs">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Fail2ban <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_fail2ban_log"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed" id="fail2ban_log"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Rspamd history <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed log-table" id="rspamd_history"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Autodiscover <span class="badge badge-info log-lines"></span>
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100">+ 100</button>
|
|
||||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000">+ 1000</button>
|
|
||||||
<button class="btn btn-xs btn-default" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-condensed" id="autodiscover_log"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- /container -->
|
</div> <!-- /container -->
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
@media (max-width:1050px){.navbar-header{float:none}.navbar-left,.navbar-nav,.navbar-right{float:none!important}.navbar-toggle{display:block}.navbar-collapse{border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-collapse.collapse{display:none!important}.navbar-nav{margin-top:7.5px}.navbar-nav>li{float:none}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px}.collapse.in{display:block!important}.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}}
|
|
@ -0,0 +1,37 @@
|
||||||
|
table.footable>tbody>tr.footable-empty>td {
|
||||||
|
font-size:15px !important;
|
||||||
|
font-style:italic;
|
||||||
|
}
|
||||||
|
.pagination a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
.panel panel-default {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
.table-responsive {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.table-responsive {
|
||||||
|
overflow-x: scroll !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footer-add-item {
|
||||||
|
display:block;
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 10px;
|
||||||
|
background: #F5F5F5;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.container {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mass-actions-debug {
|
||||||
|
user-select: none;
|
||||||
|
padding:10px 0 10px 10px;
|
||||||
|
}
|
||||||
|
.inputMissingAttr {
|
||||||
|
border-color: #FF4136;
|
||||||
|
}
|
|
@ -35,3 +35,11 @@ table.footable>tbody>tr.footable-empty>td {
|
||||||
.inputMissingAttr {
|
.inputMissingAttr {
|
||||||
border-color: #FF4136;
|
border-color: #FF4136;
|
||||||
}
|
}
|
||||||
|
.dns-found {
|
||||||
|
max-width: 300px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.dns-recommended {
|
||||||
|
max-width: 150px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
table.footable>tbody>tr.footable-empty>td {
|
||||||
|
font-size:15px !important;
|
||||||
|
font-style:italic;
|
||||||
|
}
|
||||||
|
.pagination a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
.panel panel-default {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
.table-responsive {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.table-responsive {
|
||||||
|
overflow-x: scroll !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footer-add-item {
|
||||||
|
display:block;
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 10px;
|
||||||
|
background: #F5F5F5;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.container {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mass-actions-quarantaine {
|
||||||
|
user-select: none;
|
||||||
|
padding:10px 0 10px 10px;
|
||||||
|
}
|
||||||
|
.inputMissingAttr {
|
||||||
|
border-color: #FF4136;
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
<?php
|
||||||
|
require_once "inc/prerequisites.inc.php";
|
||||||
|
|
||||||
|
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||||
|
require_once "inc/header.inc.php";
|
||||||
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li class="dropdown active"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Rspamd
|
||||||
|
<span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li role="presentation" class="active"><a href="#tab-rspamd-ui" aria-controls="tab-rspamd-ui" role="tab" data-toggle="tab">Rspamd UI</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-rspamd-settings" aria-controls="tab-rspamd-settings" role="tab" data-toggle="tab">Rspamd settings map</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li role="presentation"><a href="#tab-containers" aria-controls="tab-containers" role="tab" data-toggle="tab">Containers</a></li>
|
||||||
|
<li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Logs
|
||||||
|
<span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li role="presentation"><a href="#tab-postfix-logs" aria-controls="tab-postfix-logs" role="tab" data-toggle="tab">Postfix</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-dovecot-logs" aria-controls="tab-dovecot-logs" role="tab" data-toggle="tab">Dovecot</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-fail2ban-logs" aria-controls="tab-fail2ban-logs" role="tab" data-toggle="tab">Fail2ban</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-autodiscover-logs" aria-controls="tab-autodiscover-logs" role="tab" data-toggle="tab">Autodiscover</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-watchdog-logs" aria-controls="tab-watchdog-logs" role="tab" data-toggle="tab">Watchdog</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-acme-logs" aria-controls="tab-acme-logs" role="tab" data-toggle="tab">ACME</a></li>
|
||||||
|
<li role="presentation"><a href="#tab-api-logs" aria-controls="tab-api-logs" role="tab" data-toggle="tab">API</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="tab-content" style="padding-top:20px">
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="tab-rspamd-ui">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Rspamd UI</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<form class="form-horizontal" autocapitalize="none" data-id="admin" autocorrect="off" role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
|
<label>
|
||||||
|
<a href="/rspamd/" target="_blank"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> Rspamd UI</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-3" for="rspamd_ui_pass"><?=$lang['admin']['password'];?>:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" name="rspamd_ui_pass" id="rspamd_ui_pass">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-3" for="rspamd_ui_pass2"><?=$lang['admin']['password_repeat'];?>:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" name="rspamd_ui_pass2" id="rspamd_ui_pass2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
|
<button type="submit" class="btn btn-default" id="rspamd_ui" name="rspamd_ui" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<img class="img-responsive" src="/img/rspamd_logo.png" alt="Rspamd UI" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-rspamd-settings">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Rspamd settings map</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="settings_map" name="settings_map" readonly><?=file_get_contents('http://nginx:8081/settings.php');?></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-containers">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Container information</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<ul class="list-group">
|
||||||
|
<?php
|
||||||
|
$container_array = array(
|
||||||
|
'nginx-mailcow',
|
||||||
|
'rspamd-mailcow',
|
||||||
|
'postfix-mailcow',
|
||||||
|
'dovecot-mailcow',
|
||||||
|
'sogo-mailcow',
|
||||||
|
'acme-mailcow',
|
||||||
|
'memcached-mailcow',
|
||||||
|
'watchdog-mailcow',
|
||||||
|
'unbound-mailcow',
|
||||||
|
'redis-mailcow',
|
||||||
|
'php-fpm-mailcow',
|
||||||
|
'mysql-mailcow',
|
||||||
|
'fail2ban-mailcow',
|
||||||
|
'clamd-mailcow'
|
||||||
|
);
|
||||||
|
foreach ($container_array as $container) {
|
||||||
|
$container_stats = docker($container, 'info');
|
||||||
|
?>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<?=$container;?>
|
||||||
|
<?php
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
$StartedAt = date_parse($container_stats['State']['StartedAt']);
|
||||||
|
$date = new \DateTime();
|
||||||
|
$date->setTimestamp(mktime(
|
||||||
|
$StartedAt['hour'],
|
||||||
|
$StartedAt['minute'],
|
||||||
|
$StartedAt['second'],
|
||||||
|
$StartedAt['month'],
|
||||||
|
$StartedAt['day'],
|
||||||
|
$StartedAt['year']));
|
||||||
|
$user_tz = new DateTimeZone(getenv('TZ'));
|
||||||
|
$date->setTimezone($user_tz);
|
||||||
|
$started = $date->format('r');
|
||||||
|
?>
|
||||||
|
<small>(Started on <?=$started;?>),
|
||||||
|
<a href data-toggle="modal" data-container="<?=$container;?>" data-target="#RestartContainer">Restart</a></small>
|
||||||
|
<span class="pull-right label label-<?=($container_stats['State']['Running'] == 1) ? 'success' : 'danger';?>"> </span>
|
||||||
|
</li>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Postfix <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="postfix_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Dovecot <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="dovecot_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">SOGo <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_sogo_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="sogo_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-fail2ban-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Fail2ban <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_fail2ban_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="fail2ban_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Rspamd history <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed log-table" id="rspamd_history"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Autodiscover <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="autodiscover_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-watchdog-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Watchdog <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_watchdog_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="watchdog_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-acme-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">ACME <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_acme_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="acme_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-api-logs">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">API <span class="badge badge-info log-lines"></span>
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="100">+ 100</button>
|
||||||
|
<button class="btn btn-xs btn-default add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="1000">+ 1000</button>
|
||||||
|
<button class="btn btn-xs btn-default" id="refresh_api_log"><?=$lang['admin']['refresh'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-condensed" id="api_log"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- /tab-content -->
|
||||||
|
</div> <!-- /col-md-12 -->
|
||||||
|
</div> <!-- /row -->
|
||||||
|
</div> <!-- /container -->
|
||||||
|
<?php
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/debug.php';
|
||||||
|
?>
|
||||||
|
<script type='text/javascript'>
|
||||||
|
<?php
|
||||||
|
$lang_admin = json_encode($lang['admin']);
|
||||||
|
echo "var lang = ". $lang_admin . ";\n";
|
||||||
|
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
|
||||||
|
echo "var log_pagination_size = '". $LOG_PAGINATION_SIZE . "';\n";
|
||||||
|
|
||||||
|
?>
|
||||||
|
</script>
|
||||||
|
<script src="js/footable.min.js"></script>
|
||||||
|
<script src="js/debug.js"></script>
|
||||||
|
<?php
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
header('Location: /');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
|
@ -1,241 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once 'inc/prerequisites.inc.php';
|
|
||||||
require_once 'inc/spf.inc.php';
|
|
||||||
|
|
||||||
define('state_good', "✓");
|
|
||||||
define('state_missing', "✗");
|
|
||||||
define('state_nomatch', "?");
|
|
||||||
define('state_optional', "(optional)");
|
|
||||||
|
|
||||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
|
||||||
require_once("inc/header.inc.php");
|
|
||||||
|
|
||||||
$ch = curl_init('http://ip4.mailcow.email');
|
|
||||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
|
||||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
|
|
||||||
$ip = curl_exec($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
$ch = curl_init('http://ip6.mailcow.email');
|
|
||||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
|
|
||||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
|
|
||||||
$ip6 = curl_exec($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
$ptr = implode('.', array_reverse(explode('.', $ip))) . '.in-addr.arpa';
|
|
||||||
if (!empty($ip6)) {
|
|
||||||
$ip6_full = str_replace('::', str_repeat(':', 9-substr_count($ip6, ':')), $ip6);
|
|
||||||
$ip6_full = str_replace('::', ':0:', $ip6_full);
|
|
||||||
$ip6_full = str_replace('::', ':0:', $ip6_full);
|
|
||||||
$ptr6 = '';
|
|
||||||
foreach (explode(':', $ip6_full) as $part) {
|
|
||||||
$ptr6 .= str_pad($part, 4, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
$ptr6 = implode('.', array_reverse(str_split($ptr6, 1))) . '.ip6.arpa';
|
|
||||||
}
|
|
||||||
|
|
||||||
$https_port = strpos($_SERVER['HTTP_HOST'], ':');
|
|
||||||
if ($https_port === FALSE) {
|
|
||||||
$https_port = 443;
|
|
||||||
} else {
|
|
||||||
$https_port = substr($_SERVER['HTTP_HOST'], $https_port+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$records = array();
|
|
||||||
$records[] = array($mailcow_hostname, 'A', $ip);
|
|
||||||
$records[] = array($ptr, 'PTR', $mailcow_hostname);
|
|
||||||
if (!empty($ip6)) {
|
|
||||||
$records[] = array($mailcow_hostname, 'AAAA', $ip6);
|
|
||||||
$records[] = array($ptr6, 'PTR', $mailcow_hostname);
|
|
||||||
}
|
|
||||||
$domains = mailbox('get', 'domains');
|
|
||||||
foreach(mailbox('get', 'domains') as $domain) {
|
|
||||||
$domains = array_merge($domains, mailbox('get', 'alias_domains', $domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($autodiscover_config['sieve'])) {
|
|
||||||
$autodiscover_config['sieve'] = array('server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('SIEVE_PORT'))));
|
|
||||||
}
|
|
||||||
|
|
||||||
$records[] = array('_25._tcp.' . $autodiscover_config['smtp']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['smtp']['server'], 25, 1));
|
|
||||||
$records[] = array('_' . $https_port . '._tcp.' . $mailcow_hostname, 'TLSA', generate_tlsa_digest($mailcow_hostname, $https_port));
|
|
||||||
$records[] = array('_' . $autodiscover_config['pop3']['tlsport'] . '._tcp.' . $autodiscover_config['pop3']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['pop3']['server'], $autodiscover_config['pop3']['tlsport'], 1));
|
|
||||||
$records[] = array('_' . $autodiscover_config['imap']['tlsport'] . '._tcp.' . $autodiscover_config['imap']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['imap']['server'], $autodiscover_config['imap']['tlsport'], 1));
|
|
||||||
$records[] = array('_' . $autodiscover_config['smtp']['port'] . '._tcp.' . $autodiscover_config['smtp']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['smtp']['server'], $autodiscover_config['smtp']['port']));
|
|
||||||
$records[] = array('_' . $autodiscover_config['smtp']['tlsport'] . '._tcp.' . $autodiscover_config['smtp']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['smtp']['server'], $autodiscover_config['smtp']['tlsport'], 1));
|
|
||||||
$records[] = array('_' . $autodiscover_config['imap']['port'] . '._tcp.' . $autodiscover_config['imap']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['imap']['server'], $autodiscover_config['imap']['port']));
|
|
||||||
$records[] = array('_' . $autodiscover_config['pop3']['port'] . '._tcp.' . $autodiscover_config['pop3']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['pop3']['server'], $autodiscover_config['pop3']['port']));
|
|
||||||
$records[] = array('_' . $autodiscover_config['sieve']['port'] . '._tcp.' . $autodiscover_config['sieve']['server'], 'TLSA', generate_tlsa_digest($autodiscover_config['sieve']['server'], $autodiscover_config['sieve']['port'], 1));
|
|
||||||
|
|
||||||
foreach ($domains as $domain) {
|
|
||||||
$records[] = array($domain, 'MX', $mailcow_hostname);
|
|
||||||
$records[] = array('autodiscover.' . $domain, 'CNAME', $mailcow_hostname);
|
|
||||||
$records[] = array('_autodiscover._tcp.' . $domain, 'SRV', $mailcow_hostname . ' ' . $https_port);
|
|
||||||
$records[] = array('autoconfig.' . $domain, 'CNAME', $mailcow_hostname);
|
|
||||||
$records[] = array($domain, 'TXT', '<a href="http://www.openspf.org/SPF_Record_Syntax" target="_blank">SPF Record Syntax</a>', state_optional);
|
|
||||||
$records[] = array('_dmarc.' . $domain, 'TXT', '<a href="http://www.kitterman.com/dmarc/assistant.html" target="_blank">DMARC Assistant</a>', state_optional);
|
|
||||||
|
|
||||||
if (!empty($dkim = dkim('details', $domain))) {
|
|
||||||
$records[] = array($dkim['dkim_selector'] . '._domainkey.' . $domain, 'TXT', $dkim['dkim_txt']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$current_records = dns_get_record('_pop3._tcp.' . $domain, DNS_SRV);
|
|
||||||
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
|
|
||||||
if ($autodiscover_config['pop3']['tlsport'] != '110') {
|
|
||||||
$records[] = array('_pop3._tcp.' . $domain, 'SRV', $autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['tlsport']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$records[] = array('_pop3._tcp.' . $domain, 'SRV', '. 0');
|
|
||||||
}
|
|
||||||
$current_records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV);
|
|
||||||
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
|
|
||||||
if ($autodiscover_config['pop3']['port'] != '995') {
|
|
||||||
$records[] = array('_pop3s._tcp.' . $domain, 'SRV', $autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['port']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$records[] = array('_pop3s._tcp.' . $domain, 'SRV', '. 0');
|
|
||||||
}
|
|
||||||
if ($autodiscover_config['imap']['tlsport'] != '143') {
|
|
||||||
$records[] = array('_imap._tcp.' . $domain, 'SRV', $autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['tlsport']);
|
|
||||||
}
|
|
||||||
if ($autodiscover_config['imap']['port'] != '993') {
|
|
||||||
$records[] = array('_imaps._tcp.' . $domain, 'SRV', $autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['port']);
|
|
||||||
}
|
|
||||||
if ($autodiscover_config['smtp']['tlsport'] != '587') {
|
|
||||||
$records[] = array('_submission._tcp.' . $domain, 'SRV', $autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['tlsport']);
|
|
||||||
}
|
|
||||||
if ($autodiscover_config['smtp']['port'] != '465') {
|
|
||||||
$records[] = array('_smtps._tcp.' . $domain, 'SRV', $autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['port']);
|
|
||||||
}
|
|
||||||
if ($autodiscover_config['sieve']['port'] != '4190') {
|
|
||||||
$records[] = array('_sieve._tcp.' . $domain, 'SRV', $autodiscover_config['sieve']['server'] . ' ' . $autodiscover_config['sieve']['port']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$record_types = array(
|
|
||||||
'A' => DNS_A,
|
|
||||||
'AAAA' => DNS_AAAA,
|
|
||||||
'CNAME' => DNS_CNAME,
|
|
||||||
'MX' => DNS_MX,
|
|
||||||
'PTR' => DNS_PTR,
|
|
||||||
'SRV' => DNS_SRV,
|
|
||||||
'TXT' => DNS_TXT,
|
|
||||||
);
|
|
||||||
$data_field = array(
|
|
||||||
'A' => 'ip',
|
|
||||||
'AAAA' => 'ipv6',
|
|
||||||
'CNAME' => 'target',
|
|
||||||
'MX' => 'target',
|
|
||||||
'PTR' => 'target',
|
|
||||||
'SRV' => 'data',
|
|
||||||
'TLSA' => 'data',
|
|
||||||
'TXT' => 'txt',
|
|
||||||
);
|
|
||||||
?>
|
|
||||||
<div class="container">
|
|
||||||
<h3><?=$lang['diagnostics']['dns_records'];?></h3>
|
|
||||||
<p><?=$lang['diagnostics']['dns_records_24hours'];?></p>
|
|
||||||
<div class="table-responsive" id="dnstable">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tr> <th><?=$lang['diagnostics']['dns_records_name'];?></th> <th><?=$lang['diagnostics']['dns_records_type'];?></th> <th><?=$lang['diagnostics']['dns_records_data'];?></th ><th><?=$lang['diagnostics']['dns_records_status'];?></th> </tr>
|
|
||||||
<?php
|
|
||||||
foreach ($records as $record)
|
|
||||||
{
|
|
||||||
$record[1] = strtoupper($record[1]);
|
|
||||||
$state = state_missing;
|
|
||||||
if ($record[1] == 'TLSA') {
|
|
||||||
$currents = dns_get_record($record[0], 52, $_, $_, TRUE);
|
|
||||||
foreach ($currents as &$current) {
|
|
||||||
$current['type'] = 'TLSA';
|
|
||||||
$current['cert_usage'] = hexdec(bin2hex($current['data']{0}));
|
|
||||||
$current['selector'] = hexdec(bin2hex($current['data']{1}));
|
|
||||||
$current['match_type'] = hexdec(bin2hex($current['data']{2}));
|
|
||||||
$current['cert_data'] = bin2hex(substr($current['data'], 3));
|
|
||||||
$current['data'] = $current['cert_usage'] . ' ' . $current['selector'] . ' ' . $current['match_type'] . ' ' . $current['cert_data'];
|
|
||||||
}
|
|
||||||
unset($current);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$currents = dns_get_record($record[0], $record_types[$record[1]]);
|
|
||||||
if ($record[1] == 'SRV') {
|
|
||||||
foreach ($currents as &$current) {
|
|
||||||
if ($current['target'] == '') {
|
|
||||||
$current['target'] = '.';
|
|
||||||
$current['port'] = '0';
|
|
||||||
}
|
|
||||||
$current['data'] = $current['target'] . ' ' . $current['port'];
|
|
||||||
}
|
|
||||||
unset($current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($record[1] == 'CNAME' && count($currents) == 0) {
|
|
||||||
// A and AAAA are also valid instead of CNAME
|
|
||||||
$a = dns_get_record($record[0], DNS_A);
|
|
||||||
$cname = dns_get_record($record[2], DNS_A);
|
|
||||||
if (count($a) > 0 && count($cname) > 0) {
|
|
||||||
if ($a[0]['ip'] == $cname[0]['ip']) {
|
|
||||||
$currents = array(array('host' => $record[0], 'class' => 'IN', 'type' => 'CNAME', 'target' => $record[2]));
|
|
||||||
|
|
||||||
$aaaa = dns_get_record($record[0], DNS_AAAA);
|
|
||||||
$cname = dns_get_record($record[2], DNS_AAAA);
|
|
||||||
if (count($aaaa) == 0 || count($cname) == 0 || $aaaa[0]['ipv6'] != $cname[0]['ipv6']) {
|
|
||||||
$currents[0]['target'] = $aaaa[0]['ipv6'];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$currents = array(array('host' => $record[0], 'class' => 'IN', 'type' => 'CNAME', 'target' => $a[0]['ip']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($currents as $current) {
|
|
||||||
$current['type'] == strtoupper($current['type']);
|
|
||||||
if ($current['type'] != $record[1])
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif ($current['type'] == 'TXT' && strpos($record[0], '_dmarc.') === 0) {
|
|
||||||
$state = state_optional . '<br />' . $current[$data_field[$current['type']]];
|
|
||||||
}
|
|
||||||
else if ($current['type'] == 'TXT' && strpos($current['txt'], 'v=spf1') === 0) {
|
|
||||||
$state = state_optional . '<br />' . $current[$data_field[$current['type']]];
|
|
||||||
}
|
|
||||||
else if ($current['type'] != 'TXT' && isset($data_field[$current['type']]) && $state != state_good) {
|
|
||||||
$state = state_nomatch;
|
|
||||||
if ($current[$data_field[$current['type']]] == $record[2])
|
|
||||||
$state = state_good;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($record[3]) && $record[3] == state_optional && ($state == state_missing || $state == state_nomatch)) {
|
|
||||||
$state = state_optional;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($state == state_nomatch) {
|
|
||||||
$state = array();
|
|
||||||
foreach ($currents as $current) {
|
|
||||||
$state[] = $current[$data_field[$current['type']]];
|
|
||||||
}
|
|
||||||
$state = implode('<br />', $state);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo sprintf('<tr><td>%s</td><td>%s</td><td style="max-width: 300px; word-break: break-all">%s</td><td style="max-width: 150px; word-break: break-all">%s</td></tr>', $record[0], $record[1], $record[2], $state);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
require_once("inc/footer.inc.php");
|
|
||||||
} else {
|
|
||||||
header('Location: index.php');
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -608,6 +608,52 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif (isset($_GET['bcc']) && !empty($_GET["bcc"])) {
|
||||||
|
$bcc = intval($_GET["bcc"]);
|
||||||
|
$result = bcc('details', $bcc);
|
||||||
|
if (!empty($result)) {
|
||||||
|
?>
|
||||||
|
<h4>BCC map</h4>
|
||||||
|
<br />
|
||||||
|
<form class="form-horizontal" data-id="editbcc" role="form" method="post">
|
||||||
|
<input type="hidden" value="0" name="active">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="bcc_dest">BCC destination</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea id="bcc_dest" class="form-control" autocapitalize="none" autocorrect="off" rows="10" id="bcc_dest" name="bcc_dest" required><?=$result['bcc_dest'];?></textarea>
|
||||||
|
<small>BCC destinations can only be valid email addresses. Separated by whitespace, semicolon, new line or comma.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="type">Type:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select id="addFilterType" name="type" id="type" required>
|
||||||
|
<option value="sender" <?=($result['type'] == 'sender') ? 'selected' : null;?>>Sender map</option>
|
||||||
|
<option value="rcpt" <?=($result['type'] == 'rcpt') ? 'selected' : null;?>>Recipient map</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label><input type="checkbox" value="1" name="active" <?php if (isset($result['active_int']) && $result['active_int']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button class="btn btn-success" id="edit_selected" data-id="editbcc" data-item="<?=$bcc;?>" data-api-url='edit/bcc' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
?>
|
||||||
|
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin" || $_SESSION['mailcow_cc_role'] == "user") {
|
if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin" || $_SESSION['mailcow_cc_role'] == "user") {
|
||||||
if (isset($_GET['syncjob']) &&
|
if (isset($_GET['syncjob']) &&
|
||||||
|
@ -722,9 +768,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
elseif (isset($_GET['filter']) &&
|
||||||
if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin" || $_SESSION['mailcow_cc_role'] == "user") {
|
|
||||||
if (isset($_GET['filter']) &&
|
|
||||||
is_numeric($_GET['filter'])) {
|
is_numeric($_GET['filter'])) {
|
||||||
$id = $_GET["filter"];
|
$id = $_GET["filter"];
|
||||||
$result = mailbox('get', 'filter_details', $id);
|
$result = mailbox('get', 'filter_details', $id);
|
||||||
|
@ -774,7 +818,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||||
|
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^[a-z\-]{0,}-mailcow/', $_GET['service'])) {
|
||||||
|
if ($_GET['action'] == "start") {
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
$retry = 0;
|
||||||
|
while (docker($_GET['service'], 'info')['State']['Running'] != 1 && $retry <= 3) {
|
||||||
|
$response = docker($_GET['service'], 'post', 'start');
|
||||||
|
$response = json_decode($response, true);
|
||||||
|
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
|
||||||
|
if ($response['type'] == "success") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(1500000);
|
||||||
|
$retry++;
|
||||||
|
}
|
||||||
|
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Already running</span></b>' : $last_response;
|
||||||
|
}
|
||||||
|
if ($_GET['action'] == "stop") {
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
$retry = 0;
|
||||||
|
while (docker($_GET['service'], 'info')['State']['Running'] == 1 && $retry <= 3) {
|
||||||
|
$response = docker($_GET['service'], 'post', 'stop');
|
||||||
|
$response = json_decode($response, true);
|
||||||
|
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
|
||||||
|
if ($response['type'] == "success") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(1500000);
|
||||||
|
$retry++;
|
||||||
|
}
|
||||||
|
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Not running</span></b>' : $last_response;
|
||||||
|
}
|
||||||
|
if ($_GET['action'] == "restart") {
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
$response = docker($_GET['service'], 'post', 'restart');
|
||||||
|
$response = json_decode($response, true);
|
||||||
|
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
|
||||||
|
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Cannot restart container</span></b>' : $last_response;
|
||||||
|
}
|
||||||
|
if ($_GET['action'] == "logs") {
|
||||||
|
$lines = (empty($_GET['lines']) || !is_numeric($_GET['lines'])) ? 1000 : $_GET['lines'];
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
print_r(preg_split('/\n/', docker($_GET['service'], 'logs', $lines)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,402 @@
|
||||||
|
<?php
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/spf.inc.php';
|
||||||
|
|
||||||
|
define('state_good', '<span class="glyphicon glyphicon-ok text-success"></span>');
|
||||||
|
define('state_missing', '<span class="glyphicon glyphicon-remove text-danger"></span>');
|
||||||
|
define('state_nomatch', "?");
|
||||||
|
define('state_optional', " <sup>2</sup>");
|
||||||
|
|
||||||
|
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||||
|
|
||||||
|
$domains = mailbox('get', 'domains');
|
||||||
|
foreach(mailbox('get', 'domains') as $dn) {
|
||||||
|
$domains = array_merge($domains, mailbox('get', 'alias_domains', $dn));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['domain'])) {
|
||||||
|
if (is_valid_domain_name($_GET['domain'])) {
|
||||||
|
if (in_array($_GET['domain'], $domains)) {
|
||||||
|
$domain = $_GET['domain'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo "No such domain in context";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo "Invalid domain name";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init('http://ip4.mailcow.email');
|
||||||
|
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||||
|
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
|
||||||
|
$ip = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$ch = curl_init('http://ip6.mailcow.email');
|
||||||
|
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
|
||||||
|
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
|
||||||
|
$ip6 = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$ptr = implode('.', array_reverse(explode('.', $ip))) . '.in-addr.arpa';
|
||||||
|
if (!empty($ip6)) {
|
||||||
|
$ip6_full = str_replace('::', str_repeat(':', 9-substr_count($ip6, ':')), $ip6);
|
||||||
|
$ip6_full = str_replace('::', ':0:', $ip6_full);
|
||||||
|
$ip6_full = str_replace('::', ':0:', $ip6_full);
|
||||||
|
$ptr6 = '';
|
||||||
|
foreach (explode(':', $ip6_full) as $part) {
|
||||||
|
$ptr6 .= str_pad($part, 4, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
$ptr6 = implode('.', array_reverse(str_split($ptr6, 1))) . '.ip6.arpa';
|
||||||
|
}
|
||||||
|
|
||||||
|
$https_port = strpos($_SERVER['HTTP_HOST'], ':');
|
||||||
|
if ($https_port === FALSE) {
|
||||||
|
$https_port = 443;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$https_port = substr($_SERVER['HTTP_HOST'], $https_port+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($autodiscover_config['sieve'])) {
|
||||||
|
$autodiscover_config['sieve'] = array('server' => $mailcow_hostname, 'port' => array_pop(explode(':', getenv('SIEVE_PORT'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init records array
|
||||||
|
$spf_link = '<a href="http://www.openspf.org/SPF_Record_Syntax" target="_blank">SPF Record Syntax</a>';
|
||||||
|
$dmarc_link = '<a href="http://www.kitterman.com/dmarc/assistant.html" target="_blank">DMARC Assistant</a>';
|
||||||
|
|
||||||
|
$records = array();
|
||||||
|
$records[] = array(
|
||||||
|
$mailcow_hostname,
|
||||||
|
'A',
|
||||||
|
$ip
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
$ptr,
|
||||||
|
'PTR',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
if (!empty($ip6)) {
|
||||||
|
$records[] = array(
|
||||||
|
$mailcow_hostname,
|
||||||
|
'AAAA',
|
||||||
|
$ip6
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
$ptr6,
|
||||||
|
'PTR',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$records[] = array(
|
||||||
|
'_25._tcp.' . $autodiscover_config['smtp']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['smtp']['server'], 25, 1)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $https_port . '._tcp.' . $mailcow_hostname,
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($mailcow_hostname, $https_port)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['pop3']['tlsport'] . '._tcp.' . $autodiscover_config['pop3']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['pop3']['server'], $autodiscover_config['pop3']['tlsport'], 1)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['imap']['tlsport'] . '._tcp.' . $autodiscover_config['imap']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['imap']['server'], $autodiscover_config['imap']['tlsport'], 1)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['smtp']['port'] . '._tcp.' . $autodiscover_config['smtp']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['smtp']['server'], $autodiscover_config['smtp']['port'])
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['smtp']['tlsport'] . '._tcp.' . $autodiscover_config['smtp']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['smtp']['server'], $autodiscover_config['smtp']['tlsport'], 1)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['imap']['port'] . '._tcp.' . $autodiscover_config['imap']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['imap']['server'], $autodiscover_config['imap']['port'])
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['pop3']['port'] . '._tcp.' . $autodiscover_config['pop3']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['pop3']['server'], $autodiscover_config['pop3']['port'])
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_' . $autodiscover_config['sieve']['port'] . '._tcp.' . $autodiscover_config['sieve']['server'],
|
||||||
|
'TLSA',
|
||||||
|
generate_tlsa_digest($autodiscover_config['sieve']['server'], $autodiscover_config['sieve']['port'], 1)
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
$domain,
|
||||||
|
'MX',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'autodiscover.' . $domain,
|
||||||
|
'CNAME',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_autodiscover._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$mailcow_hostname . ' ' . $https_port
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'autoconfig.' . $domain,
|
||||||
|
'CNAME',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
$domain,
|
||||||
|
'TXT',
|
||||||
|
$spf_link,
|
||||||
|
state_optional
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_dmarc.' . $domain,
|
||||||
|
'TXT',
|
||||||
|
$dmarc_link,
|
||||||
|
state_optional
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($dkim = dkim('details', $domain))) {
|
||||||
|
$records[] = array(
|
||||||
|
$dkim['dkim_selector'] . '._domainkey.' . $domain,
|
||||||
|
'TXT',
|
||||||
|
$dkim['dkim_txt']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$current_records = dns_get_record('_pop3._tcp.' . $domain, DNS_SRV);
|
||||||
|
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
|
||||||
|
if ($autodiscover_config['pop3']['tlsport'] != '110') {
|
||||||
|
$records[] = array(
|
||||||
|
'_pop3._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['tlsport']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$records[] = array(
|
||||||
|
'_pop3._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
'. 0'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$current_records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV);
|
||||||
|
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
|
||||||
|
if ($autodiscover_config['pop3']['port'] != '995') {
|
||||||
|
$records[] = array(
|
||||||
|
'_pop3s._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['port']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$records[] = array(
|
||||||
|
'_pop3s._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
'. 0'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($autodiscover_config['imap']['tlsport'] != '143') {
|
||||||
|
$records[] = array(
|
||||||
|
'_imap._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['tlsport']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($autodiscover_config['imap']['port'] != '993') {
|
||||||
|
$records[] = array(
|
||||||
|
'_imaps._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['port']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($autodiscover_config['smtp']['tlsport'] != '587') {
|
||||||
|
$records[] = array(
|
||||||
|
'_submission._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['tlsport']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($autodiscover_config['smtp']['port'] != '465') {
|
||||||
|
$records[] = array(
|
||||||
|
'_smtps._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['port']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($autodiscover_config['sieve']['port'] != '4190') {
|
||||||
|
$records[] = array(
|
||||||
|
'_sieve._tcp.' . $domain,
|
||||||
|
'SRV',
|
||||||
|
$autodiscover_config['sieve']['server'] . ' ' . $autodiscover_config['sieve']['port']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$record_types = array(
|
||||||
|
'A' => DNS_A,
|
||||||
|
'AAAA' => DNS_AAAA,
|
||||||
|
'CNAME' => DNS_CNAME,
|
||||||
|
'MX' => DNS_MX,
|
||||||
|
'PTR' => DNS_PTR,
|
||||||
|
'SRV' => DNS_SRV,
|
||||||
|
'TXT' => DNS_TXT,
|
||||||
|
);
|
||||||
|
$data_field = array(
|
||||||
|
'A' => 'ip',
|
||||||
|
'AAAA' => 'ipv6',
|
||||||
|
'CNAME' => 'target',
|
||||||
|
'MX' => 'target',
|
||||||
|
'PTR' => 'target',
|
||||||
|
'SRV' => 'data',
|
||||||
|
'TLSA' => 'data',
|
||||||
|
'TXT' => 'txt',
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<div class="table-responsive" id="dnstable">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<tr>
|
||||||
|
<th><?=$lang['diagnostics']['dns_records_name'];?></th>
|
||||||
|
<th><?=$lang['diagnostics']['dns_records_type'];?></th>
|
||||||
|
<th><?=$lang['diagnostics']['dns_records_data'];?></th>
|
||||||
|
<th><?=$lang['diagnostics']['dns_records_status'];?></th>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$record[1] = strtoupper($record[1]);
|
||||||
|
$state = state_missing;
|
||||||
|
if ($record[1] == 'TLSA') {
|
||||||
|
$currents = dns_get_record($record[0], 52, $_, $_, TRUE);
|
||||||
|
foreach ($currents as &$current) {
|
||||||
|
$current['type'] = 'TLSA';
|
||||||
|
$current['cert_usage'] = hexdec(bin2hex($current['data']{0}));
|
||||||
|
$current['selector'] = hexdec(bin2hex($current['data']{1}));
|
||||||
|
$current['match_type'] = hexdec(bin2hex($current['data']{2}));
|
||||||
|
$current['cert_data'] = bin2hex(substr($current['data'], 3));
|
||||||
|
$current['data'] = $current['cert_usage'] . ' ' . $current['selector'] . ' ' . $current['match_type'] . ' ' . $current['cert_data'];
|
||||||
|
}
|
||||||
|
unset($current);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$currents = dns_get_record($record[0], $record_types[$record[1]]);
|
||||||
|
if ($record[1] == 'SRV') {
|
||||||
|
foreach ($currents as &$current) {
|
||||||
|
if ($current['target'] == '') {
|
||||||
|
$current['target'] = '.';
|
||||||
|
$current['port'] = '0';
|
||||||
|
}
|
||||||
|
$current['data'] = $current['target'] . ' ' . $current['port'];
|
||||||
|
}
|
||||||
|
unset($current);
|
||||||
|
}
|
||||||
|
elseif ($record[1] == 'TXT') {
|
||||||
|
foreach ($currents as &$current) {
|
||||||
|
unset($current);
|
||||||
|
}
|
||||||
|
unset($current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($record[1] == 'CNAME' && count($currents) == 0) {
|
||||||
|
// A and AAAA are also valid instead of CNAME
|
||||||
|
$a = dns_get_record($record[0], DNS_A);
|
||||||
|
$cname = dns_get_record($record[2], DNS_A);
|
||||||
|
if (count($a) > 0 && count($cname) > 0) {
|
||||||
|
if ($a[0]['ip'] == $cname[0]['ip']) {
|
||||||
|
$currents = array(array('host' => $record[0], 'class' => 'IN', 'type' => 'CNAME', 'target' => $record[2]));
|
||||||
|
$aaaa = dns_get_record($record[0], DNS_AAAA);
|
||||||
|
$cname = dns_get_record($record[2], DNS_AAAA);
|
||||||
|
if (count($aaaa) == 0 || count($cname) == 0 || $aaaa[0]['ipv6'] != $cname[0]['ipv6']) {
|
||||||
|
$currents[0]['target'] = $aaaa[0]['ipv6'] . ' <sup>1</sup>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$currents = array(array('host' => $record[0], 'class' => 'IN', 'type' => 'CNAME', 'target' => $a[0]['ip'] . ' <sup>1</sup>'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($currents as &$current) {
|
||||||
|
if ($current['type'] == 'TXT' &&
|
||||||
|
stripos($current['txt'], 'v=dmarc') === 0 &&
|
||||||
|
$record[2] == $dmarc_link) {
|
||||||
|
$current['txt'] = str_replace(' ', '', $current['txt']);
|
||||||
|
$state = $current[$data_field[$current['type']]] . state_optional;
|
||||||
|
}
|
||||||
|
elseif ($current['type'] == 'TXT' &&
|
||||||
|
stripos($current['txt'], 'v=spf' &&
|
||||||
|
$record[2] == $spf_link) === 0) {
|
||||||
|
$state = $current[$data_field[$current['type']]] . state_optional;
|
||||||
|
}
|
||||||
|
elseif ($current['type'] == 'TXT' &&
|
||||||
|
stripos($current['txt'], 'v=dkim') === 0 &&
|
||||||
|
stripos($record[2], 'v=dkim') === 0) {
|
||||||
|
$current['txt'] = str_replace(' ', '', $current['txt']);
|
||||||
|
if ($current[$data_field[$current['type']]] == $record[2]) {
|
||||||
|
$state = state_good;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($current['type'] != 'TXT' &&
|
||||||
|
isset($data_field[$current['type']]) && $state != state_good) {
|
||||||
|
$state = state_nomatch;
|
||||||
|
if ($current[$data_field[$current['type']]] == $record[2]) {
|
||||||
|
$state = state_good;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($current);
|
||||||
|
|
||||||
|
if (isset($record[3]) &&
|
||||||
|
$record[3] == state_optional &&
|
||||||
|
($state == state_missing || $state == state_nomatch)) {
|
||||||
|
$state = state_optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($state == state_nomatch) {
|
||||||
|
$state = array();
|
||||||
|
foreach ($currents as $current) {
|
||||||
|
$state[] = $current[$data_field[$current['type']]];
|
||||||
|
}
|
||||||
|
$state = implode('<br />', $state);
|
||||||
|
}
|
||||||
|
echo sprintf('<tr>
|
||||||
|
<td>%s</td>
|
||||||
|
<td>%s</td>
|
||||||
|
<td class="dns-found">%s</td>
|
||||||
|
<td class="dns-recommended">%s</td>
|
||||||
|
</tr>', $record[0], $record[1], $record[2], $state);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">
|
||||||
|
<sup>1</sup> <?=$lang['diagnostics']['cname_from_a'];?><br />
|
||||||
|
<sup>2</sup> <?=$lang['diagnostics']['optional'];?>
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
echo "Session invalid";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?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['type']) && isset($_GET['msg'])) {
|
||||||
|
global $mailcow_hostname;
|
||||||
|
//empty
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||||
|
if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
function rrmdir($src) {
|
||||||
|
$dir = opendir($src);
|
||||||
|
while(false !== ( $file = readdir($dir)) ) {
|
||||||
|
if (( $file != '.' ) && ( $file != '..' )) {
|
||||||
|
$full = $src . '/' . $file;
|
||||||
|
if ( is_dir($full) ) {
|
||||||
|
rrmdir($full);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unlink($full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dir);
|
||||||
|
rmdir($src);
|
||||||
|
}
|
||||||
|
if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
|
||||||
|
$tmpdir = '/tmp/' . $_GET['id'] . '/';
|
||||||
|
$mailc = quarantaine('details', $_GET['id']);
|
||||||
|
if (strlen($mailc['msg']) > 10485760) {
|
||||||
|
echo json_encode(array('error' => 'Message size exceeds 10 MiB.'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!empty($mailc['msg'])) {
|
||||||
|
// Init message array
|
||||||
|
$data = array();
|
||||||
|
// Init parser
|
||||||
|
$mail_parser = new PhpMimeMailParser\Parser();
|
||||||
|
// Load msg to parser
|
||||||
|
$mail_parser->setText($mailc['msg']);
|
||||||
|
// Get text/plain content
|
||||||
|
$data['text_plain'] = $mail_parser->getMessageBody('text');
|
||||||
|
// Get subject
|
||||||
|
$data['subject'] = $mail_parser->getHeader('subject');
|
||||||
|
// Get attachments
|
||||||
|
if (is_dir($tmpdir)) {
|
||||||
|
rrmdir($tmpdir);
|
||||||
|
}
|
||||||
|
mkdir('/tmp/' . $_GET['id']);
|
||||||
|
$mail_parser->saveAttachments($tmpdir, true);
|
||||||
|
$atts = $mail_parser->getAttachments(true);
|
||||||
|
if (count($atts) > 0) {
|
||||||
|
foreach ($atts as $key => $val) {
|
||||||
|
$data['attachments'][$key] = array(
|
||||||
|
// Index
|
||||||
|
// 0 => file name
|
||||||
|
// 1 => mime type
|
||||||
|
// 2 => file size
|
||||||
|
// 3 => vt link by sha256
|
||||||
|
$val->getFilename(),
|
||||||
|
$val->getContentType(),
|
||||||
|
filesize($tmpdir . $val->getFilename()),
|
||||||
|
'https://www.virustotal.com/file/' . hash_file('SHA256', $tmpdir . $val->getFilename()) . '/analysis/'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($_GET['att'])) {
|
||||||
|
$dl_id = intval($_GET['att']);
|
||||||
|
$dl_filename = $data['attachments'][$dl_id][0];
|
||||||
|
if (!is_dir($tmpdir . $dl_filename) && file_exists($tmpdir . $dl_filename)) {
|
||||||
|
header('Pragma: public');
|
||||||
|
header('Expires: 0');
|
||||||
|
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||||
|
header('Cache-Control: private', false);
|
||||||
|
header('Content-Type: ' . $data['attachments'][$dl_id][1]);
|
||||||
|
header('Content-Disposition: attachment; filename="'. $dl_filename . '";');
|
||||||
|
header('Content-Transfer-Encoding: binary');
|
||||||
|
header('Content-Length: ' . $data['attachments'][$dl_id][2]);
|
||||||
|
readfile($tmpdir . $dl_filename);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
session_start();
|
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
|
||||||
header('Content-Type: text/html; charset=utf-8');
|
|
||||||
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') {
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_GET['ACTION'] == "start") {
|
|
||||||
$retry = 0;
|
|
||||||
while (docker('sogo-mailcow', 'info')['State']['Running'] != 1 && $retry <= 3) {
|
|
||||||
$response = docker('sogo-mailcow', 'post', 'start');
|
|
||||||
$response = json_decode($response, true);
|
|
||||||
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
|
|
||||||
if ($response['type'] == "success") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(1500000);
|
|
||||||
$retry++;
|
|
||||||
}
|
|
||||||
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Already running</span></b>' : $last_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_GET['ACTION'] == "stop") {
|
|
||||||
$retry = 0;
|
|
||||||
while (docker('sogo-mailcow', 'info')['State']['Running'] == 1 && $retry <= 3) {
|
|
||||||
$response = docker('sogo-mailcow', 'post', 'stop');
|
|
||||||
$response = json_decode($response, true);
|
|
||||||
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
|
|
||||||
if ($response['type'] == "success") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(1500000);
|
|
||||||
$retry++;
|
|
||||||
}
|
|
||||||
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Not running</span></b>' : $last_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -8,10 +8,12 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/footer.php';
|
||||||
<script src="/js/bootstrap-select.min.js"></script>
|
<script src="/js/bootstrap-select.min.js"></script>
|
||||||
<script src="/js/bootstrap-filestyle.min.js"></script>
|
<script src="/js/bootstrap-filestyle.min.js"></script>
|
||||||
<script src="/js/notifications.min.js"></script>
|
<script src="/js/notifications.min.js"></script>
|
||||||
|
<script src="/js/formcache.min.js"></script>
|
||||||
<script src="/js/numberedtextarea.min.js"></script>
|
<script src="/js/numberedtextarea.min.js"></script>
|
||||||
<script src="/js/u2f-api.js"></script>
|
<script src="/js/u2f-api.js"></script>
|
||||||
<script src="/js/api.js"></script>
|
<script src="/js/api.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
var loading_text = '<?= $lang['footer']['loading']; ?>'
|
||||||
$(window).scroll(function() {
|
$(window).scroll(function() {
|
||||||
sessionStorage.scrollTop = $(this).scrollTop();
|
sessionStorage.scrollTop = $(this).scrollTop();
|
||||||
});
|
});
|
||||||
|
@ -26,11 +28,19 @@ $(document).ready(function() {
|
||||||
msg = $('<span/>').html(message).text();
|
msg = $('<span/>').html(message).text();
|
||||||
if (type == 'danger') {
|
if (type == 'danger') {
|
||||||
auto_hide = 0;
|
auto_hide = 0;
|
||||||
|
$('#' + localStorage.getItem("add_modal")).modal('show');
|
||||||
|
localStorage.removeItem("add_modal");
|
||||||
} else {
|
} else {
|
||||||
auto_hide = 5000;
|
auto_hide = 5000;
|
||||||
}
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: '/inc/ajax/log_driver.php',
|
||||||
|
data: {"type": type,"msg": msg},
|
||||||
|
type: "GET"
|
||||||
|
});
|
||||||
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
||||||
}
|
}
|
||||||
|
$('[data-cached-form="true"]').formcache({key: $(this).data('id')});
|
||||||
<?php if (isset($_SESSION['return'])): ?>
|
<?php if (isset($_SESSION['return'])): ?>
|
||||||
mailcow_alert_box(<?=json_encode($_SESSION['return']['msg']); ?>, "<?= $_SESSION['return']['type']; ?>");
|
mailcow_alert_box(<?=json_encode($_SESSION['return']['msg']); ?>, "<?= $_SESSION['return']['type']; ?>");
|
||||||
<?php endif; unset($_SESSION['return']); ?>
|
<?php endif; unset($_SESSION['return']); ?>
|
||||||
|
@ -40,6 +50,7 @@ $(document).ready(function() {
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
keyboard: false
|
keyboard: false
|
||||||
});
|
});
|
||||||
|
$('#u2f_status_auth').html('<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> Initializing, please wait...</p>');
|
||||||
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
|
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
|
||||||
$(this).find('#token').focus();
|
$(this).find('#token').focus();
|
||||||
// If U2F
|
// If U2F
|
||||||
|
@ -49,20 +60,21 @@ $(document).ready(function() {
|
||||||
cache: false,
|
cache: false,
|
||||||
dataType: 'script',
|
dataType: 'script',
|
||||||
url: "/api/v1/get/u2f-authentication/<?= (isset($_SESSION['pending_mailcow_cc_username'])) ? $_SESSION['pending_mailcow_cc_username'] : null; ?>",
|
url: "/api/v1/get/u2f-authentication/<?= (isset($_SESSION['pending_mailcow_cc_username'])) ? $_SESSION['pending_mailcow_cc_username'] : null; ?>",
|
||||||
success: function(data){
|
complete: function(data){
|
||||||
|
$('#u2f_status_auth').html('<?=$lang['tfa']['waiting_usb_auth'];?>');
|
||||||
data;
|
data;
|
||||||
|
setTimeout(function() {
|
||||||
|
console.log("Ready to authenticate");
|
||||||
|
u2f.sign(appId, challenge, registeredKeys, function(data) {
|
||||||
|
var form = document.getElementById('u2f_auth_form');
|
||||||
|
var auth = document.getElementById('u2f_auth_data');
|
||||||
|
console.log("Authenticate callback", data);
|
||||||
|
auth.value = JSON.stringify(data);
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setTimeout(function() {
|
|
||||||
console.log("sign: ", req);
|
|
||||||
u2f.sign(req, function(data) {
|
|
||||||
var form = document.getElementById('u2f_auth_form');
|
|
||||||
var auth = document.getElementById('u2f_auth_data');
|
|
||||||
console.log("Authenticate callback", data);
|
|
||||||
auth.value = JSON.stringify(data);
|
|
||||||
form.submit();
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
@ -81,32 +93,34 @@ $(document).ready(function() {
|
||||||
if ($(this).val() == "u2f") {
|
if ($(this).val() == "u2f") {
|
||||||
$('#U2FModal').modal('show');
|
$('#U2FModal').modal('show');
|
||||||
$("option:selected").prop("selected", false);
|
$("option:selected").prop("selected", false);
|
||||||
|
$('#u2f_status_reg').html('<p><span class="glyphicon glyphicon-refresh glyphicon-spin"></span> Initializing, please wait...</p>');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
cache: false,
|
cache: false,
|
||||||
dataType: 'script',
|
dataType: 'script',
|
||||||
url: "/api/v1/get/u2f-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? $_SESSION['mailcow_cc_username'] : null; ?>",
|
url: "/api/v1/get/u2f-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? $_SESSION['mailcow_cc_username'] : null; ?>",
|
||||||
success: function(data){
|
complete: function(data){
|
||||||
data;
|
data;
|
||||||
|
setTimeout(function() {
|
||||||
|
console.log("Ready to register");
|
||||||
|
$('#u2f_status_reg').html('<?=$lang['tfa']['waiting_usb_register'];?>');
|
||||||
|
u2f.register(appId, registerRequests, registeredKeys, function(deviceResponse) {
|
||||||
|
var form = document.getElementById('u2f_reg_form');
|
||||||
|
var reg = document.getElementById('u2f_register_data');
|
||||||
|
console.log("Register callback: ", data);
|
||||||
|
if (deviceResponse.errorCode && deviceResponse.errorCode != 0) {
|
||||||
|
var u2f_return_code = document.getElementById('u2f_return_code');
|
||||||
|
u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
|
||||||
|
if (deviceResponse.errorCode == "4") { deviceResponse.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle"; }
|
||||||
|
u2f_return_code.innerHTML = 'Error code: ' + deviceResponse.errorCode;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reg.value = JSON.stringify(deviceResponse);
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setTimeout(function() {
|
|
||||||
console.log("Register: ", req);
|
|
||||||
u2f.register([req], sigs, function(data) {
|
|
||||||
var form = document.getElementById('u2f_reg_form');
|
|
||||||
var reg = document.getElementById('u2f_register_data');
|
|
||||||
console.log("Register callback", data);
|
|
||||||
if (data.errorCode && data.errorCode != 0) {
|
|
||||||
var u2f_return_code = document.getElementById('u2f_return_code');
|
|
||||||
u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
|
|
||||||
if (data.errorCode == "4") { data.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle"; }
|
|
||||||
u2f_return_code.innerHTML = 'Error code: ' + data.errorCode;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
reg.value = JSON.stringify(data);
|
|
||||||
form.submit();
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
if ($(this).val() == "none") {
|
if ($(this).val() == "none") {
|
||||||
$('#DisableTFAModal').modal('show');
|
$('#DisableTFAModal').modal('show');
|
||||||
|
@ -114,13 +128,8 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Activate tooltips
|
|
||||||
$(function () {
|
$(function () {
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
})
|
|
||||||
// Hide alerts after n seconds
|
|
||||||
$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){
|
|
||||||
$("#alert-fade").alert('close');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remember last navigation pill
|
// Remember last navigation pill
|
||||||
|
@ -149,7 +158,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Disable submit after submitting form
|
// Disable submit after submitting form (not API driven buttons)
|
||||||
$('form').submit(function() {
|
$('form').submit(function() {
|
||||||
if ($('form button[type="submit"]').data('submitted') == '1') {
|
if ($('form button[type="submit"]').data('submitted') == '1') {
|
||||||
return false;
|
return false;
|
||||||
|
@ -169,36 +178,32 @@ $(document).ready(function() {
|
||||||
// Init Bootstrap Selectpicker
|
// Init Bootstrap Selectpicker
|
||||||
$('select').selectpicker();
|
$('select').selectpicker();
|
||||||
|
|
||||||
// Trigger SOGo restart
|
// Trigger container restart
|
||||||
$('#triggerRestartSogo').click(function(){
|
$('#RestartContainer').on('show.bs.modal', function(e) {
|
||||||
$(this).prop("disabled",true);
|
var container = $(e.relatedTarget).data('container');
|
||||||
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
|
$('#containerName').text(container);
|
||||||
$('#statusTriggerRestartSogo').text('Stopping SOGo workers, this may take a while... ');
|
$('#triggerRestartContainer').click(function(){
|
||||||
$.ajax({
|
$(this).prop("disabled",true);
|
||||||
method: 'get',
|
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
|
||||||
url: '/inc/ajax/sogo_ctrl.php',
|
$('#statusTriggerRestartContainer').text('Restarting container, this may take a while... ');
|
||||||
data: {
|
$.ajax({
|
||||||
'ajax': true,
|
method: 'get',
|
||||||
'ACTION': 'stop'
|
url: '/inc/ajax/container_ctrl.php',
|
||||||
},
|
timeout: 10000,
|
||||||
success: function(data) {
|
data: {
|
||||||
$('#statusTriggerRestartSogo').append(data);
|
'service': container,
|
||||||
$('#statusTriggerRestartSogo').append('<br>Starting SOGo...');
|
'action': 'restart'
|
||||||
$.ajax({
|
},
|
||||||
method: 'get',
|
error: function() {
|
||||||
url: '/inc/ajax/sogo_ctrl.php',
|
window.location = window.location.href.split("#")[0];
|
||||||
data: {
|
},
|
||||||
'ajax': true,
|
success: function(data) {
|
||||||
'ACTION': 'start'
|
$('#statusTriggerRestartContainer').append(data);
|
||||||
},
|
$('#triggerRestartContainer').html('<span class="glyphicon glyphicon-ok"></span> ');
|
||||||
success: function(data) {
|
}
|
||||||
$('#statusTriggerRestartSogo').append(data);
|
});
|
||||||
$('#triggerRestartSogo').html('<span class="glyphicon glyphicon-ok"></span> ');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
|
||||||
// CSRF
|
// CSRF
|
||||||
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');
|
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');
|
||||||
|
|
|
@ -0,0 +1,292 @@
|
||||||
|
<?php
|
||||||
|
function bcc($_action, $_data = null, $attr = null) {
|
||||||
|
global $pdo;
|
||||||
|
global $lang;
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch ($_action) {
|
||||||
|
case 'add':
|
||||||
|
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$local_dest = strtolower(trim($_data['local_dest']));
|
||||||
|
$bcc_dest = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['bcc_dest']));
|
||||||
|
$active = intval($_data['active']);
|
||||||
|
$type = $_data['type'];
|
||||||
|
if ($type != 'sender' && $type != 'rcpt') {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Invalid BCC map type'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (empty($bcc_dest)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'BCC destination cannot be empty'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (is_valid_domain_name($local_dest)) {
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$domain = idn_to_ascii($local_dest);
|
||||||
|
$local_dest_sane = '@' . idn_to_ascii($local_dest);
|
||||||
|
}
|
||||||
|
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$domain = mailbox('get', 'mailbox_details', $local_dest)['domain'];
|
||||||
|
if (empty($domain)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$local_dest_sane = $local_dest;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($bcc_dest as &$bcc_dest_e) {
|
||||||
|
if (!filter_var($bcc_dest_e, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$bcc_dest_e = null;;
|
||||||
|
}
|
||||||
|
$bcc_dest_e = strtolower($bcc_dest_e);
|
||||||
|
}
|
||||||
|
$bcc_dest = array_filter($bcc_dest);
|
||||||
|
$bcc_dest = implode(",", $bcc_dest);
|
||||||
|
if (empty($bcc_dest)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'BCC map destination cannot be empty'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
|
||||||
|
WHERE `local_dest` = :local_dest AND `type` = :type");
|
||||||
|
$stmt->execute(array(':local_dest' => $local_dest_sane, ':type' => $type));
|
||||||
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($num_results != 0) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'A BCC map entry "' . htmlspecialchars($local_dest_sane) . '" exists for type "' . $type . '"'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `bcc_maps` (`local_dest`, `bcc_dest`, `domain`, `active`, `type`) VALUES
|
||||||
|
(:local_dest, :bcc_dest, :domain, :active, :type)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':local_dest' => $local_dest_sane,
|
||||||
|
':bcc_dest' => $bcc_dest,
|
||||||
|
':domain' => $domain,
|
||||||
|
':active' => $active,
|
||||||
|
':type' => $type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'BCC map entry saved'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$ids = (array)$_data['id'];
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$is_now = bcc('details', $id);
|
||||||
|
if (!empty($is_now)) {
|
||||||
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||||
|
$bcc_dest = (!empty($_data['bcc_dest'])) ? $_data['bcc_dest'] : $is_now['bcc_dest'];
|
||||||
|
$local_dest = $is_now['local_dest'];
|
||||||
|
$type = (!empty($_data['type'])) ? $_data['type'] : $is_now['type'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$bcc_dest = array_map('trim', preg_split( "/( |,|;|\n)/", $bcc_dest));
|
||||||
|
$active = intval($_data['active']);
|
||||||
|
foreach ($bcc_dest as &$bcc_dest_e) {
|
||||||
|
if (!filter_var($bcc_dest_e, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$bcc_dest_e = null;;
|
||||||
|
}
|
||||||
|
$bcc_dest_e = strtolower($bcc_dest_e);
|
||||||
|
}
|
||||||
|
$bcc_dest = array_filter($bcc_dest);
|
||||||
|
$bcc_dest = implode(",", $bcc_dest);
|
||||||
|
if (empty($bcc_dest)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'BCC map destination cannot be empty'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
|
||||||
|
WHERE `local_dest` = :local_dest AND `type` = :type");
|
||||||
|
$stmt->execute(array(':local_dest' => $local_dest, ':type' => $type));
|
||||||
|
$id_now = $stmt->fetch(PDO::FETCH_ASSOC)['id'];
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isset($id_now) && $id_now != $id) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'A BCC map entry ' . htmlspecialchars($local_dest) . ' exists for this type'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("UPDATE `bcc_maps` SET `bcc_dest` = :bcc_dest, `active` = :active, `type` = :type WHERE `id`= :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':bcc_dest' => $bcc_dest,
|
||||||
|
':active' => $active,
|
||||||
|
':type' => $type,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'BCC map entry edited'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'details':
|
||||||
|
$bccdata = array();
|
||||||
|
$id = intval($_data);
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`,
|
||||||
|
`local_dest`,
|
||||||
|
`bcc_dest`,
|
||||||
|
`active` AS `active_int`,
|
||||||
|
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
|
||||||
|
`type`,
|
||||||
|
`created`,
|
||||||
|
`domain`,
|
||||||
|
`modified` FROM `bcc_maps`
|
||||||
|
WHERE `id` = :id");
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
$bccdata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $bccdata['domain'])) {
|
||||||
|
$bccdata = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $bccdata;
|
||||||
|
break;
|
||||||
|
case 'get':
|
||||||
|
$bccdata = array();
|
||||||
|
$all_items = array();
|
||||||
|
$id = intval($_data);
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("SELECT `id`, `domain` FROM `bcc_maps`");
|
||||||
|
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($all_items as $i) {
|
||||||
|
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $i['domain'])) {
|
||||||
|
$bccdata[] = $i['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$all_items = null;
|
||||||
|
return $bccdata;
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
$ids = (array)$_data['id'];
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (!is_numeric($id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("SELECT `domain` FROM `bcc_maps` WHERE id = :id");
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
$domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `id`= :id");
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Deleted BCC map id/s ' . implode(', ', $ids)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
function customize($_action, $_item, $_data = null) {
|
function customize($_action, $_item, $_data = null) {
|
||||||
global $redis;
|
global $redis;
|
||||||
global $lang;
|
global $lang;
|
||||||
|
@ -19,7 +18,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
if (file_exists($_data['main_logo']['tmp_name']) !== true) {
|
if (file_exists($_data['main_logo']['tmp_name']) !== true) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Cannot validate image file: Temporary file not found'
|
'msg' => $lang['danger']['img_tmp_missing']
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +26,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
if ($image->valid() !== true) {
|
if ($image->valid() !== true) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Cannot validate image file'
|
'msg' => $lang['danger']['img_invalid']
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +35,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
catch (ImagickException $e) {
|
catch (ImagickException $e) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Cannot validate image file'
|
'msg' => $lang['danger']['img_invalid']
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +43,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
else {
|
else {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Invalid mime type'
|
'msg' => $lang['danger']['invalid_mime_type']
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +59,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
}
|
}
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'msg' => 'File uploaded successfully'
|
'msg' => $lang['success']['upload_success']
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +77,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
$apps = (array)$_data['app'];
|
$apps = (array)$_data['app'];
|
||||||
$links = (array)$_data['href'];
|
$links = (array)$_data['href'];
|
||||||
$out = array();
|
$out = array();
|
||||||
if (count($apps) == count($links)) {;
|
if (count($apps) == count($links)) {
|
||||||
for ($i = 0; $i < count($apps); $i++) {
|
for ($i = 0; $i < count($apps); $i++) {
|
||||||
$out[] = array($apps[$i] => $links[$i]);
|
$out[] = array($apps[$i] => $links[$i]);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +94,28 @@ function customize($_action, $_item, $_data = null) {
|
||||||
}
|
}
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'msg' => 'Saved changes to app links'
|
'msg' => $lang['success']['app_links']
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'ui_texts':
|
||||||
|
$main_name = $_data['main_name'];
|
||||||
|
$apps_name = $_data['apps_name'];
|
||||||
|
$help_text = $_data['help_text'];
|
||||||
|
try {
|
||||||
|
$redis->set('MAIN_NAME', htmlspecialchars($main_name));
|
||||||
|
$redis->set('APPS_NAME', htmlspecialchars($apps_name));
|
||||||
|
$redis->set('HELP_TEXT', $help_text);
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Redis: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => $lang['success']['ui_texts']
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +134,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
if ($redis->del('MAIN_LOGO')) {
|
if ($redis->del('MAIN_LOGO')) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'msg' => 'Reset default logo'
|
'msg' => $lang['success']['reset_main_logo']
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -156,6 +176,21 @@ function customize($_action, $_item, $_data = null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'ui_texts':
|
||||||
|
try {
|
||||||
|
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI';
|
||||||
|
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : 'mailcow Apps';
|
||||||
|
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Redis: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'main_logo_specs':
|
case 'main_logo_specs':
|
||||||
try {
|
try {
|
||||||
$image = new Imagick();
|
$image = new Imagick();
|
||||||
|
@ -168,7 +203,7 @@ function customize($_action, $_item, $_data = null) {
|
||||||
catch (ImagickException $e) {
|
catch (ImagickException $e) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Error: Imagick exception while reading image'
|
'msg' => $lang['danger']['imagick_exception']
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
function docker($service_name, $action, $post_action = null, $post_fields = null) {
|
function docker($service_name, $action, $attr1 = null, $attr2 = null, $extra_headers = null) {
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$curl = curl_init();
|
$curl = curl_init();
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
|
curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
|
||||||
switch($action) {
|
switch($action) {
|
||||||
|
@ -53,13 +60,16 @@ function docker($service_name, $action, $post_action = null, $post_fields = null
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'post':
|
case 'post':
|
||||||
if (!empty($post_action)) {
|
if (!empty($attr1)) {
|
||||||
$container_id = docker($service_name, 'get_id');
|
$container_id = docker($service_name, 'get_id');
|
||||||
if (ctype_xdigit($container_id) && ctype_alnum($post_action)) {
|
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
|
||||||
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $post_action);
|
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $attr1);
|
||||||
curl_setopt($curl, CURLOPT_POST, 1);
|
curl_setopt($curl, CURLOPT_POST, 1);
|
||||||
if (!empty($post_fields)) {
|
if (!empty($attr2)) {
|
||||||
curl_setopt( $curl, CURLOPT_POSTFIELDS, json_encode($post_fields));
|
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($attr2));
|
||||||
|
}
|
||||||
|
if (!empty($extra_headers) && is_array($extra_headers)) {
|
||||||
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $extra_headers);
|
||||||
}
|
}
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
$response = curl_exec($curl);
|
$response = curl_exec($curl);
|
||||||
|
|
|
@ -75,15 +75,15 @@ function generate_tlsa_digest($hostname, $port, $starttls = null) {
|
||||||
}
|
}
|
||||||
if (empty($starttls)) {
|
if (empty($starttls)) {
|
||||||
$context = stream_context_create(array("ssl" => array("capture_peer_cert" => true, 'verify_peer' => false, 'allow_self_signed' => true)));
|
$context = stream_context_create(array("ssl" => array("capture_peer_cert" => true, 'verify_peer' => false, 'allow_self_signed' => true)));
|
||||||
$stream = stream_socket_client('tls://' . $hostname . ':' . $port, $error_nr, $error_msg, 5, STREAM_CLIENT_CONNECT, $context);
|
$stream = stream_socket_client('ssl://' . $hostname . ':' . $port, $error_nr, $error_msg, 5, STREAM_CLIENT_CONNECT, $context);
|
||||||
// error_nr can be 0, so checking against error_msg
|
if (!$stream) {
|
||||||
if ($error_msg) {
|
$error_msg = isset($error_msg) ? $error_msg : '-';
|
||||||
return $error_nr . ': ' . $error_msg;
|
return $error_nr . ': ' . $error_msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stream = stream_socket_client('tcp://' . $hostname . ':' . $port, $error_nr, $error_msg, 5);
|
$stream = stream_socket_client('tcp://' . $hostname . ':' . $port, $error_nr, $error_msg, 5);
|
||||||
if ($error_msg) {
|
if (!$stream) {
|
||||||
return $error_nr . ': ' . $error_msg;
|
return $error_nr . ': ' . $error_msg;
|
||||||
}
|
}
|
||||||
$banner = fread($stream, 512 );
|
$banner = fread($stream, 512 );
|
||||||
|
@ -443,14 +443,31 @@ function user_get_alias_details($username) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$data['address'] = $username;
|
$data['address'] = $username;
|
||||||
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `aliases` FROM `alias`
|
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `shared_aliases` FROM `alias`
|
||||||
WHERE `goto` REGEXP :username_goto
|
WHERE `goto` REGEXP :username_goto
|
||||||
AND `address` NOT LIKE '@%'
|
AND `address` NOT LIKE '@%'
|
||||||
|
AND `goto` != :username_goto2
|
||||||
AND `address` != :username_address");
|
AND `address` != :username_address");
|
||||||
$stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username));
|
$stmt->execute(array(
|
||||||
|
':username_goto' => '(^|,)'.$username.'($|,)',
|
||||||
|
':username_goto2' => $username,
|
||||||
|
':username_address' => $username
|
||||||
|
));
|
||||||
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while ($row = array_shift($run)) {
|
while ($row = array_shift($run)) {
|
||||||
$data['aliases'] = $row['aliases'];
|
$data['shared_aliases'] = $row['shared_aliases'];
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `direct_aliases` FROM `alias`
|
||||||
|
WHERE `goto` = :username_goto
|
||||||
|
AND `address` != :username_address");
|
||||||
|
$stmt->execute(
|
||||||
|
array(
|
||||||
|
':username_goto' => $username,
|
||||||
|
':username_address' => $username
|
||||||
|
));
|
||||||
|
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while ($row = array_shift($run)) {
|
||||||
|
$data['direct_aliases'] = $row['direct_aliases'];
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '✘') AS `ad_alias` FROM `mailbox`
|
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '✘') AS `ad_alias` FROM `mailbox`
|
||||||
LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
|
LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
|
||||||
|
@ -466,7 +483,7 @@ function user_get_alias_details($username) {
|
||||||
while ($row = array_shift($run)) {
|
while ($row = array_shift($run)) {
|
||||||
$data['aliases_also_send_as'] = $row['send_as'];
|
$data['aliases_also_send_as'] = $row['send_as'];
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '✘') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` LIKE '@%';");
|
$stmt = $pdo->prepare("SELECT IFNULL(CONCAT(GROUP_CONCAT(DISTINCT `send_as` SEPARATOR ', '), ', ', GROUP_CONCAT(DISTINCT CONCAT('@',`alias_domain`) SEPARATOR ', ')), '✘') AS `send_as` FROM `sender_acl` LEFT JOIN `alias_domain` ON `alias_domain`.`target_domain` = TRIM(LEADING '@' FROM `send_as`) WHERE `logged_in_as` = :username AND `send_as` LIKE '@%';");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while ($row = array_shift($run)) {
|
while ($row = array_shift($run)) {
|
||||||
|
@ -851,6 +868,135 @@ function verify_tfa_login($username, $token) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
function admin_api($action, $data = null) {
|
||||||
|
global $pdo;
|
||||||
|
global $lang;
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch ($action) {
|
||||||
|
case "edit":
|
||||||
|
$regen_key = $data['admin_api_regen_key'];
|
||||||
|
$active = (isset($data['active'])) ? 1 : 0;
|
||||||
|
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
|
||||||
|
foreach ($allow_from as $key => $val) {
|
||||||
|
if (!filter_var($val, FILTER_VALIDATE_IP)) {
|
||||||
|
unset($allow_from[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$allow_from = implode(',', array_unique(array_filter($allow_from)));
|
||||||
|
if (empty($allow_from)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'List of allowed IPs cannot be empty'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$api_key = implode('-', array(
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3)))
|
||||||
|
));
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `api` (`username`, `api_key`, `active`, `allow_from`)
|
||||||
|
SELECT `username`, :api_key, :active, :allow_from FROM `admin` WHERE `superadmin`='1' AND `active`='1'
|
||||||
|
ON DUPLICATE KEY UPDATE `active` = :active_u, `allow_from` = :allow_from_u ;");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':api_key' => $api_key,
|
||||||
|
':active' => $active,
|
||||||
|
':active_u' => $active,
|
||||||
|
':allow_from' => $allow_from,
|
||||||
|
':allow_from_u' => $allow_from
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case "regen_key":
|
||||||
|
$api_key = implode('-', array(
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3)))
|
||||||
|
));
|
||||||
|
$stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `username` IN
|
||||||
|
(SELECT `username` FROM `admin` WHERE `superadmin`='1' AND `active`='1')");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':api_key' => $api_key
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => sprintf($lang['success']['admin_modified'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function rspamd_ui($action, $data = null) {
|
||||||
|
global $lang;
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch ($action) {
|
||||||
|
case "edit":
|
||||||
|
$rspamd_ui_pass = $data['rspamd_ui_pass'];
|
||||||
|
$rspamd_ui_pass2 = $data['rspamd_ui_pass2'];
|
||||||
|
if (empty($rspamd_ui_pass) || empty($rspamd_ui_pass2)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Password cannot be empty'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($rspamd_ui_pass != $rspamd_ui_pass2) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Passwords do not match'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strlen($rspamd_ui_pass) < 6) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Please use at least 6 characters for your password'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$docker_return = docker('rspamd-mailcow', 'post', 'exec', array('cmd' => 'worker_password', 'raw' => $rspamd_ui_pass), array('Content-Type: application/json'));
|
||||||
|
if ($docker_return_array = json_decode($docker_return, true)) {
|
||||||
|
if ($docker_return_array['type'] == 'success') {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Rspamd UI password set successfully'
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => $docker_return_array['type'],
|
||||||
|
'msg' => $docker_return_array['msg']
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Unknown error'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
function get_admin_details() {
|
function get_admin_details() {
|
||||||
// No parameter to be given, only one admin should exist
|
// No parameter to be given, only one admin should exist
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
@ -860,8 +1006,10 @@ function get_admin_details() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'");
|
$stmt = $pdo->query("SELECT `admin`.`username`, `api`.`active` AS `api_active`, `api`.`api_key`, `api`.`allow_from` FROM `admin`
|
||||||
$stmt->execute();
|
INNER JOIN `api` ON `admin`.`username` = `api`.`username`
|
||||||
|
WHERE `admin`.`superadmin`='1'
|
||||||
|
AND `admin`.`active`='1'");
|
||||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
catch(PDOException $e) {
|
catch(PDOException $e) {
|
||||||
|
@ -932,6 +1080,51 @@ function get_logs($container, $lines = false) {
|
||||||
return $data_array;
|
return $data_array;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($container == "watchdog-mailcow") {
|
||||||
|
if (!is_numeric($lines)) {
|
||||||
|
list ($from, $to) = explode('-', $lines);
|
||||||
|
$data = $redis->lRange('WATCHDOG_LOG', intval($from), intval($to));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$data = $redis->lRange('WATCHDOG_LOG', 0, intval($lines));
|
||||||
|
}
|
||||||
|
if ($data) {
|
||||||
|
foreach ($data as $json_line) {
|
||||||
|
$data_array[] = json_decode($json_line, true);
|
||||||
|
}
|
||||||
|
return $data_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($container == "acme-mailcow") {
|
||||||
|
if (!is_numeric($lines)) {
|
||||||
|
list ($from, $to) = explode('-', $lines);
|
||||||
|
$data = $redis->lRange('ACME_LOG', intval($from), intval($to));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$data = $redis->lRange('ACME_LOG', 0, intval($lines));
|
||||||
|
}
|
||||||
|
if ($data) {
|
||||||
|
foreach ($data as $json_line) {
|
||||||
|
$data_array[] = json_decode($json_line, true);
|
||||||
|
}
|
||||||
|
return $data_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($container == "api-mailcow") {
|
||||||
|
if (!is_numeric($lines)) {
|
||||||
|
list ($from, $to) = explode('-', $lines);
|
||||||
|
$data = $redis->lRange('API_LOG', intval($from), intval($to));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$data = $redis->lRange('API_LOG', 0, intval($lines));
|
||||||
|
}
|
||||||
|
if ($data) {
|
||||||
|
foreach ($data as $json_line) {
|
||||||
|
$data_array[] = json_decode($json_line, true);
|
||||||
|
}
|
||||||
|
return $data_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($container == "fail2ban-mailcow") {
|
if ($container == "fail2ban-mailcow") {
|
||||||
if (!is_numeric($lines)) {
|
if (!is_numeric($lines)) {
|
||||||
list ($from, $to) = explode('-', $lines);
|
list ($from, $to) = explode('-', $lines);
|
||||||
|
|
|
@ -333,6 +333,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
$aliases = $_data['aliases'];
|
$aliases = $_data['aliases'];
|
||||||
$mailboxes = $_data['mailboxes'];
|
$mailboxes = $_data['mailboxes'];
|
||||||
$maxquota = $_data['maxquota'];
|
$maxquota = $_data['maxquota'];
|
||||||
|
$restart_sogo = $_data['restart_sogo'];
|
||||||
$quota = $_data['quota'];
|
$quota = $_data['quota'];
|
||||||
if ($maxquota > $quota) {
|
if ($maxquota > $quota) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -416,10 +417,21 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$_SESSION['return'] = array(
|
if (!empty($restart_sogo)) {
|
||||||
'type' => 'success',
|
$restart_reponse = json_decode(docker('sogo-mailcow', 'post', 'restart'), true);
|
||||||
'msg' => sprintf($lang['success']['domain_added'], htmlspecialchars($domain))
|
if ($restart_reponse['type'] == "success") {
|
||||||
);
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => sprintf($lang['success']['domain_added'], htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'warning',
|
||||||
|
'msg' => 'Added domain but failed to restart SOGo, please check your server logs.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
mailbox('delete', 'domain', array('domain' => $domain));
|
mailbox('delete', 'domain', array('domain' => $domain));
|
||||||
|
@ -490,9 +502,20 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
if (in_array($address, $gotos)) {
|
if (in_array($address, $gotos)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
$domain = idn_to_ascii(substr(strstr($address, '@'), 1));
|
||||||
|
$local_part = strstr($address, '@', true);
|
||||||
|
$address = $local_part.'@'.$domain;
|
||||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias`
|
$stmt = $pdo->prepare("SELECT `address` FROM `alias`
|
||||||
WHERE `address`= :address");
|
WHERE `address`= :address OR `address` IN (
|
||||||
$stmt->execute(array(':address' => $address));
|
SELECT `username` FROM `mailbox`, `alias_domain`
|
||||||
|
WHERE (
|
||||||
|
`alias_domain`.`alias_domain` = :address_d
|
||||||
|
AND `mailbox`.`username` = CONCAT(:address_l, '@', alias_domain.target_domain)))");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':address' => $address,
|
||||||
|
':address_l' => $local_part,
|
||||||
|
':address_d' => $domain
|
||||||
|
));
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
if ($num_results != 0) {
|
if ($num_results != 0) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -501,9 +524,6 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$domain = idn_to_ascii(substr(strstr($address, '@'), 1));
|
|
||||||
$local_part = strstr($address, '@', true);
|
|
||||||
$address = $local_part.'@'.$domain;
|
|
||||||
$domaindata = mailbox('get', 'domain_details', $domain);
|
$domaindata = mailbox('get', 'domain_details', $domain);
|
||||||
if (is_array($domaindata) && $domaindata['aliases_left'] == "0") {
|
if (is_array($domaindata) && $domaindata['aliases_left'] == "0") {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -722,7 +742,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
}
|
}
|
||||||
$active = intval($_data['active']);
|
$active = intval($_data['active']);
|
||||||
$quota_b = ($quota_m * 1048576);
|
$quota_b = ($quota_m * 1048576);
|
||||||
$maildir = $domain."/".$local_part."/";
|
$maildir = $domain . "/" . $local_part . "/";
|
||||||
if (!is_valid_domain_name($domain)) {
|
if (!is_valid_domain_name($domain)) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
|
@ -1405,7 +1425,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
$subfolder2 = (isset($_data['subfolder2'])) ? $_data['subfolder2'] : $is_now['subfolder2'];
|
$subfolder2 = (isset($_data['subfolder2'])) ? $_data['subfolder2'] : $is_now['subfolder2'];
|
||||||
$enc1 = (!empty($_data['enc1'])) ? $_data['enc1'] : $is_now['enc1'];
|
$enc1 = (!empty($_data['enc1'])) ? $_data['enc1'] : $is_now['enc1'];
|
||||||
$mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
|
$mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
|
||||||
$exclude = (!empty($_data['exclude'])) ? $_data['exclude'] : '';
|
$exclude = (!empty($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude'];
|
||||||
$maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage'];
|
$maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2302,7 +2322,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
|
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND (`domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $_SESSION['mailcow_cc_username'],
|
':username' => $_SESSION['mailcow_cc_username'],
|
||||||
':role' => $_SESSION['mailcow_cc_role'],
|
':role' => $_SESSION['mailcow_cc_role'],
|
||||||
|
@ -3103,10 +3123,6 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
}
|
}
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
if (!is_numeric($id)) {
|
if (!is_numeric($id)) {
|
||||||
$_SESSION['return'] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'msg' => $id
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -3154,10 +3170,6 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
}
|
}
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
if (!is_numeric($id)) {
|
if (!is_numeric($id)) {
|
||||||
$_SESSION['return'] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'msg' => $id
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -3366,6 +3378,10 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':domain' => '%@'.$domain,
|
':domain' => '%@'.$domain,
|
||||||
));
|
));
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :domain");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':domain' => $domain,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -3486,6 +3502,10 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':alias_domain' => $alias_domain,
|
':alias_domain' => $alias_domain,
|
||||||
));
|
));
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :alias_domain");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':alias_domain' => $alias_domain,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -3580,6 +3600,10 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :username");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username
|
||||||
|
));
|
||||||
$stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
|
$stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
|
||||||
WHERE `goto` REGEXP :username");
|
WHERE `goto` REGEXP :username");
|
||||||
$stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
|
$stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
<?php
|
||||||
|
function quarantaine($_action, $_data = null) {
|
||||||
|
global $pdo;
|
||||||
|
global $redis;
|
||||||
|
global $lang;
|
||||||
|
switch ($_action) {
|
||||||
|
case 'delete':
|
||||||
|
if (!is_array($_data['id'])) {
|
||||||
|
$ids = array();
|
||||||
|
$ids[] = $_data['id'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ids = $_data['id'];
|
||||||
|
}
|
||||||
|
if (!isset($_SESSION['acl']['quarantaine']) || $_SESSION['acl']['quarantaine'] != "1" ) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (!is_numeric($id)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare('SELECT `rcpt` FROM `quarantaine` WHERE `id` = :id');
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `quarantaine` WHERE `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $ids))
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
if (!isset($_SESSION['acl']['quarantaine']) || $_SESSION['acl']['quarantaine'] != "1" ) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Edit settings
|
||||||
|
if ($_data['action'] == 'settings') {
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$retention_size = $_data['retention_size'];
|
||||||
|
$max_size = $_data['max_size'];
|
||||||
|
$exclude_domains = (array)$_data['exclude_domains'];
|
||||||
|
try {
|
||||||
|
$redis->Set('Q_RETENTION_SIZE', intval($retention_size));
|
||||||
|
$redis->Set('Q_MAX_SIZE', intval($max_size));
|
||||||
|
$redis->Set('Q_EXCLUDE_DOMAINS', json_encode($exclude_domains));
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Redis: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Saved settings'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Release item
|
||||||
|
elseif ($_data['action'] == 'release') {
|
||||||
|
if (!is_array($_data['id'])) {
|
||||||
|
$ids = array();
|
||||||
|
$ids[] = $_data['id'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ids = $_data['id'];
|
||||||
|
}
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (!is_numeric($id)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare('SELECT `msg`, `qid`, `sender`, `rcpt` FROM `quarantaine` WHERE `id` = :id');
|
||||||
|
$stmt->execute(array(':id' => $id));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$sender = (isset($row['sender'])) ? $row['sender'] : 'sender-unknown@rspamd';
|
||||||
|
try {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->SMTPDebug = 0;
|
||||||
|
$mail->SMTPOptions = array(
|
||||||
|
'ssl' => array(
|
||||||
|
'verify_peer' => false,
|
||||||
|
'verify_peer_name' => false,
|
||||||
|
'allow_self_signed' => true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!empty(gethostbynamel('postfix-mailcow'))) {
|
||||||
|
$postfix = 'postfix-mailcow';
|
||||||
|
}
|
||||||
|
if (!empty(gethostbynamel('postfix'))) {
|
||||||
|
$postfix = 'postfix';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'warning',
|
||||||
|
'msg' => sprintf($lang['danger']['release_send_failed'], 'Cannot determine Postfix host')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$mail->Host = $postfix;
|
||||||
|
$mail->Port = 590;
|
||||||
|
$mail->setFrom($sender);
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
$mail->Subject = sprintf($lang['quarantaine']['release_subject'], $row['qid']);
|
||||||
|
$mail->addAddress($row['rcpt']);
|
||||||
|
$mail->IsHTML(false);
|
||||||
|
$msg_tmpf = tempnam("/tmp", $row['qid']);
|
||||||
|
file_put_contents($msg_tmpf, $row['msg']);
|
||||||
|
$mail->addAttachment($msg_tmpf, $row['qid'] . '.eml');
|
||||||
|
$mail->Body = sprintf($lang['quarantaine']['release_body']);
|
||||||
|
$mail->send();
|
||||||
|
unlink($msg_tmpf);
|
||||||
|
}
|
||||||
|
catch (phpmailerException $e) {
|
||||||
|
unlink($msg_tmpf);
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'warning',
|
||||||
|
'msg' => sprintf($lang['danger']['release_send_failed'], $e->errorMessage())
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `quarantaine` WHERE `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => $lang['success']['items_released']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 'get':
|
||||||
|
try {
|
||||||
|
if ($_SESSION['mailcow_cc_role'] == "user") {
|
||||||
|
$stmt = $pdo->prepare('SELECT `id`, `qid`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantaine` WHERE `rcpt` = :mbox');
|
||||||
|
$stmt->execute(array(':mbox' => $_SESSION['mailcow_cc_username']));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$q_meta[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach (mailbox('get', 'mailboxes') as $mbox) {
|
||||||
|
$stmt = $pdo->prepare('SELECT `id`, `qid`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantaine` WHERE `rcpt` = :mbox');
|
||||||
|
$stmt->execute(array(':mbox' => $mbox));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$q_meta[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $q_meta;
|
||||||
|
break;
|
||||||
|
case 'settings':
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$settings['exclude_domains'] = json_decode($redis->Get('Q_EXCLUDE_DOMAINS'), true);
|
||||||
|
$settings['max_size'] = $redis->Get('Q_MAX_SIZE');
|
||||||
|
$settings['retention_size'] = $redis->Get('Q_RETENTION_SIZE');
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Redis: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $settings;
|
||||||
|
break;
|
||||||
|
case 'details':
|
||||||
|
if (!is_numeric($_data) || empty($_data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare('SELECT `rcpt`, `symbols`, `msg`, `domain` FROM `quarantaine` WHERE `id`= :id');
|
||||||
|
$stmt->execute(array(':id' => $_data));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch(PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,8 +56,8 @@ function relayhost($_action, $_data = null) {
|
||||||
$is_now = relayhost('details', $id);
|
$is_now = relayhost('details', $id);
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
$hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
|
$hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
|
||||||
$username = (!empty($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
$username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
||||||
$password = (!empty($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
$password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
||||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<link rel="stylesheet" href="/css/breakpoint.min.css">
|
||||||
<link rel="stylesheet" href="/css/bootstrap-select.min.css">
|
<link rel="stylesheet" href="/css/bootstrap-select.min.css">
|
||||||
<link rel="stylesheet" href="/css/bootstrap-slider.min.css">
|
<link rel="stylesheet" href="/css/bootstrap-slider.min.css">
|
||||||
<link rel="stylesheet" href="/css/bootstrap-switch.min.css">
|
<link rel="stylesheet" href="/css/bootstrap-switch.min.css">
|
||||||
|
@ -27,6 +28,8 @@
|
||||||
<?= (preg_match("/admin.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/admin.css">' : null; ?>
|
<?= (preg_match("/admin.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/admin.css">' : null; ?>
|
||||||
<?= (preg_match("/user.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/user.css">' : null; ?>
|
<?= (preg_match("/user.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/user.css">' : null; ?>
|
||||||
<?= (preg_match("/edit.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/edit.css">' : null; ?>
|
<?= (preg_match("/edit.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/edit.css">' : null; ?>
|
||||||
|
<?= (preg_match("/quarantaine.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/quarantaine.css">' : null; ?>
|
||||||
|
<?= (preg_match("/debug.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/debug.css">' : null; ?>
|
||||||
<link rel="shortcut icon" href="/favicon.png" type="image/png">
|
<link rel="shortcut icon" href="/favicon.png" type="image/png">
|
||||||
<link rel="icon" href="/favicon.png" type="image/png">
|
<link rel="icon" href="/favicon.png" type="image/png">
|
||||||
</head>
|
</head>
|
||||||
|
@ -35,7 +38,6 @@
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
|
@ -70,6 +72,7 @@
|
||||||
if (isset($_SESSION['mailcow_cc_role'])) {
|
if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
if ($_SESSION['mailcow_cc_role'] == 'admin') {
|
if ($_SESSION['mailcow_cc_role'] == 'admin') {
|
||||||
?>
|
?>
|
||||||
|
<li<?= (preg_match("/debug/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/debug.php"><?= $lang['header']['debug']; ?></a></li>
|
||||||
<li<?= (preg_match("/admin/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/admin.php"><?= $lang['header']['administration']; ?></a></li>
|
<li<?= (preg_match("/admin/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/admin.php"><?= $lang['header']['administration']; ?></a></li>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
@ -88,14 +91,19 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<?php
|
<?php
|
||||||
|
if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
|
?>
|
||||||
|
<li<?= (preg_match("/quarantaine/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/quarantaine.php"><span style="font-size: 12px;" class="glyphicon glyphicon-briefcase"></span> <?= $lang['header']['quarantaine']; ?></a></li>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
if ($_SESSION['mailcow_cc_role'] == 'admin') {
|
if ($_SESSION['mailcow_cc_role'] == 'admin') {
|
||||||
?>
|
?>
|
||||||
<li><a href data-toggle="modal" data-target="#RestartSOGo"><span style="font-size: 12px;" class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?= $lang['header']['restart_sogo']; ?></a></li>
|
<li><a href data-toggle="modal" data-container="sogo-mailcow" data-target="#RestartContainer"><span style="font-size: 12px;" class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_sogo']; ?></a></li>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-link" aria-hidden="true"></span> Apps <span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-link"></span> Apps <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<?php
|
<?php
|
||||||
foreach ($MAILCOW_APPS as $app):
|
foreach ($MAILCOW_APPS as $app):
|
||||||
|
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "14112017_2103";
|
$db_version = "02012018_1515";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
@ -103,6 +103,30 @@ function init_db_schema() {
|
||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"api" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
|
"api_key" => "VARCHAR(255) NOT NULL",
|
||||||
|
"allow_from" => "VARCHAR(512) NOT NULL",
|
||||||
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
|
"modified" => "DATETIME ON UPDATE NOW(0)",
|
||||||
|
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("username")
|
||||||
|
),
|
||||||
|
"fkey" => array(
|
||||||
|
"fk_username_api" => array(
|
||||||
|
"col" => "username",
|
||||||
|
"ref" => "admin.username",
|
||||||
|
"delete" => "CASCADE",
|
||||||
|
"update" => "NO ACTION"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"sender_acl" => array(
|
"sender_acl" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"logged_in_as" => "VARCHAR(255) NOT NULL",
|
"logged_in_as" => "VARCHAR(255) NOT NULL",
|
||||||
|
@ -133,6 +157,28 @@ function init_db_schema() {
|
||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"quarantaine" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||||
|
"qid" => "VARCHAR(30) NOT NULL",
|
||||||
|
"score" => "FLOAT(8,2)",
|
||||||
|
"ip" => "VARBINARY(16)",
|
||||||
|
"action" => "CHAR(20) NOT NULL DEFAULT 'unknown'",
|
||||||
|
"symbols" => "JSON",
|
||||||
|
"sender" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
|
||||||
|
"rcpt" => "VARCHAR(255)",
|
||||||
|
"msg" => "LONGTEXT",
|
||||||
|
"domain" => "VARCHAR(255)",
|
||||||
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
|
"user" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("id")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"mailbox" => array(
|
"mailbox" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"username" => "VARCHAR(255) NOT NULL",
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
|
@ -202,6 +248,8 @@ function init_db_schema() {
|
||||||
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
|
"quarantaine" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
|
"bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
"fkey" => array(
|
"fkey" => array(
|
||||||
|
@ -325,6 +373,27 @@ function init_db_schema() {
|
||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"bcc_maps" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||||
|
"local_dest" => "VARCHAR(255) NOT NULL",
|
||||||
|
"bcc_dest" => "VARCHAR(255) NOT NULL",
|
||||||
|
"domain" => "VARCHAR(255) NOT NULL",
|
||||||
|
"type" => "ENUM('sender','rcpt')",
|
||||||
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
|
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
|
||||||
|
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("id")
|
||||||
|
),
|
||||||
|
"key" => array(
|
||||||
|
"local_dest" => array("local_dest"),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"tfa" => array(
|
"tfa" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||||
|
@ -471,12 +540,13 @@ function init_db_schema() {
|
||||||
"c_sn" => "VARCHAR(255)",
|
"c_sn" => "VARCHAR(255)",
|
||||||
"c_screenname" => "VARCHAR(255)",
|
"c_screenname" => "VARCHAR(255)",
|
||||||
"c_l" => "VARCHAR(255)",
|
"c_l" => "VARCHAR(255)",
|
||||||
"c_mail" => "VARCHAR(255)",
|
"c_mail" => "TEXT",
|
||||||
"c_o" => "VARCHAR(255)",
|
"c_o" => "VARCHAR(255)",
|
||||||
"c_ou" => "VARCHAR(255)",
|
"c_ou" => "VARCHAR(255)",
|
||||||
"c_telephonenumber" => "VARCHAR(255)",
|
"c_telephonenumber" => "VARCHAR(255)",
|
||||||
"c_categories" => "VARCHAR(255)",
|
"c_categories" => "VARCHAR(255)",
|
||||||
"c_component" => "VARCHAR(10) NOT NULL"
|
"c_component" => "VARCHAR(10) NOT NULL",
|
||||||
|
"c_hascertificate" => "INT4 DEFAULT 0"
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
"primary" => array(
|
"primary" => array(
|
||||||
|
@ -519,8 +589,8 @@ function init_db_schema() {
|
||||||
"sogo_user_profile" => array(
|
"sogo_user_profile" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"c_uid" => "VARCHAR(255) NOT NULL",
|
"c_uid" => "VARCHAR(255) NOT NULL",
|
||||||
"c_defaults" => "TEXT",
|
"c_defaults" => "LONGTEXT",
|
||||||
"c_settings" => "TEXT"
|
"c_settings" => "LONGTEXT"
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
"primary" => array(
|
"primary" => array(
|
||||||
|
@ -686,6 +756,20 @@ function init_db_schema() {
|
||||||
$pdo->query($create);
|
$pdo->query($create);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create events to clean database
|
||||||
|
$events[] = 'DROP EVENT IF EXISTS clean_spamalias;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE EVENT clean_spamalias
|
||||||
|
ON SCHEDULE EVERY 1 DAY DO
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
DELIMITER ;';
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$pdo->exec($event);
|
||||||
|
}
|
||||||
|
|
||||||
// Inject admin if not exists
|
// Inject admin if not exists
|
||||||
$stmt = $pdo->query("SELECT NULL FROM `admin`");
|
$stmt = $pdo->query("SELECT NULL FROM `admin`");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"require": {
|
"require": {
|
||||||
"robthree/twofactorauth": "^1.6",
|
"robthree/twofactorauth": "^1.6",
|
||||||
"yubico/u2flib-server": "^1.0",
|
"yubico/u2flib-server": "^1.0",
|
||||||
"phpmailer/phpmailer": "^5.2"
|
"phpmailer/phpmailer": "^5.2",
|
||||||
|
"php-mime-mail-parser/php-mime-mail-parser": "^2.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,88 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "d51ef1712a74b0dfed729f2bdd85d1e3",
|
"content-hash": "ee4c9e269c29282221ce88bc23f1bda9",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"version": "2.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git",
|
||||||
|
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c6884c7bc77adbf55979db99841195b232fd30f1",
|
||||||
|
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mailparse": "*",
|
||||||
|
"php": "^5.4.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"exorus/php-mime-mail-parser": "*",
|
||||||
|
"messaged/php-mime-mail-parser": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/php-token-stream": "^1.3.0",
|
||||||
|
"phpunit/phpunit": "^4.0 || ^5.0",
|
||||||
|
"satooshi/php-coveralls": "0.*",
|
||||||
|
"squizlabs/php_codesniffer": "2.*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpMimeMailParser\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "bucabay",
|
||||||
|
"email": "gabe@fijiwebdesign.com",
|
||||||
|
"homepage": "http://www.fijiwebdesign.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eXorus",
|
||||||
|
"email": "exorus.spam@gmail.com",
|
||||||
|
"homepage": "https://github.com/eXorus/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "M.Valinskis",
|
||||||
|
"email": "M.Valins@gmail.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eugene.emmett.wood",
|
||||||
|
"email": "gene_w@cementhorizon.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "alknetso",
|
||||||
|
"email": "alkne@gmail.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
|
||||||
|
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"keywords": [
|
||||||
|
"MimeMailParser",
|
||||||
|
"mail",
|
||||||
|
"mailparse",
|
||||||
|
"mime"
|
||||||
|
],
|
||||||
|
"time": "2017-11-02T05:49:00+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpmailer/phpmailer",
|
"name": "phpmailer/phpmailer",
|
||||||
"version": "v5.2.25",
|
"version": "v5.2.25",
|
||||||
|
|
|
@ -7,4 +7,5 @@ $baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'),
|
'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'),
|
||||||
|
'PhpMimeMailParser\\' => array($vendorDir . '/php-mime-mail-parser/php-mime-mail-parser/src'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
|
||||||
array (
|
array (
|
||||||
'RobThree\\Auth\\' => 14,
|
'RobThree\\Auth\\' => 14,
|
||||||
),
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'PhpMimeMailParser\\' => 18,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $prefixDirsPsr4 = array (
|
public static $prefixDirsPsr4 = array (
|
||||||
|
@ -18,6 +22,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib',
|
0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib',
|
||||||
),
|
),
|
||||||
|
'PhpMimeMailParser\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/php-mime-mail-parser/php-mime-mail-parser/src',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $classMap = array (
|
public static $classMap = array (
|
||||||
|
|
|
@ -167,5 +167,87 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP"
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"version": "2.9.3",
|
||||||
|
"version_normalized": "2.9.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git",
|
||||||
|
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c6884c7bc77adbf55979db99841195b232fd30f1",
|
||||||
|
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mailparse": "*",
|
||||||
|
"php": "^5.4.0 || ^7.0"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"exorus/php-mime-mail-parser": "*",
|
||||||
|
"messaged/php-mime-mail-parser": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/php-token-stream": "^1.3.0",
|
||||||
|
"phpunit/phpunit": "^4.0 || ^5.0",
|
||||||
|
"satooshi/php-coveralls": "0.*",
|
||||||
|
"squizlabs/php_codesniffer": "2.*"
|
||||||
|
},
|
||||||
|
"time": "2017-11-02T05:49:00+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpMimeMailParser\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "bucabay",
|
||||||
|
"email": "gabe@fijiwebdesign.com",
|
||||||
|
"homepage": "http://www.fijiwebdesign.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eXorus",
|
||||||
|
"email": "exorus.spam@gmail.com",
|
||||||
|
"homepage": "https://github.com/eXorus/",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "M.Valinskis",
|
||||||
|
"email": "M.Valins@gmail.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eugene.emmett.wood",
|
||||||
|
"email": "gene_w@cementhorizon.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "alknetso",
|
||||||
|
"email": "alkne@gmail.com",
|
||||||
|
"homepage": "https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
|
||||||
|
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"keywords": [
|
||||||
|
"MimeMailParser",
|
||||||
|
"mail",
|
||||||
|
"mailparse",
|
||||||
|
"mime"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Vincent Dauce
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,167 @@
|
||||||
|
# php-mime-mail-parser
|
||||||
|
|
||||||
|
A fully tested mailparse extension wrapper for PHP 5.4+
|
||||||
|
|
||||||
|
[![Latest Version](https://img.shields.io/packagist/v/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases)
|
||||||
|
[![Total Downloads](https://img.shields.io/packagist/dt/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://packagist.org/packages/php-mime-mail-parser/php-mime-mail-parser)
|
||||||
|
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
This extension can be used to...
|
||||||
|
* Parse and read email from Postfix
|
||||||
|
* Create webmail
|
||||||
|
* Store email information such a subject, HTML body, attachments, and etc. into a database
|
||||||
|
|
||||||
|
## Is it reliable?
|
||||||
|
|
||||||
|
Yes. All known issues have been reproduced, fixed and tested.
|
||||||
|
|
||||||
|
We use Travis CI to help ensure code quality. You can see real-time statistics below:
|
||||||
|
|
||||||
|
[![Build Status](https://img.shields.io/travis/php-mime-mail-parser/php-mime-mail-parser/master.svg?style=flat-square)](https://travis-ci.org/php-mime-mail-parser/php-mime-mail-parser)
|
||||||
|
[![Coverage](https://img.shields.io/coveralls/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://coveralls.io/r/php-mime-mail-parser/php-mime-mail-parser)
|
||||||
|
[![Quality Score](https://img.shields.io/scrutinizer/g/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-mime-mail-parser/php-mime-mail-parser)
|
||||||
|
|
||||||
|
## How do I install it?
|
||||||
|
|
||||||
|
The easiest way is via [Composer](https://getcomposer.org/).
|
||||||
|
|
||||||
|
To install the latest version of PHP MIME Mail Parser, run the command below:
|
||||||
|
|
||||||
|
composer require php-mime-mail-parser/php-mime-mail-parser
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
The following versions of PHP are supported:
|
||||||
|
|
||||||
|
* PHP 5.4
|
||||||
|
* PHP 5.5
|
||||||
|
* PHP 5.6
|
||||||
|
* PHP 7
|
||||||
|
* HHVM
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install php-cli php-pear php-dev php-mbstring
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you have the mailparse extension (http://php.net/manual/en/book.mailparse.php) properly installed. The command line `php -m | grep mailparse` need to return "mailparse" else install it:
|
||||||
|
* PHP version > 7.0: mailparse
|
||||||
|
* PHP version < 7.0: mailparse 2.1.6
|
||||||
|
|
||||||
|
Follow this steps to install mailparse:
|
||||||
|
|
||||||
|
* Compile in the temp folder the extension mailparse or mailparse-2.1.6 (workaround because pecl install doesn't work yet)
|
||||||
|
```
|
||||||
|
cd
|
||||||
|
pecl download mailparse
|
||||||
|
tar -xvf mailparse-3.0.2.tgz
|
||||||
|
cd mailparse-3.0.2/
|
||||||
|
phpize
|
||||||
|
./configure
|
||||||
|
sed -i 's/#if\s!HAVE_MBSTRING/#ifndef MBFL_MBFILTER_H/' ./mailparse.c
|
||||||
|
make
|
||||||
|
sudo mv modules/mailparse.so /usr/lib/php/20160303/
|
||||||
|
```
|
||||||
|
* Add the extension mailparse and activate it
|
||||||
|
```
|
||||||
|
echo "extension=mailparse.so" | sudo tee /etc/php/7.1/mods-available/mailparse.ini
|
||||||
|
sudo phpenmod mailparse
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows, you need to download mailparse DLL from http://pecl.php.net/package/mailparse and add the line "extension=php_mailparse.dll" to php.ini accordingly.
|
||||||
|
|
||||||
|
## How do I use it?
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// Include the library first
|
||||||
|
require_once __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
$path = 'path/to/mail.txt';
|
||||||
|
$Parser = new PhpMimeMailParser\Parser();
|
||||||
|
|
||||||
|
// There are four methods available to indicate which mime mail to parse.
|
||||||
|
// You only need to use one of the following four:
|
||||||
|
|
||||||
|
// 1. Specify a file path to the mime mail.
|
||||||
|
$Parser->setPath($path);
|
||||||
|
|
||||||
|
// 2. Specify a php file resource (stream) to the mime mail.
|
||||||
|
$Parser->setStream(fopen($path, "r"));
|
||||||
|
|
||||||
|
// 3. Specify the raw mime mail text.
|
||||||
|
$Parser->setText(file_get_contents($path));
|
||||||
|
|
||||||
|
// 4. Specify a stream to work with mail server
|
||||||
|
$Parser->setStream(fopen("php://stdin", "r"));
|
||||||
|
|
||||||
|
// Once we've indicated where to find the mail, we can parse out the data
|
||||||
|
$to = $Parser->getHeader('to'); // "test" <test@example.com>, "test2" <test2@example.com>
|
||||||
|
$addressesTo = $Parser->getAddresses('to'); //Return an array : [[test, test@example.com, false],[test2, test2@example.com, false]]
|
||||||
|
|
||||||
|
$from = $Parser->getHeader('from'); // "test" <test@example.com>
|
||||||
|
$addressesFrom = $Parser->getAddresses('from'); //Return an array : test, test@example.com, false
|
||||||
|
|
||||||
|
$subject = $Parser->getHeader('subject');
|
||||||
|
|
||||||
|
$text = $Parser->getMessageBody('text');
|
||||||
|
|
||||||
|
$html = $Parser->getMessageBody('html');
|
||||||
|
$htmlEmbedded = $Parser->getMessageBody('htmlEmbedded'); //HTML Body included data
|
||||||
|
|
||||||
|
$stringHeaders = $Parser->getHeadersRaw(); // Get all headers as a string, no charset conversion
|
||||||
|
$arrayHeaders = $Parser->getHeaders(); // Get all headers as an array, with charset conversion
|
||||||
|
|
||||||
|
// Pass in a writeable path to save attachments
|
||||||
|
$attach_dir = '/path/to/save/attachments/'; // Be sure to include the trailing slash
|
||||||
|
$include_inline = true; // Optional argument to include inline attachments (default: true)
|
||||||
|
$Parser->saveAttachments($attach_dir [,$include_inline]);
|
||||||
|
|
||||||
|
// Get an array of Attachment items from $Parser
|
||||||
|
$attachments = $Parser->getAttachments([$include_inline]);
|
||||||
|
|
||||||
|
// Loop through all the Attachments
|
||||||
|
if (count($attachments) > 0) {
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
echo 'Filename : '.$attachment->getFilename().'<br />'; // logo.jpg
|
||||||
|
echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />'; // 1000
|
||||||
|
echo 'Filetype : '.$attachment->getContentType().'<br />'; // image/jpeg
|
||||||
|
echo 'MIME part string : '.$attachment->getMimePartStr().'<br />'; // (the whole MIME part of the attachment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
```
|
||||||
|
|
||||||
|
Next you need to forward emails to this script above. For that I'm using [Postfix](http://www.postfix.org/) like a mail server, you need to configure /etc/postfix/master.cf
|
||||||
|
|
||||||
|
Add this line at the end of the file (specify myhook to send all emails to the script test.php)
|
||||||
|
```
|
||||||
|
myhook unix - n n - - pipe
|
||||||
|
flags=F user=www-data argv=php -c /etc/php5/apache2/php.ini -f /var/www/test.php ${sender} ${size} ${recipient}
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit this line (register myhook)
|
||||||
|
```
|
||||||
|
smtp inet n - - - - smtpd
|
||||||
|
-o content_filter=myhook:dummy
|
||||||
|
```
|
||||||
|
|
||||||
|
The php script must use the fourth method to work with this configuration.
|
||||||
|
|
||||||
|
|
||||||
|
## Can I contribute?
|
||||||
|
|
||||||
|
Feel free to contribute!
|
||||||
|
|
||||||
|
git clone https://github.com/php-mime-mail-parser/php-mime-mail-parser
|
||||||
|
cd php-mime-mail-parser
|
||||||
|
composer install
|
||||||
|
./vendor/bin/phpunit
|
||||||
|
|
||||||
|
If you report an issue, please provide the raw email that triggered it. This helps us reproduce the issue and fix it more quickly.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
The php-mime-mail-parser/php-mime-mail-parser is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"name": "php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
|
||||||
|
"keywords": ["mime", "mail", "mailparse", "MimeMailParser"],
|
||||||
|
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name":"eXorus",
|
||||||
|
"email":"exorus.spam@gmail.com",
|
||||||
|
"homepage":"https://github.com/eXorus/",
|
||||||
|
"role":"Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"M.Valinskis",
|
||||||
|
"email":"M.Valins@gmail.com",
|
||||||
|
"homepage":"https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role":"Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"eugene.emmett.wood",
|
||||||
|
"email":"gene_w@cementhorizon.com",
|
||||||
|
"homepage":"https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role":"Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"alknetso",
|
||||||
|
"email":"alkne@gmail.com",
|
||||||
|
"homepage":"https://code.google.com/p/php-mime-mail-parser",
|
||||||
|
"role":"Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"bucabay",
|
||||||
|
"email":"gabe@fijiwebdesign.com",
|
||||||
|
"homepage":"http://www.fijiwebdesign.com",
|
||||||
|
"role":"Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository":{
|
||||||
|
"type":"git",
|
||||||
|
"url":"https://github.com/php-mime-mail-parser/php-mime-mail-parser.git"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^5.4.0 || ^7.0",
|
||||||
|
"ext-mailparse": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.0 || ^5.0",
|
||||||
|
"phpunit/php-token-stream": "^1.3.0",
|
||||||
|
"satooshi/php-coveralls": "0.*",
|
||||||
|
"squizlabs/PHP_CodeSniffer": "2.*"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"exorus/php-mime-mail-parser": "*",
|
||||||
|
"messaged/php-mime-mail-parser": "*"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "PhpMimeMailParser\\": "src/" }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,303 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @link http://php.net/manual/en/mailparse.constants.php
|
||||||
|
*/
|
||||||
|
define('MAILPARSE_EXTRACT_OUTPUT', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link http://php.net/manual/en/mailparse.constants.php
|
||||||
|
*/
|
||||||
|
define('MAILPARSE_EXTRACT_STREAM', 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link http://php.net/manual/en/mailparse.constants.php
|
||||||
|
*/
|
||||||
|
define('MAILPARSE_EXTRACT_RETURN', 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a file. This is the optimal way of parsing a mail file that you have on
|
||||||
|
* disk.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-parse-file.php
|
||||||
|
*
|
||||||
|
* @param string $filename Path to the file holding the message. The file is opened
|
||||||
|
* and streamed through the parser
|
||||||
|
*
|
||||||
|
* @return resource Returns a MIME resource representing the structure, or false on error
|
||||||
|
*/
|
||||||
|
function mailparse_msg_parse_file($filename)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-get-part.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
* @param string $mimesection
|
||||||
|
*
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
function mailparse_msg_get_part($mimemail, $mimesection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-get-structure.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function mailparse_msg_get_structure($mimemail)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-get-part-data.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function mailparse_msg_get_part_data($mimemail)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-extract-part.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
* @param string $msgbody
|
||||||
|
* @param callable $callbackfunc
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function mailparse_msg_extract_part($mimemail, $msgbody, $callbackfunc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts/decodes a message section from the supplied filename.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-extract-part-file.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource, created with
|
||||||
|
* mailparse_msg_create
|
||||||
|
* @param mixed $filename Can be a file name or a valid stream resource
|
||||||
|
* @param callable $callbackfunc If set, this must be either a valid callback that
|
||||||
|
* will be passed the extracted section, or null to make this function return the
|
||||||
|
* extracted section
|
||||||
|
*
|
||||||
|
* @return string If $callbackfunc is not null returns true on success
|
||||||
|
*/
|
||||||
|
function mailparse_msg_extract_part_file($mimemail, $filename, $callbackfunc = false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-extract-whole-part-file.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
* @param string $filename
|
||||||
|
* @param callable $callbackfunc
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function mailparse_msg_extract_whole_part_file($mimemail, $filename, $callbackfunc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MIME mail resource.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-create.php
|
||||||
|
* @return resource Returns a handle that can be used to parse a message
|
||||||
|
*/
|
||||||
|
function mailparse_msg_create()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees a MIME resource.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-free.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource allocated by
|
||||||
|
* mailparse_msg_create or mailparse_msg_parse_file
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function mailparse_msg_free($mimemail)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incrementally parse data into the supplied mime mail resource.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-msg-parse.php
|
||||||
|
*
|
||||||
|
* @param resource $mimemail A valid MIME resource
|
||||||
|
* @param string $data
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function mailparse_msg_parse($mimemail, $data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a RFC 822 compliant recipient list, such as that found in the To: header.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-rfc822-parse-addresses.php
|
||||||
|
*
|
||||||
|
* @param string $addresses A string containing addresses, like in: Wez Furlong
|
||||||
|
* wez@example.com, doe@example.com
|
||||||
|
*
|
||||||
|
* @return array Returns an array of associative arrays with the following keys for each
|
||||||
|
* recipient: display The recipient name, for display purpose. If this part is not
|
||||||
|
* set for a recipient, this key will hold the same value as address. address The
|
||||||
|
* email address is_group true if the recipient is a newsgroup, false otherwise
|
||||||
|
*/
|
||||||
|
function mailparse_rfc822_parse_addresses($addresses)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figures out the best way of encoding the content read from the given file
|
||||||
|
* pointer.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-determine-best-xfer-encoding.php
|
||||||
|
*
|
||||||
|
* @param resource $fp A valid file pointer, which must be seek-able
|
||||||
|
*
|
||||||
|
* @return string Returns one of the character encodings supported by the mbstring module
|
||||||
|
*/
|
||||||
|
function mailparse_determine_best_xfer_encoding($fp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streams data from the source file pointer, apply $encoding and write to the
|
||||||
|
* destination file pointer.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-stream-encode.php
|
||||||
|
*
|
||||||
|
* @param resource $sourcefp A valid file handle. The file is streamed through the
|
||||||
|
* parser
|
||||||
|
* @param resource $destfp The destination file handle in which the encoded data
|
||||||
|
* will be written
|
||||||
|
* @param string $encoding One of the character encodings supported by the mbstring
|
||||||
|
* module
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function mailparse_stream_encode($sourcefp, $destfp, $encoding)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the data from the given file pointer and extract each embedded uuencoded
|
||||||
|
* file into a temporary file.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/functions.mailparse-uudecode-all.php
|
||||||
|
*
|
||||||
|
* @param resource $fp A valid file pointer
|
||||||
|
*
|
||||||
|
* @return array Returns an array of associative arrays listing filename information.
|
||||||
|
* filename Path to the temporary file name created origfilename The original
|
||||||
|
* filename, for uuencoded parts only The first filename entry is the message body.
|
||||||
|
* The next entries are the decoded uuencoded files
|
||||||
|
*/
|
||||||
|
function mailparse_uudecode_all($fp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
function mailparse_test()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class mimemessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function mimemessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get_child()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get_child_count()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function get_parent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function extract_headers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function extract_body()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function enum_uue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function extract_uue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function remove()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function add_child()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit colors="true" bootstrap="vendor/autoload.php">
|
||||||
|
<testsuite name="eXorus PhpMimeMailParser Test Suite">
|
||||||
|
<directory suffix="Test.php">tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</phpunit>
|
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpMimeMailParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attachment of php-mime-mail-parser
|
||||||
|
*
|
||||||
|
* Fully Tested Mailparse Extension Wrapper for PHP 5.4+
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Attachment
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string $filename Filename
|
||||||
|
*/
|
||||||
|
protected $filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $contentType Mime Type
|
||||||
|
*/
|
||||||
|
protected $contentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $content File Content
|
||||||
|
*/
|
||||||
|
protected $content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $contentDisposition Content-Disposition (attachment or inline)
|
||||||
|
*/
|
||||||
|
protected $contentDisposition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $contentId Content-ID
|
||||||
|
*/
|
||||||
|
protected $contentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array $headers An Array of the attachment headers
|
||||||
|
*/
|
||||||
|
protected $headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var resource $stream
|
||||||
|
*/
|
||||||
|
protected $stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $mimePartStr
|
||||||
|
*/
|
||||||
|
protected $mimePartStr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attachment constructor.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @param string $contentType
|
||||||
|
* @param resource $stream
|
||||||
|
* @param string $contentDisposition
|
||||||
|
* @param string $contentId
|
||||||
|
* @param array $headers
|
||||||
|
* @param string $mimePartStr
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$filename,
|
||||||
|
$contentType,
|
||||||
|
$stream,
|
||||||
|
$contentDisposition = 'attachment',
|
||||||
|
$contentId = '',
|
||||||
|
$headers = [],
|
||||||
|
$mimePartStr = ''
|
||||||
|
) {
|
||||||
|
$this->filename = $filename;
|
||||||
|
$this->contentType = $contentType;
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->content = null;
|
||||||
|
$this->contentDisposition = $contentDisposition;
|
||||||
|
$this->contentId = $contentId;
|
||||||
|
$this->headers = $headers;
|
||||||
|
$this->mimePartStr = $mimePartStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve the attachment filename
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFilename()
|
||||||
|
{
|
||||||
|
return $this->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Attachment Content-Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentType()
|
||||||
|
{
|
||||||
|
return $this->contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Attachment Content-Disposition
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentDisposition()
|
||||||
|
{
|
||||||
|
return $this->contentDisposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Attachment Content-ID
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContentID()
|
||||||
|
{
|
||||||
|
return $this->contentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Attachment Headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHeaders()
|
||||||
|
{
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a handle to the stream
|
||||||
|
*
|
||||||
|
* @return stream
|
||||||
|
*/
|
||||||
|
public function getStream()
|
||||||
|
{
|
||||||
|
return $this->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the contents a few bytes at a time until completed
|
||||||
|
* Once read to completion, it always returns false
|
||||||
|
*
|
||||||
|
* @param int $bytes (default: 2082)
|
||||||
|
*
|
||||||
|
* @return string|bool
|
||||||
|
*/
|
||||||
|
public function read($bytes = 2082)
|
||||||
|
{
|
||||||
|
return feof($this->stream) ? false : fread($this->stream, $bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the file content in one go
|
||||||
|
* Once you retrieve the content you cannot use MimeMailParser_attachment::read()
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if ($this->content === null) {
|
||||||
|
fseek($this->stream, 0);
|
||||||
|
while (($buf = $this->read()) !== false) {
|
||||||
|
$this->content .= $buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mime part string for this attachment
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMimePartStr()
|
||||||
|
{
|
||||||
|
return $this->mimePartStr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
<?php namespace PhpMimeMailParser;
|
||||||
|
|
||||||
|
use PhpMimeMailParser\Contracts\CharsetManager;
|
||||||
|
|
||||||
|
class Charset implements CharsetManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Charset Aliases
|
||||||
|
*/
|
||||||
|
private $charsetAlias = [
|
||||||
|
'ascii' => 'us-ascii',
|
||||||
|
'us-ascii' => 'us-ascii',
|
||||||
|
'ansi_x3.4-1968' => 'us-ascii',
|
||||||
|
'646' => 'us-ascii',
|
||||||
|
'iso-8859-1' => 'ISO-8859-1',
|
||||||
|
'iso-8859-2' => 'ISO-8859-2',
|
||||||
|
'iso-8859-3' => 'ISO-8859-3',
|
||||||
|
'iso-8859-4' => 'ISO-8859-4',
|
||||||
|
'iso-8859-5' => 'ISO-8859-5',
|
||||||
|
'iso-8859-6' => 'ISO-8859-6',
|
||||||
|
'iso-8859-6-i' => 'ISO-8859-6-I',
|
||||||
|
'iso-8859-6-e' => 'ISO-8859-6-E',
|
||||||
|
'iso-8859-7' => 'ISO-8859-7',
|
||||||
|
'iso-8859-8' => 'ISO-8859-8',
|
||||||
|
'iso-8859-8-i' => 'ISO-8859-8',
|
||||||
|
'iso-8859-8-e' => 'ISO-8859-8-E',
|
||||||
|
'iso-8859-9' => 'ISO-8859-9',
|
||||||
|
'iso-8859-10' => 'ISO-8859-10',
|
||||||
|
'iso-8859-11' => 'ISO-8859-11',
|
||||||
|
'iso-8859-13' => 'ISO-8859-13',
|
||||||
|
'iso-8859-14' => 'ISO-8859-14',
|
||||||
|
'iso-8859-15' => 'ISO-8859-15',
|
||||||
|
'iso-8859-16' => 'ISO-8859-16',
|
||||||
|
'iso-ir-111' => 'ISO-IR-111',
|
||||||
|
'iso-2022-cn' => 'ISO-2022-CN',
|
||||||
|
'iso-2022-cn-ext' => 'ISO-2022-CN',
|
||||||
|
'iso-2022-kr' => 'ISO-2022-KR',
|
||||||
|
'iso-2022-jp' => 'ISO-2022-JP',
|
||||||
|
'utf-16be' => 'UTF-16BE',
|
||||||
|
'utf-16le' => 'UTF-16LE',
|
||||||
|
'utf-16' => 'UTF-16',
|
||||||
|
'windows-1250' => 'windows-1250',
|
||||||
|
'windows-1251' => 'windows-1251',
|
||||||
|
'windows-1252' => 'windows-1252',
|
||||||
|
'windows-1253' => 'windows-1253',
|
||||||
|
'windows-1254' => 'windows-1254',
|
||||||
|
'windows-1255' => 'windows-1255',
|
||||||
|
'windows-1256' => 'windows-1256',
|
||||||
|
'windows-1257' => 'windows-1257',
|
||||||
|
'windows-1258' => 'windows-1258',
|
||||||
|
'ibm866' => 'IBM866',
|
||||||
|
'ibm850' => 'IBM850',
|
||||||
|
'ibm852' => 'IBM852',
|
||||||
|
'ibm855' => 'IBM855',
|
||||||
|
'ibm857' => 'IBM857',
|
||||||
|
'ibm862' => 'IBM862',
|
||||||
|
'ibm864' => 'IBM864',
|
||||||
|
'utf-8' => 'UTF-8',
|
||||||
|
'utf-7' => 'UTF-7',
|
||||||
|
'shift_jis' => 'Shift_JIS',
|
||||||
|
'big5' => 'Big5',
|
||||||
|
'euc-jp' => 'EUC-JP',
|
||||||
|
'euc-kr' => 'EUC-KR',
|
||||||
|
'gb2312' => 'GB2312',
|
||||||
|
'gb18030' => 'gb18030',
|
||||||
|
'viscii' => 'VISCII',
|
||||||
|
'koi8-r' => 'KOI8-R',
|
||||||
|
'koi8_r' => 'KOI8-R',
|
||||||
|
'cskoi8r' => 'KOI8-R',
|
||||||
|
'koi' => 'KOI8-R',
|
||||||
|
'koi8' => 'KOI8-R',
|
||||||
|
'koi8-u' => 'KOI8-U',
|
||||||
|
'tis-620' => 'TIS-620',
|
||||||
|
't.61-8bit' => 'T.61-8bit',
|
||||||
|
'hz-gb-2312' => 'HZ-GB-2312',
|
||||||
|
'big5-hkscs' => 'Big5-HKSCS',
|
||||||
|
'gbk' => 'gbk',
|
||||||
|
'cns11643' => 'x-euc-tw',
|
||||||
|
'x-imap4-modified-utf7' => 'x-imap4-modified-utf7',
|
||||||
|
'x-euc-tw' => 'x-euc-tw',
|
||||||
|
'x-mac-ce' => 'x-mac-ce',
|
||||||
|
'x-mac-turkish' => 'x-mac-turkish',
|
||||||
|
'x-mac-greek' => 'x-mac-greek',
|
||||||
|
'x-mac-icelandic' => 'x-mac-icelandic',
|
||||||
|
'x-mac-croatian' => 'x-mac-croatian',
|
||||||
|
'x-mac-romanian' => 'x-mac-romanian',
|
||||||
|
'x-mac-cyrillic' => 'x-mac-cyrillic',
|
||||||
|
'x-mac-ukrainian' => 'x-mac-cyrillic',
|
||||||
|
'x-mac-hebrew' => 'x-mac-hebrew',
|
||||||
|
'x-mac-arabic' => 'x-mac-arabic',
|
||||||
|
'x-mac-farsi' => 'x-mac-farsi',
|
||||||
|
'x-mac-devanagari' => 'x-mac-devanagari',
|
||||||
|
'x-mac-gujarati' => 'x-mac-gujarati',
|
||||||
|
'x-mac-gurmukhi' => 'x-mac-gurmukhi',
|
||||||
|
'armscii-8' => 'armscii-8',
|
||||||
|
'x-viet-tcvn5712' => 'x-viet-tcvn5712',
|
||||||
|
'x-viet-vps' => 'x-viet-vps',
|
||||||
|
'iso-10646-ucs-2' => 'UTF-16BE',
|
||||||
|
'x-iso-10646-ucs-2-be' => 'UTF-16BE',
|
||||||
|
'x-iso-10646-ucs-2-le' => 'UTF-16LE',
|
||||||
|
'x-user-defined' => 'x-user-defined',
|
||||||
|
'x-johab' => 'x-johab',
|
||||||
|
'latin1' => 'ISO-8859-1',
|
||||||
|
'iso_8859-1' => 'ISO-8859-1',
|
||||||
|
'iso8859-1' => 'ISO-8859-1',
|
||||||
|
'iso8859-2' => 'ISO-8859-2',
|
||||||
|
'iso8859-3' => 'ISO-8859-3',
|
||||||
|
'iso8859-4' => 'ISO-8859-4',
|
||||||
|
'iso8859-5' => 'ISO-8859-5',
|
||||||
|
'iso8859-6' => 'ISO-8859-6',
|
||||||
|
'iso8859-7' => 'ISO-8859-7',
|
||||||
|
'iso8859-8' => 'ISO-8859-8',
|
||||||
|
'iso8859-9' => 'ISO-8859-9',
|
||||||
|
'iso8859-10' => 'ISO-8859-10',
|
||||||
|
'iso8859-11' => 'ISO-8859-11',
|
||||||
|
'iso8859-13' => 'ISO-8859-13',
|
||||||
|
'iso8859-14' => 'ISO-8859-14',
|
||||||
|
'iso8859-15' => 'ISO-8859-15',
|
||||||
|
'iso_8859-1:1987' => 'ISO-8859-1',
|
||||||
|
'iso-ir-100' => 'ISO-8859-1',
|
||||||
|
'l1' => 'ISO-8859-1',
|
||||||
|
'ibm819' => 'ISO-8859-1',
|
||||||
|
'cp819' => 'ISO-8859-1',
|
||||||
|
'csisolatin1' => 'ISO-8859-1',
|
||||||
|
'latin2' => 'ISO-8859-2',
|
||||||
|
'iso_8859-2' => 'ISO-8859-2',
|
||||||
|
'iso_8859-2:1987' => 'ISO-8859-2',
|
||||||
|
'iso-ir-101' => 'ISO-8859-2',
|
||||||
|
'l2' => 'ISO-8859-2',
|
||||||
|
'csisolatin2' => 'ISO-8859-2',
|
||||||
|
'latin3' => 'ISO-8859-3',
|
||||||
|
'iso_8859-3' => 'ISO-8859-3',
|
||||||
|
'iso_8859-3:1988' => 'ISO-8859-3',
|
||||||
|
'iso-ir-109' => 'ISO-8859-3',
|
||||||
|
'l3' => 'ISO-8859-3',
|
||||||
|
'csisolatin3' => 'ISO-8859-3',
|
||||||
|
'latin4' => 'ISO-8859-4',
|
||||||
|
'iso_8859-4' => 'ISO-8859-4',
|
||||||
|
'iso_8859-4:1988' => 'ISO-8859-4',
|
||||||
|
'iso-ir-110' => 'ISO-8859-4',
|
||||||
|
'l4' => 'ISO-8859-4',
|
||||||
|
'csisolatin4' => 'ISO-8859-4',
|
||||||
|
'cyrillic' => 'ISO-8859-5',
|
||||||
|
'iso_8859-5' => 'ISO-8859-5',
|
||||||
|
'iso_8859-5:1988' => 'ISO-8859-5',
|
||||||
|
'iso-ir-144' => 'ISO-8859-5',
|
||||||
|
'csisolatincyrillic' => 'ISO-8859-5',
|
||||||
|
'arabic' => 'ISO-8859-6',
|
||||||
|
'iso_8859-6' => 'ISO-8859-6',
|
||||||
|
'iso_8859-6:1987' => 'ISO-8859-6',
|
||||||
|
'iso-ir-127' => 'ISO-8859-6',
|
||||||
|
'ecma-114' => 'ISO-8859-6',
|
||||||
|
'asmo-708' => 'ISO-8859-6',
|
||||||
|
'csisolatinarabic' => 'ISO-8859-6',
|
||||||
|
'csiso88596i' => 'ISO-8859-6-I',
|
||||||
|
'csiso88596e' => 'ISO-8859-6-E',
|
||||||
|
'greek' => 'ISO-8859-7',
|
||||||
|
'greek8' => 'ISO-8859-7',
|
||||||
|
'sun_eu_greek' => 'ISO-8859-7',
|
||||||
|
'iso_8859-7' => 'ISO-8859-7',
|
||||||
|
'iso_8859-7:1987' => 'ISO-8859-7',
|
||||||
|
'iso-ir-126' => 'ISO-8859-7',
|
||||||
|
'elot_928' => 'ISO-8859-7',
|
||||||
|
'ecma-118' => 'ISO-8859-7',
|
||||||
|
'csisolatingreek' => 'ISO-8859-7',
|
||||||
|
'hebrew' => 'ISO-8859-8',
|
||||||
|
'iso_8859-8' => 'ISO-8859-8',
|
||||||
|
'visual' => 'ISO-8859-8',
|
||||||
|
'iso_8859-8:1988' => 'ISO-8859-8',
|
||||||
|
'iso-ir-138' => 'ISO-8859-8',
|
||||||
|
'csisolatinhebrew' => 'ISO-8859-8',
|
||||||
|
'csiso88598i' => 'ISO-8859-8',
|
||||||
|
'iso-8859-8i' => 'ISO-8859-8',
|
||||||
|
'logical' => 'ISO-8859-8',
|
||||||
|
'csiso88598e' => 'ISO-8859-8-E',
|
||||||
|
'latin5' => 'ISO-8859-9',
|
||||||
|
'iso_8859-9' => 'ISO-8859-9',
|
||||||
|
'iso_8859-9:1989' => 'ISO-8859-9',
|
||||||
|
'iso-ir-148' => 'ISO-8859-9',
|
||||||
|
'l5' => 'ISO-8859-9',
|
||||||
|
'csisolatin5' => 'ISO-8859-9',
|
||||||
|
'unicode-1-1-utf-8' => 'UTF-8',
|
||||||
|
'utf8' => 'UTF-8',
|
||||||
|
'x-sjis' => 'Shift_JIS',
|
||||||
|
'shift-jis' => 'Shift_JIS',
|
||||||
|
'ms_kanji' => 'Shift_JIS',
|
||||||
|
'csshiftjis' => 'Shift_JIS',
|
||||||
|
'windows-31j' => 'Shift_JIS',
|
||||||
|
'cp932' => 'Shift_JIS',
|
||||||
|
'sjis' => 'Shift_JIS',
|
||||||
|
'cseucpkdfmtjapanese' => 'EUC-JP',
|
||||||
|
'x-euc-jp' => 'EUC-JP',
|
||||||
|
'csiso2022jp' => 'ISO-2022-JP',
|
||||||
|
'iso-2022-jp-2' => 'ISO-2022-JP',
|
||||||
|
'csiso2022jp2' => 'ISO-2022-JP',
|
||||||
|
'csbig5' => 'Big5',
|
||||||
|
'cn-big5' => 'Big5',
|
||||||
|
'x-x-big5' => 'Big5',
|
||||||
|
'zh_tw-big5' => 'Big5',
|
||||||
|
'cseuckr' => 'EUC-KR',
|
||||||
|
'ks_c_5601-1987' => 'EUC-KR',
|
||||||
|
'iso-ir-149' => 'EUC-KR',
|
||||||
|
'ks_c_5601-1989' => 'EUC-KR',
|
||||||
|
'ksc_5601' => 'EUC-KR',
|
||||||
|
'ksc5601' => 'EUC-KR',
|
||||||
|
'korean' => 'EUC-KR',
|
||||||
|
'csksc56011987' => 'EUC-KR',
|
||||||
|
'5601' => 'EUC-KR',
|
||||||
|
'windows-949' => 'EUC-KR',
|
||||||
|
'gb_2312-80' => 'GB2312',
|
||||||
|
'iso-ir-58' => 'GB2312',
|
||||||
|
'chinese' => 'GB2312',
|
||||||
|
'csiso58gb231280' => 'GB2312',
|
||||||
|
'csgb2312' => 'GB2312',
|
||||||
|
'zh_cn.euc' => 'GB2312',
|
||||||
|
'gb_2312' => 'GB2312',
|
||||||
|
'x-cp1250' => 'windows-1250',
|
||||||
|
'x-cp1251' => 'windows-1251',
|
||||||
|
'x-cp1252' => 'windows-1252',
|
||||||
|
'x-cp1253' => 'windows-1253',
|
||||||
|
'x-cp1254' => 'windows-1254',
|
||||||
|
'x-cp1255' => 'windows-1255',
|
||||||
|
'x-cp1256' => 'windows-1256',
|
||||||
|
'x-cp1257' => 'windows-1257',
|
||||||
|
'x-cp1258' => 'windows-1258',
|
||||||
|
'windows-874' => 'windows-874',
|
||||||
|
'ibm874' => 'windows-874',
|
||||||
|
'dos-874' => 'windows-874',
|
||||||
|
'macintosh' => 'macintosh',
|
||||||
|
'x-mac-roman' => 'macintosh',
|
||||||
|
'mac' => 'macintosh',
|
||||||
|
'csmacintosh' => 'macintosh',
|
||||||
|
'cp866' => 'IBM866',
|
||||||
|
'cp-866' => 'IBM866',
|
||||||
|
'866' => 'IBM866',
|
||||||
|
'csibm866' => 'IBM866',
|
||||||
|
'cp850' => 'IBM850',
|
||||||
|
'850' => 'IBM850',
|
||||||
|
'csibm850' => 'IBM850',
|
||||||
|
'cp852' => 'IBM852',
|
||||||
|
'852' => 'IBM852',
|
||||||
|
'csibm852' => 'IBM852',
|
||||||
|
'cp855' => 'IBM855',
|
||||||
|
'855' => 'IBM855',
|
||||||
|
'csibm855' => 'IBM855',
|
||||||
|
'cp857' => 'IBM857',
|
||||||
|
'857' => 'IBM857',
|
||||||
|
'csibm857' => 'IBM857',
|
||||||
|
'cp862' => 'IBM862',
|
||||||
|
'862' => 'IBM862',
|
||||||
|
'csibm862' => 'IBM862',
|
||||||
|
'cp864' => 'IBM864',
|
||||||
|
'864' => 'IBM864',
|
||||||
|
'csibm864' => 'IBM864',
|
||||||
|
'ibm-864' => 'IBM864',
|
||||||
|
't.61' => 'T.61-8bit',
|
||||||
|
'iso-ir-103' => 'T.61-8bit',
|
||||||
|
'csiso103t618bit' => 'T.61-8bit',
|
||||||
|
'x-unicode-2-0-utf-7' => 'UTF-7',
|
||||||
|
'unicode-2-0-utf-7' => 'UTF-7',
|
||||||
|
'unicode-1-1-utf-7' => 'UTF-7',
|
||||||
|
'csunicode11utf7' => 'UTF-7',
|
||||||
|
'csunicode' => 'UTF-16BE',
|
||||||
|
'csunicode11' => 'UTF-16BE',
|
||||||
|
'iso-10646-ucs-basic' => 'UTF-16BE',
|
||||||
|
'csunicodeascii' => 'UTF-16BE',
|
||||||
|
'iso-10646-unicode-latin1' => 'UTF-16BE',
|
||||||
|
'csunicodelatin1' => 'UTF-16BE',
|
||||||
|
'iso-10646' => 'UTF-16BE',
|
||||||
|
'iso-10646-j-1' => 'UTF-16BE',
|
||||||
|
'latin6' => 'ISO-8859-10',
|
||||||
|
'iso-ir-157' => 'ISO-8859-10',
|
||||||
|
'l6' => 'ISO-8859-10',
|
||||||
|
'csisolatin6' => 'ISO-8859-10',
|
||||||
|
'iso_8859-15' => 'ISO-8859-15',
|
||||||
|
'csisolatin9' => 'ISO-8859-15',
|
||||||
|
'l9' => 'ISO-8859-15',
|
||||||
|
'ecma-cyrillic' => 'ISO-IR-111',
|
||||||
|
'csiso111ecmacyrillic' => 'ISO-IR-111',
|
||||||
|
'csiso2022kr' => 'ISO-2022-KR',
|
||||||
|
'csviscii' => 'VISCII',
|
||||||
|
'zh_tw-euc' => 'x-euc-tw',
|
||||||
|
'iso88591' => 'ISO-8859-1',
|
||||||
|
'iso88592' => 'ISO-8859-2',
|
||||||
|
'iso88593' => 'ISO-8859-3',
|
||||||
|
'iso88594' => 'ISO-8859-4',
|
||||||
|
'iso88595' => 'ISO-8859-5',
|
||||||
|
'iso88596' => 'ISO-8859-6',
|
||||||
|
'iso88597' => 'ISO-8859-7',
|
||||||
|
'iso88598' => 'ISO-8859-8',
|
||||||
|
'iso88599' => 'ISO-8859-9',
|
||||||
|
'iso885910' => 'ISO-8859-10',
|
||||||
|
'iso885911' => 'ISO-8859-11',
|
||||||
|
'iso885912' => 'ISO-8859-12',
|
||||||
|
'iso885913' => 'ISO-8859-13',
|
||||||
|
'iso885914' => 'ISO-8859-14',
|
||||||
|
'iso885915' => 'ISO-8859-15',
|
||||||
|
'tis620' => 'TIS-620',
|
||||||
|
'cp1250' => 'windows-1250',
|
||||||
|
'cp1251' => 'windows-1251',
|
||||||
|
'cp1252' => 'windows-1252',
|
||||||
|
'cp1253' => 'windows-1253',
|
||||||
|
'cp1254' => 'windows-1254',
|
||||||
|
'cp1255' => 'windows-1255',
|
||||||
|
'cp1256' => 'windows-1256',
|
||||||
|
'cp1257' => 'windows-1257',
|
||||||
|
'cp1258' => 'windows-1258',
|
||||||
|
'x-gbk' => 'gbk',
|
||||||
|
'windows-936' => 'gbk',
|
||||||
|
'ansi-1251' => 'windows-1251',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function decodeCharset($encodedString, $charset)
|
||||||
|
{
|
||||||
|
if (strtolower($charset) == 'utf-8' || strtolower($charset) == 'us-ascii') {
|
||||||
|
return $encodedString;
|
||||||
|
} else {
|
||||||
|
return iconv($this->getCharsetAlias($charset), 'UTF-8//TRANSLIT//IGNORE', $encodedString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCharsetAlias($charset)
|
||||||
|
{
|
||||||
|
$charset = strtolower($charset);
|
||||||
|
|
||||||
|
if (array_key_exists($charset, $this->charsetAlias)) {
|
||||||
|
return $this->charsetAlias[$charset];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php namespace PhpMimeMailParser\Contracts;
|
||||||
|
|
||||||
|
interface CharsetManager
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the string from Charset
|
||||||
|
*
|
||||||
|
* @param string $encodedString The string in its original encoded state
|
||||||
|
* @param string $charset The Charset header of the part.
|
||||||
|
*
|
||||||
|
* @return string The decoded string
|
||||||
|
*/
|
||||||
|
public function decodeCharset($encodedString, $charset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get charset alias
|
||||||
|
*
|
||||||
|
* @param string $charset .
|
||||||
|
*
|
||||||
|
* @return string The charset alias
|
||||||
|
*/
|
||||||
|
public function getCharsetAlias($charset);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpMimeMailParser;
|
||||||
|
|
||||||
|
class Exception extends \RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
893
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Parser.php
vendored
100644
893
data/web/inc/lib/vendor/php-mime-mail-parser/php-mime-mail-parser/src/Parser.php
vendored
100644
|
@ -0,0 +1,893 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpMimeMailParser;
|
||||||
|
|
||||||
|
use PhpMimeMailParser\Contracts\CharsetManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser of php-mime-mail-parser
|
||||||
|
*
|
||||||
|
* Fully Tested Mailparse Extension Wrapper for PHP 5.4+
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Attachment filename argument option for ->saveAttachments().
|
||||||
|
*/
|
||||||
|
const ATTACHMENT_DUPLICATE_THROW = 'DuplicateThrow';
|
||||||
|
const ATTACHMENT_DUPLICATE_SUFFIX = 'DuplicateSuffix';
|
||||||
|
const ATTACHMENT_RANDOM_FILENAME = 'RandomFilename';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP MimeParser Resource ID
|
||||||
|
*
|
||||||
|
* @var resource $resource
|
||||||
|
*/
|
||||||
|
protected $resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file pointer to email
|
||||||
|
*
|
||||||
|
* @var resource $stream
|
||||||
|
*/
|
||||||
|
protected $stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text of an email
|
||||||
|
*
|
||||||
|
* @var string $data
|
||||||
|
*/
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parts of an email
|
||||||
|
*
|
||||||
|
* @var array $parts
|
||||||
|
*/
|
||||||
|
protected $parts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CharsetManager object
|
||||||
|
*/
|
||||||
|
protected $charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser constructor.
|
||||||
|
*
|
||||||
|
* @param CharsetManager|null $charset
|
||||||
|
*/
|
||||||
|
public function __construct(CharsetManager $charset = null)
|
||||||
|
{
|
||||||
|
if ($charset == null) {
|
||||||
|
$charset = new Charset();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the held resources
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
// clear the email file resource
|
||||||
|
if (is_resource($this->stream)) {
|
||||||
|
fclose($this->stream);
|
||||||
|
}
|
||||||
|
// clear the MailParse resource
|
||||||
|
if (is_resource($this->resource)) {
|
||||||
|
mailparse_msg_free($this->resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the file path we use to get the email text
|
||||||
|
*
|
||||||
|
* @param string $path File path to the MIME mail
|
||||||
|
*
|
||||||
|
* @return Parser MimeMailParser Instance
|
||||||
|
*/
|
||||||
|
public function setPath($path)
|
||||||
|
{
|
||||||
|
// should parse message incrementally from file
|
||||||
|
$this->resource = mailparse_msg_parse_file($path);
|
||||||
|
$this->stream = fopen($path, 'r');
|
||||||
|
$this->parse();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Stream resource we use to get the email text
|
||||||
|
*
|
||||||
|
* @param resource $stream
|
||||||
|
*
|
||||||
|
* @return Parser MimeMailParser Instance
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function setStream($stream)
|
||||||
|
{
|
||||||
|
// streams have to be cached to file first
|
||||||
|
$meta = @stream_get_meta_data($stream);
|
||||||
|
if (!$meta || !$meta['mode'] || $meta['mode'][0] != 'r' || $meta['eof']) {
|
||||||
|
throw new Exception(
|
||||||
|
'setStream() expects parameter stream to be readable stream resource.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var resource $tmp_fp */
|
||||||
|
$tmp_fp = tmpfile();
|
||||||
|
if ($tmp_fp) {
|
||||||
|
while (!feof($stream)) {
|
||||||
|
fwrite($tmp_fp, fread($stream, 2028));
|
||||||
|
}
|
||||||
|
fseek($tmp_fp, 0);
|
||||||
|
$this->stream = &$tmp_fp;
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fclose($stream);
|
||||||
|
|
||||||
|
$this->resource = mailparse_msg_create();
|
||||||
|
// parses the message incrementally (low memory usage but slower)
|
||||||
|
while (!feof($this->stream)) {
|
||||||
|
mailparse_msg_parse($this->resource, fread($this->stream, 2082));
|
||||||
|
}
|
||||||
|
$this->parse();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the email text
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
*
|
||||||
|
* @return Parser MimeMailParser Instance
|
||||||
|
*/
|
||||||
|
public function setText($data)
|
||||||
|
{
|
||||||
|
if (!$data) {
|
||||||
|
throw new Exception('You must not call MimeMailParser::setText with an empty string parameter');
|
||||||
|
}
|
||||||
|
$this->resource = mailparse_msg_create();
|
||||||
|
// does not parse incrementally, fast memory hog might explode
|
||||||
|
mailparse_msg_parse($this->resource, $data);
|
||||||
|
$this->data = $data;
|
||||||
|
$this->parse();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the Message into parts
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function parse()
|
||||||
|
{
|
||||||
|
$structure = mailparse_msg_get_structure($this->resource);
|
||||||
|
$this->parts = [];
|
||||||
|
foreach ($structure as $part_id) {
|
||||||
|
$part = mailparse_msg_get_part($this->resource, $part_id);
|
||||||
|
$this->parts[$part_id] = mailparse_msg_get_part_data($part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a specific Email Header, without charset conversion.
|
||||||
|
*
|
||||||
|
* @param string $name Header name (case-insensitive)
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function getRawHeader($name)
|
||||||
|
{
|
||||||
|
$name = strtolower($name);
|
||||||
|
if (isset($this->parts[1])) {
|
||||||
|
$headers = $this->getPart('headers', $this->parts[1]);
|
||||||
|
|
||||||
|
return (isset($headers[$name])) ? $headers[$name] : false;
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'setPath() or setText() or setStream() must be called before retrieving email headers.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a specific Email Header
|
||||||
|
*
|
||||||
|
* @param string $name Header name (case-insensitive)
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHeader($name)
|
||||||
|
{
|
||||||
|
$rawHeader = $this->getRawHeader($name);
|
||||||
|
if ($rawHeader === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->decodeHeader($rawHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all mail headers
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function getHeaders()
|
||||||
|
{
|
||||||
|
if (isset($this->parts[1])) {
|
||||||
|
$headers = $this->getPart('headers', $this->parts[1]);
|
||||||
|
foreach ($headers as $name => &$value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
foreach ($value as &$v) {
|
||||||
|
$v = $this->decodeSingleHeader($v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $this->decodeSingleHeader($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $headers;
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'setPath() or setText() or setStream() must be called before retrieving email headers.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the raw mail headers as a string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function getHeadersRaw()
|
||||||
|
{
|
||||||
|
if (isset($this->parts[1])) {
|
||||||
|
return $this->getPartHeader($this->parts[1]);
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'setPath() or setText() or setStream() must be called before retrieving email headers.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the raw Header of a MIME part
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
* @param $part Object
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function getPartHeader(&$part)
|
||||||
|
{
|
||||||
|
$header = '';
|
||||||
|
if ($this->stream) {
|
||||||
|
$header = $this->getPartHeaderFromFile($part);
|
||||||
|
} elseif ($this->data) {
|
||||||
|
$header = $this->getPartHeaderFromText($part);
|
||||||
|
}
|
||||||
|
return $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Header from a MIME part from file
|
||||||
|
*
|
||||||
|
* @return String Mime Header Part
|
||||||
|
* @param $part Array
|
||||||
|
*/
|
||||||
|
protected function getPartHeaderFromFile(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos'];
|
||||||
|
$end = $part['starting-pos-body'];
|
||||||
|
fseek($this->stream, $start, SEEK_SET);
|
||||||
|
$header = fread($this->stream, $end-$start);
|
||||||
|
return $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Header from a MIME part from text
|
||||||
|
*
|
||||||
|
* @return String Mime Header Part
|
||||||
|
* @param $part Array
|
||||||
|
*/
|
||||||
|
protected function getPartHeaderFromText(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos'];
|
||||||
|
$end = $part['starting-pos-body'];
|
||||||
|
$header = substr($this->data, $start, $end-$start);
|
||||||
|
return $header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given part ID is a child of another part
|
||||||
|
* eg. an RFC822 attachment may have one or more text parts
|
||||||
|
*
|
||||||
|
* @param string $partId
|
||||||
|
* @param string $parentPartId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function partIdIsChildOfPart($partId, $parentPartId)
|
||||||
|
{
|
||||||
|
return substr($partId, 0, strlen($parentPartId)) == $parentPartId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the given part ID is a child of any attachment part in the message.
|
||||||
|
*
|
||||||
|
* @param string $checkPartId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function partIdIsChildOfAnAttachment($checkPartId)
|
||||||
|
{
|
||||||
|
foreach ($this->parts as $partId => $part) {
|
||||||
|
if ($this->getPart('content-disposition', $part) == 'attachment') {
|
||||||
|
if ($this->partIdIsChildOfPart($checkPartId, $partId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the email message body in the specified format
|
||||||
|
*
|
||||||
|
* @param string $type text, html or htmlEmbedded
|
||||||
|
*
|
||||||
|
* @return false|string Body or False if not found
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function getMessageBody($type = 'text')
|
||||||
|
{
|
||||||
|
$body = false;
|
||||||
|
$mime_types = [
|
||||||
|
'text' => 'text/plain',
|
||||||
|
'html' => 'text/html',
|
||||||
|
'htmlEmbedded' => 'text/html',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($type, array_keys($mime_types))) {
|
||||||
|
$part_type = $type === 'htmlEmbedded' ? 'html' : $type;
|
||||||
|
$inline_parts = $this->getInlineParts($part_type);
|
||||||
|
$body = empty($inline_parts) ? '' : $inline_parts[0];
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'Invalid type specified for getMessageBody(). Expected: text, html or htmlEmbeded.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type == 'htmlEmbedded') {
|
||||||
|
$attachments = $this->getAttachments();
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
if ($attachment->getContentID() != '') {
|
||||||
|
$body = str_replace(
|
||||||
|
'"cid:'.$attachment->getContentID().'"',
|
||||||
|
'"'.$this->getEmbeddedData($attachment->getContentID()).'"',
|
||||||
|
$body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the embedded data structure
|
||||||
|
*
|
||||||
|
* @param string $contentId Content-Id
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getEmbeddedData($contentId)
|
||||||
|
{
|
||||||
|
foreach ($this->parts as $part) {
|
||||||
|
if ($this->getPart('content-id', $part) == $contentId) {
|
||||||
|
$embeddedData = 'data:';
|
||||||
|
$embeddedData .= $this->getPart('content-type', $part);
|
||||||
|
$embeddedData .= ';'.$this->getPart('transfer-encoding', $part);
|
||||||
|
$embeddedData .= ','.$this->getPartBody($part);
|
||||||
|
return $embeddedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with the following keys display, address, is_group
|
||||||
|
*
|
||||||
|
* @param string $name Header name (case-insensitive)
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAddresses($name)
|
||||||
|
{
|
||||||
|
$value = $this->getHeader($name);
|
||||||
|
|
||||||
|
return mailparse_rfc822_parse_addresses($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attachments contents in order of appearance
|
||||||
|
*
|
||||||
|
* @return Attachment[]
|
||||||
|
*/
|
||||||
|
public function getInlineParts($type = 'text')
|
||||||
|
{
|
||||||
|
$inline_parts = [];
|
||||||
|
$dispositions = ['inline'];
|
||||||
|
$mime_types = [
|
||||||
|
'text' => 'text/plain',
|
||||||
|
'html' => 'text/html',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($type, array_keys($mime_types))) {
|
||||||
|
throw new Exception('Invalid type specified for getInlineParts(). "type" can either be text or html.');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->parts as $partId => $part) {
|
||||||
|
if ($this->getPart('content-type', $part) == $mime_types[$type]
|
||||||
|
&& $this->getPart('content-disposition', $part) != 'attachment'
|
||||||
|
&& !$this->partIdIsChildOfAnAttachment($partId)
|
||||||
|
) {
|
||||||
|
$headers = $this->getPart('headers', $part);
|
||||||
|
$encodingType = array_key_exists('content-transfer-encoding', $headers) ?
|
||||||
|
$headers['content-transfer-encoding'] : '';
|
||||||
|
if (is_array($encodingType)) {
|
||||||
|
$encodingType = $encodingType[0];
|
||||||
|
}
|
||||||
|
$undecoded_body = $this->decodeContentTransfer($this->getPartBody($part), $encodingType);
|
||||||
|
$inline_parts[] = $this->charset->decodeCharset($undecoded_body, $this->getPartCharset($part));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $inline_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attachments contents in order of appearance
|
||||||
|
*
|
||||||
|
* @return Attachment[]
|
||||||
|
*/
|
||||||
|
public function getAttachments($include_inline = true)
|
||||||
|
{
|
||||||
|
$attachments = [];
|
||||||
|
$dispositions = $include_inline ?
|
||||||
|
['attachment', 'inline'] :
|
||||||
|
['attachment'];
|
||||||
|
$non_attachment_types = ['text/plain', 'text/html'];
|
||||||
|
$nonameIter = 0;
|
||||||
|
|
||||||
|
foreach ($this->parts as $part) {
|
||||||
|
$disposition = $this->getPart('content-disposition', $part);
|
||||||
|
$filename = 'noname';
|
||||||
|
|
||||||
|
if (isset($part['disposition-filename'])) {
|
||||||
|
$filename = $this->decodeHeader($part['disposition-filename']);
|
||||||
|
// Escape all potentially unsafe characters from the filename
|
||||||
|
$filename = preg_replace('((^\.)|\/|(\.$))', '_', $filename);
|
||||||
|
} elseif (isset($part['content-name'])) {
|
||||||
|
// if we have no disposition but we have a content-name, it's a valid attachment.
|
||||||
|
// we simulate the presence of an attachment disposition with a disposition filename
|
||||||
|
$filename = $this->decodeHeader($part['content-name']);
|
||||||
|
// Escape all potentially unsafe characters from the filename
|
||||||
|
$filename = preg_replace('((^\.)|\/|(\.$))', '_', $filename);
|
||||||
|
$disposition = 'attachment';
|
||||||
|
} elseif (in_array($part['content-type'], $non_attachment_types, true)
|
||||||
|
&& $disposition !== 'attachment') {
|
||||||
|
// it is a message body, no attachment
|
||||||
|
continue;
|
||||||
|
} elseif (substr($part['content-type'], 0, 10) !== 'multipart/') {
|
||||||
|
// if we cannot get it by getMessageBody(), we assume it is an attachment
|
||||||
|
$disposition = 'attachment';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($disposition, $dispositions) === true) {
|
||||||
|
if ($filename == 'noname') {
|
||||||
|
$nonameIter++;
|
||||||
|
$filename = 'noname'.$nonameIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
$headersAttachments = $this->getPart('headers', $part);
|
||||||
|
$contentidAttachments = $this->getPart('content-id', $part);
|
||||||
|
|
||||||
|
$mimePartStr = $this->getPartComplete($part);
|
||||||
|
|
||||||
|
$attachments[] = new Attachment(
|
||||||
|
$filename,
|
||||||
|
$this->getPart('content-type', $part),
|
||||||
|
$this->getAttachmentStream($part),
|
||||||
|
$disposition,
|
||||||
|
$contentidAttachments,
|
||||||
|
$headersAttachments,
|
||||||
|
$mimePartStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save attachments in a folder
|
||||||
|
*
|
||||||
|
* @param string $attach_dir directory
|
||||||
|
* @param bool $include_inline
|
||||||
|
* @param string $filenameStrategy How to generate attachment filenames
|
||||||
|
*
|
||||||
|
* @return array Saved attachments paths
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function saveAttachments(
|
||||||
|
$attach_dir,
|
||||||
|
$include_inline = true,
|
||||||
|
$filenameStrategy = self::ATTACHMENT_DUPLICATE_SUFFIX
|
||||||
|
) {
|
||||||
|
$attachments = $this->getAttachments($include_inline);
|
||||||
|
if (empty($attachments)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir($attach_dir)) {
|
||||||
|
mkdir($attach_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachments_paths = [];
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
// Determine filename
|
||||||
|
switch ($filenameStrategy) {
|
||||||
|
case self::ATTACHMENT_RANDOM_FILENAME:
|
||||||
|
$attachment_path = tempnam($attach_dir, '');
|
||||||
|
break;
|
||||||
|
case self::ATTACHMENT_DUPLICATE_THROW:
|
||||||
|
case self::ATTACHMENT_DUPLICATE_SUFFIX:
|
||||||
|
$attachment_path = $attach_dir . $attachment->getFilename();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception('Invalid filename strategy argument provided.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle duplicate filename
|
||||||
|
if (file_exists($attachment_path)) {
|
||||||
|
switch ($filenameStrategy) {
|
||||||
|
case self::ATTACHMENT_DUPLICATE_THROW:
|
||||||
|
throw new Exception('Could not create file for attachment: duplicate filename.');
|
||||||
|
case self::ATTACHMENT_DUPLICATE_SUFFIX:
|
||||||
|
$attachment_path = tempnam($attach_dir, $attachment->getFilename());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var resource $fp */
|
||||||
|
if ($fp = fopen($attachment_path, 'w')) {
|
||||||
|
while ($bytes = $attachment->read()) {
|
||||||
|
fwrite($fp, $bytes);
|
||||||
|
}
|
||||||
|
fclose($fp);
|
||||||
|
$attachments_paths[] = realpath($attachment_path);
|
||||||
|
} else {
|
||||||
|
throw new Exception('Could not write attachments. Your directory may be unwritable by PHP.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attachments_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the attachment Body and save temporary file resource
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return resource Mime Body Part
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function getAttachmentStream(&$part)
|
||||||
|
{
|
||||||
|
/** @var resource $temp_fp */
|
||||||
|
$temp_fp = tmpfile();
|
||||||
|
|
||||||
|
$headers = $this->getPart('headers', $part);
|
||||||
|
$encodingType = array_key_exists('content-transfer-encoding', $headers) ?
|
||||||
|
$headers['content-transfer-encoding'] : '';
|
||||||
|
|
||||||
|
if ($temp_fp) {
|
||||||
|
if ($this->stream) {
|
||||||
|
$start = $part['starting-pos-body'];
|
||||||
|
$end = $part['ending-pos-body'];
|
||||||
|
fseek($this->stream, $start, SEEK_SET);
|
||||||
|
$len = $end - $start;
|
||||||
|
$written = 0;
|
||||||
|
while ($written < $len) {
|
||||||
|
$write = $len;
|
||||||
|
$part = fread($this->stream, $write);
|
||||||
|
fwrite($temp_fp, $this->decodeContentTransfer($part, $encodingType));
|
||||||
|
$written += $write;
|
||||||
|
}
|
||||||
|
} elseif ($this->data) {
|
||||||
|
$attachment = $this->decodeContentTransfer($this->getPartBodyFromText($part), $encodingType);
|
||||||
|
fwrite($temp_fp, $attachment, strlen($attachment));
|
||||||
|
}
|
||||||
|
fseek($temp_fp, 0, SEEK_SET);
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $temp_fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the string from Content-Transfer-Encoding
|
||||||
|
*
|
||||||
|
* @param string $encodedString The string in its original encoded state
|
||||||
|
* @param string $encodingType The encoding type from the Content-Transfer-Encoding header of the part.
|
||||||
|
*
|
||||||
|
* @return string The decoded string
|
||||||
|
*/
|
||||||
|
protected function decodeContentTransfer($encodedString, $encodingType)
|
||||||
|
{
|
||||||
|
$encodingType = strtolower($encodingType);
|
||||||
|
if ($encodingType == 'base64') {
|
||||||
|
return base64_decode($encodedString);
|
||||||
|
} elseif ($encodingType == 'quoted-printable') {
|
||||||
|
return quoted_printable_decode($encodedString);
|
||||||
|
} else {
|
||||||
|
return $encodedString; //8bit, 7bit, binary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $input can be a string or array
|
||||||
|
*
|
||||||
|
* @param string|array $input
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function decodeHeader($input)
|
||||||
|
{
|
||||||
|
//Sometimes we have 2 label From so we take only the first
|
||||||
|
if (is_array($input)) {
|
||||||
|
return $this->decodeSingleHeader($input[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->decodeSingleHeader($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a single header (= string)
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function decodeSingleHeader($input)
|
||||||
|
{
|
||||||
|
// For each encoded-word...
|
||||||
|
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)((\s+)=\?)?/i', $input, $matches)) {
|
||||||
|
$encoded = $matches[1];
|
||||||
|
$charset = $matches[2];
|
||||||
|
$encoding = $matches[3];
|
||||||
|
$text = $matches[4];
|
||||||
|
$space = isset($matches[6]) ? $matches[6] : '';
|
||||||
|
|
||||||
|
switch (strtolower($encoding)) {
|
||||||
|
case 'b':
|
||||||
|
$text = $this->decodeContentTransfer($text, 'base64');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
$text = str_replace('_', ' ', $text);
|
||||||
|
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
|
||||||
|
foreach ($matches[1] as $value) {
|
||||||
|
$text = str_replace('='.$value, chr(hexdec($value)), $text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$text = $this->charset->decodeCharset($text, $this->charset->getCharsetAlias($charset));
|
||||||
|
$input = str_replace($encoded . $space, $text, $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the charset of the MIME part
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
protected function getPartCharset($part)
|
||||||
|
{
|
||||||
|
if (isset($part['charset'])) {
|
||||||
|
return $this->charset->getCharsetAlias($part['charset']);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a specified MIME part
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param array $parts
|
||||||
|
*
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
protected function getPart($type, $parts)
|
||||||
|
{
|
||||||
|
return (isset($parts[$type])) ? $parts[$type] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Body of a MIME part
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getPartBody(&$part)
|
||||||
|
{
|
||||||
|
$body = '';
|
||||||
|
if ($this->stream) {
|
||||||
|
$body = $this->getPartBodyFromFile($part);
|
||||||
|
} elseif ($this->data) {
|
||||||
|
$body = $this->getPartBodyFromText($part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Body from a MIME part from file
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string Mime Body Part
|
||||||
|
*/
|
||||||
|
protected function getPartBodyFromFile(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos-body'];
|
||||||
|
$end = $part['ending-pos-body'];
|
||||||
|
$body = '';
|
||||||
|
if ($end - $start > 0) {
|
||||||
|
fseek($this->stream, $start, SEEK_SET);
|
||||||
|
$body = fread($this->stream, $end - $start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the Body from a MIME part from text
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string Mime Body Part
|
||||||
|
*/
|
||||||
|
protected function getPartBodyFromText(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos-body'];
|
||||||
|
$end = $part['ending-pos-body'];
|
||||||
|
|
||||||
|
return substr($this->data, $start, $end - $start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the content of a MIME part
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getPartComplete(&$part)
|
||||||
|
{
|
||||||
|
$body = '';
|
||||||
|
if ($this->stream) {
|
||||||
|
$body = $this->getPartFromFile($part);
|
||||||
|
} elseif ($this->data) {
|
||||||
|
$body = $this->getPartFromText($part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the content from a MIME part from file
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string Mime Content
|
||||||
|
*/
|
||||||
|
protected function getPartFromFile(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos'];
|
||||||
|
$end = $part['ending-pos'];
|
||||||
|
$body = '';
|
||||||
|
if ($end - $start > 0) {
|
||||||
|
fseek($this->stream, $start, SEEK_SET);
|
||||||
|
$body = fread($this->stream, $end - $start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the content from a MIME part from text
|
||||||
|
*
|
||||||
|
* @param array $part
|
||||||
|
*
|
||||||
|
* @return string Mime Content
|
||||||
|
*/
|
||||||
|
protected function getPartFromText(&$part)
|
||||||
|
{
|
||||||
|
$start = $part['starting-pos'];
|
||||||
|
$end = $part['ending-pos'];
|
||||||
|
|
||||||
|
return substr($this->data, $start, $end - $start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the resource
|
||||||
|
*
|
||||||
|
* @return resource resource
|
||||||
|
*/
|
||||||
|
public function getResource()
|
||||||
|
{
|
||||||
|
return $this->resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the file pointer to email
|
||||||
|
*
|
||||||
|
* @return resource stream
|
||||||
|
*/
|
||||||
|
public function getStream()
|
||||||
|
{
|
||||||
|
return $this->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the text of an email
|
||||||
|
*
|
||||||
|
* @return string data
|
||||||
|
*/
|
||||||
|
public function getData()
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the parts of an email
|
||||||
|
*
|
||||||
|
* @return array parts
|
||||||
|
*/
|
||||||
|
public function getParts()
|
||||||
|
{
|
||||||
|
return $this->parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the charset manager object
|
||||||
|
*
|
||||||
|
* @return CharsetManager charset
|
||||||
|
*/
|
||||||
|
public function getCharset()
|
||||||
|
{
|
||||||
|
return $this->charset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,6 @@ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
|
||||||
}
|
}
|
||||||
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
||||||
|
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
|
||||||
|
|
||||||
header_remove("X-Powered-By");
|
header_remove("X-Powered-By");
|
||||||
|
|
||||||
// Yubi OTP API
|
// Yubi OTP API
|
||||||
|
@ -49,17 +47,33 @@ try {
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
?>
|
?>
|
||||||
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
|
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection to database failed.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
|
||||||
<?php
|
<?php
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
||||||
|
|
||||||
// Set language
|
// Set language
|
||||||
if (!isset($_SESSION['mailcow_locale'])) {
|
if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
|
||||||
$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
|
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||||
|
$header_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
|
||||||
|
foreach ($AVAILABLE_LANGUAGES as $available_lang) {
|
||||||
|
if ($header_lang == $available_lang) {
|
||||||
|
$_SESSION['mailcow_locale'] = strtolower(trim($header_lang));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($_COOKIE['mailcow_locale'])) {
|
||||||
|
$_SESSION['mailcow_locale'] = $_COOKIE['mailcow_locale'];
|
||||||
}
|
}
|
||||||
if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) {
|
if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) {
|
||||||
$_SESSION['mailcow_locale'] = $_GET['lang'];
|
$_SESSION['mailcow_locale'] = $_GET['lang'];
|
||||||
|
setcookie("mailcow_locale", $_GET['lang'], time()+30758400); // one year
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.php';
|
||||||
|
@ -67,7 +81,9 @@ include $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php';
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.bcc.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php';
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantaine.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
|
||||||
|
@ -80,3 +96,4 @@ init_db_schema();
|
||||||
if (isset($_SESSION['mailcow_cc_role'])) {
|
if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
set_acl();
|
set_acl();
|
||||||
}
|
}
|
||||||
|
$UI_TEXTS = customize('get', 'ui_texts');
|
||||||
|
|
|
@ -26,11 +26,29 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
||||||
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API
|
||||||
|
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
||||||
|
$stmt = $pdo->prepare("SELECT `username`, `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':api_key' => preg_replace('/[^A-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
|
||||||
|
));
|
||||||
|
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (!empty($api_return['username'])) {
|
||||||
|
if (in_array($_SERVER['REMOTE_ADDR'], explode(',', $api_return['allow_from']))) {
|
||||||
|
$_SESSION['mailcow_cc_username'] = $api_return['username'];
|
||||||
|
$_SESSION['mailcow_cc_role'] = 'admin';
|
||||||
|
$_SESSION['mailcow_cc_api'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Update session cookie
|
// Update session cookie
|
||||||
// setcookie(session_name() ,session_id(), time() + $SESSION_LIFETIME);
|
// setcookie(session_name() ,session_id(), time() + $SESSION_LIFETIME);
|
||||||
|
|
||||||
// Check session
|
// Check session
|
||||||
function session_check() {
|
function session_check() {
|
||||||
|
if ($_SESSION['mailcow_cc_api'] === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||||
|
// TODO: Move file upload to API?
|
||||||
if (isset($_POST["submit_main_logo"])) {
|
if (isset($_POST["submit_main_logo"])) {
|
||||||
if ($_FILES['main_logo']['error'] == 0) {
|
if ($_FILES['main_logo']['error'] == 0) {
|
||||||
customize('add', 'main_logo', $_FILES);
|
customize('add', 'main_logo', $_FILES);
|
||||||
|
@ -72,5 +73,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
|
||||||
if (isset($_POST["reset_main_logo"])) {
|
if (isset($_POST["reset_main_logo"])) {
|
||||||
customize('delete', 'main_logo');
|
customize('delete', 'main_logo');
|
||||||
}
|
}
|
||||||
|
// API cannot be controlled by API
|
||||||
|
if (isset($_POST["admin_api"])) {
|
||||||
|
admin_api('edit', $_POST);
|
||||||
|
}
|
||||||
|
if (isset($_POST["admin_api_regen_key"])) {
|
||||||
|
admin_api('regen_key', $_POST);
|
||||||
|
}
|
||||||
|
// Not available via API
|
||||||
|
if (isset($_POST["rspamd_ui"])) {
|
||||||
|
rspamd_ui('edit', $_POST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -68,8 +68,12 @@ $autodiscover_config = array(
|
||||||
);
|
);
|
||||||
unset($https_port);
|
unset($https_port);
|
||||||
|
|
||||||
|
// If false, we will use DEFAULT_LANG
|
||||||
|
// Uses HTTP_ACCEPT_LANGUAGE header
|
||||||
|
$DETECT_LANGUAGE = true;
|
||||||
|
|
||||||
// Change default language, "de", "en", "es", "nl", "pt", "ru"
|
// Change default language, "de", "en", "es", "nl", "pt", "ru"
|
||||||
$DEFAULT_LANG = 'en';
|
$DEFAULT_LANG = 'de';
|
||||||
|
|
||||||
// Available languages
|
// Available languages
|
||||||
$AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'nl', 'pl', 'pt', 'ru', 'it');
|
$AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'nl', 'pl', 'pt', 'ru', 'it');
|
||||||
|
@ -92,13 +96,7 @@ $MAILCOW_APPS = array(
|
||||||
array(
|
array(
|
||||||
'name' => 'SOGo',
|
'name' => 'SOGo',
|
||||||
'link' => '/SOGo/',
|
'link' => '/SOGo/',
|
||||||
'description' => 'SOGo is a web-based client for email, address book and calendar.'
|
)
|
||||||
),
|
|
||||||
// array(
|
|
||||||
// 'name' => 'Roundcube',
|
|
||||||
// 'link' => '/rc/',
|
|
||||||
// 'description' => 'Roundcube is a web-based email client.',
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rows until pagination begins
|
// Rows until pagination begins
|
||||||
|
@ -118,3 +116,7 @@ $OTP_LABEL = "mailcow UI";
|
||||||
|
|
||||||
// Default "to" address in relay test tool
|
// Default "to" address in relay test tool
|
||||||
$RELAY_TO = "null@hosted.mailcow.de";
|
$RELAY_TO = "null@hosted.mailcow.de";
|
||||||
|
|
||||||
|
// Quarantaine data age in days to keep
|
||||||
|
$QUARANTAINE_AGE = 10;
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
||||||
header('Location: /user.php');
|
header('Location: /user.php');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once 'inc/header.inc.php';
|
require_once 'inc/header.inc.php';
|
||||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -24,7 +24,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
<div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div>
|
<div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div>
|
<div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div>
|
||||||
<legend>mailcow UI</legend>
|
<legend><?=$UI_TEXTS['main_name'];?></legend>
|
||||||
<form method="post" autofill="off">
|
<form method="post" autofill="off">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="sr-only" for="login_user"><?= $lang['login']['username']; ?></label>
|
<label class="sr-only" for="login_user"><?= $lang['login']['username']; ?></label>
|
||||||
|
@ -65,7 +65,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<legend>mailcow Apps</legend>
|
<legend><?=$UI_TEXTS['apps_name'];?></legend>
|
||||||
<?php
|
<?php
|
||||||
foreach ($MAILCOW_APPS as $app):
|
foreach ($MAILCOW_APPS as $app):
|
||||||
?>
|
?>
|
||||||
|
@ -73,12 +73,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
<?php
|
<?php
|
||||||
endforeach;
|
endforeach;
|
||||||
$app_links = customize('get', 'app_links');
|
$app_links = customize('get', 'app_links');
|
||||||
foreach ($app_links as $row) {
|
if (!empty($app_links)) {
|
||||||
foreach ($row as $key => $val):
|
foreach ($app_links as $row) {
|
||||||
?>
|
foreach ($row as $key => $val):
|
||||||
<a href="<?= htmlspecialchars($val); ?>" role="button" class="btn btn-lg btn-default"><?= htmlspecialchars($key); ?></a>
|
?>
|
||||||
<?php
|
<a href="<?= htmlspecialchars($val); ?>" role="button" class="btn btn-lg btn-default"><?= htmlspecialchars($key); ?></a>
|
||||||
endforeach;
|
<?php
|
||||||
|
endforeach;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,10 +93,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
</div>
|
</div>
|
||||||
<div id="collapse1" class="panel-collapse collapse">
|
<div id="collapse1" class="panel-collapse collapse">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p><span style="border-bottom: 1px dotted #999;">mailcow UI</span></p>
|
<?php if ($UI_TEXTS['help_text']): ?>
|
||||||
|
<p><?=$UI_TEXTS['help_text'];?></p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p><span style="border-bottom: 1px dotted #999;"><?=$UI_TEXTS['main_name'];?></span></p>
|
||||||
<p><?= $lang['start']['mailcow_panel_detail']; ?></p>
|
<p><?= $lang['start']['mailcow_panel_detail']; ?></p>
|
||||||
<p><span style="border-bottom: 1px dotted #999;">mailcow Apps</span></p>
|
<p><span style="border-bottom: 1px dotted #999;"><?=$UI_TEXTS['apps_name'];?></span></p>
|
||||||
<p><?= $lang['start']['mailcow_apps_detail']; ?></p>
|
<p><?= $lang['start']['mailcow_apps_detail']; ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,199 +5,10 @@ jQuery(function($){
|
||||||
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
||||||
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
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 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]}
|
||||||
$("#refresh_postfix_log").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_postfix_logs();
|
|
||||||
});
|
|
||||||
$("#refresh_autodiscover_log").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_autodiscover_logs();
|
|
||||||
});
|
|
||||||
$("#refresh_dovecot_log").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_dovecot_logs();
|
|
||||||
});
|
|
||||||
$("#refresh_sogo_log").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_sogo_logs();
|
|
||||||
});
|
|
||||||
$("#refresh_fail2ban_log").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_fail2ban_logs();
|
|
||||||
});
|
|
||||||
$("#refresh_rspamd_history").on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
draw_rspamd_history();
|
|
||||||
});
|
|
||||||
$("#import_dkim_legend").on('click', function(e) {
|
$("#import_dkim_legend").on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#import_dkim_arrow').toggleClass("animation");
|
$('#import_dkim_arrow').toggleClass("animation");
|
||||||
});
|
});
|
||||||
function draw_autodiscover_logs() {
|
|
||||||
ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
|
|
||||||
{"name":"user","title":"Username","style":{"min-width":"200px"}},
|
|
||||||
{"name":"service","title":"Service"},
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/autodiscover/100',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw autodiscover log table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'autodiscover_log');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function draw_postfix_logs() {
|
|
||||||
ft_postfix_logs = FooTable.init('#postfix_log', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
|
||||||
{"name":"message","title":lang.message},
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/postfix',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw postfix log table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'general_syslog');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {
|
|
||||||
"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function draw_fail2ban_logs() {
|
|
||||||
ft_fail2ban_logs = FooTable.init('#fail2ban_log', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
|
||||||
{"name":"message","title":lang.message},
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/fail2ban',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw fail2ban log table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'general_syslog');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {
|
|
||||||
"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function draw_sogo_logs() {
|
|
||||||
ft_sogo_logs = FooTable.init('#sogo_log', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
|
||||||
{"name":"message","title":lang.message},
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/sogo',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw sogo log table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'general_syslog');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {
|
|
||||||
"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function draw_dovecot_logs() {
|
|
||||||
ft_dovecot_logs = FooTable.init('#dovecot_log', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
|
||||||
{"name":"message","title":lang.message},
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/dovecot',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw dovecot log table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'general_syslog');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {
|
|
||||||
"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function draw_domain_admins() {
|
function draw_domain_admins() {
|
||||||
ft_domainadmins = FooTable.init('#domainadminstable', {
|
ft_domainadmins = FooTable.init('#domainadminstable', {
|
||||||
"columns": [
|
"columns": [
|
||||||
|
@ -278,108 +89,9 @@ jQuery(function($){
|
||||||
"sorting": {"enabled": true}
|
"sorting": {"enabled": true}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function draw_rspamd_history() {
|
|
||||||
ft_rspamd_history = FooTable.init('#rspamd_history', {
|
|
||||||
"columns": [
|
|
||||||
{"name":"unix_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
|
||||||
{"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
|
|
||||||
{"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
|
|
||||||
{"name": "rcpt_mime","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
|
|
||||||
{"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
|
|
||||||
{"name": "action","title": "Action","style": {"minwidth": 82}},
|
|
||||||
{"name": "score","title": "Score","style": {"maxWidth": 110},},
|
|
||||||
{"name": "symbols","title": "Symbols","breakpoints": "all",},
|
|
||||||
{"name": "size","title": "Msg size","breakpoints": "all","style": {"minwidth": 50},"formatter": function(value){return humanFileSize(value);}},
|
|
||||||
{"name": "scan_time","title": "Scan time","breakpoints": "all","style": {"maxWidth": 72},},
|
|
||||||
{"name": "message-id","title": "ID","breakpoints": "all","style": {"minWidth": 130,"overflow": "hidden","textOverflow": "ellipsis","wordBreak": "break-all","whiteSpace": "normal"}},
|
|
||||||
{"name": "user","title": "Authenticated user","breakpoints": "xs sm md","style": {"minWidth": 100}}
|
|
||||||
],
|
|
||||||
"rows": $.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: '/api/v1/get/logs/rspamd-history',
|
|
||||||
jsonp: false,
|
|
||||||
error: function () {
|
|
||||||
console.log('Cannot draw rspamd history table');
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
return process_table_data(data, 'rspamd_history');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"empty": lang.empty,
|
|
||||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
|
||||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
|
||||||
"sorting": {"enabled": true},
|
|
||||||
"on": {
|
|
||||||
"ready.ft.table": function(e, ft){
|
|
||||||
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
$(heading).children('.log-lines').text(function(){
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
return ft_paging.totalRows;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function process_table_data(data, table) {
|
function process_table_data(data, table) {
|
||||||
if (table == 'rspamd_history') {
|
if (table == 'relayhoststable') {
|
||||||
$.each(data, function (i, item) {
|
|
||||||
item.rcpt_mime = item.rcpt_mime.join(",​");
|
|
||||||
Object.keys(item.symbols).map(function(key) {
|
|
||||||
var sym = item.symbols[key];
|
|
||||||
if (sym.score <= 0) {
|
|
||||||
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
|
|
||||||
}
|
|
||||||
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
|
||||||
if (sym.options) {
|
|
||||||
str += ' [' + sym.options.join(",") + "]";
|
|
||||||
}
|
|
||||||
item.symbols[key].str = str;
|
|
||||||
});
|
|
||||||
item.symbols = Object.keys(item.symbols).
|
|
||||||
map(function(key) {
|
|
||||||
return item.symbols[key];
|
|
||||||
}).sort(function(e1, e2) {
|
|
||||||
return Math.abs(e1.score) < Math.abs(e2.score);
|
|
||||||
}).map(function(e) {
|
|
||||||
return e.str;
|
|
||||||
}).join("<br>\n");
|
|
||||||
var scan_time = item.time_real.toFixed(3) + ' / ' + item.time_virtual.toFixed(3);
|
|
||||||
item.scan_time = {
|
|
||||||
"options": {
|
|
||||||
"sortValue": item.time_real
|
|
||||||
},
|
|
||||||
"value": scan_time
|
|
||||||
};
|
|
||||||
if (item.action === 'clean' || item.action === 'no action') {
|
|
||||||
item.action = "<div class='label label-success'>" + item.action + "</div>";
|
|
||||||
} else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
|
|
||||||
item.action = "<div class='label label-warning'>" + item.action + "</div>";
|
|
||||||
} else if (item.action === 'spam' || item.action === 'reject') {
|
|
||||||
item.action = "<div class='label label-danger'>" + item.action + "</div>";
|
|
||||||
} else {
|
|
||||||
item.action = "<div class='label label-info'>" + item.action + "</div>";
|
|
||||||
}
|
|
||||||
var score_content;
|
|
||||||
if (item.score < item.required_score) {
|
|
||||||
score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
|
|
||||||
} else {
|
|
||||||
score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
|
|
||||||
}
|
|
||||||
item.score = {
|
|
||||||
"options": {
|
|
||||||
"sortValue": item.score
|
|
||||||
},
|
|
||||||
"value": score_content
|
|
||||||
};
|
|
||||||
if (item.user == null) {
|
|
||||||
item.user = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (table == 'relayhoststable') {
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="#" data-toggle="modal" id="miau" data-target="#testRelayhostModal" data-relayhost-id="' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-stats"></span> Test</a>' +
|
'<a href="#" data-toggle="modal" id="miau" data-target="#testRelayhostModal" data-relayhost-id="' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-stats"></span> Test</a>' +
|
||||||
|
@ -409,48 +121,13 @@ jQuery(function($){
|
||||||
'<a href="#" id="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
'<a href="#" id="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
});
|
});
|
||||||
} else if (table == 'autodiscover_log') {
|
|
||||||
$.each(data, function (i, item) {
|
|
||||||
item.ua = '<span style="font-size:small">' + item.ua + '</span>';
|
|
||||||
if (item.service == "activesync") {
|
|
||||||
item.service = '<span class="label label-info">ActiveSync</span>';
|
|
||||||
}
|
|
||||||
else if (item.service == "imap") {
|
|
||||||
item.service = '<span class="label label-success">IMAP, SMTP, Cal-/CardDAV</span>';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item.service = '<span class="label label-danger">' + item.service + '</span>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (table == 'general_syslog') {
|
|
||||||
$.each(data, function (i, item) {
|
|
||||||
item.message = escapeHtml(item.message);
|
|
||||||
var danger_class = ["emerg", "alert", "crit", "err"];
|
|
||||||
var warning_class = ["warning", "warn"];
|
|
||||||
var info_class = ["notice", "info", "debug"];
|
|
||||||
if (jQuery.inArray(item.priority, danger_class) !== -1) {
|
|
||||||
item.priority = '<span class="label label-danger">' + item.priority + '</span>';
|
|
||||||
}
|
|
||||||
else if (jQuery.inArray(item.priority, warning_class) !== -1) {
|
|
||||||
item.priority = '<span class="label label-warning">' + item.priority + '</span>';
|
|
||||||
}
|
|
||||||
else if (jQuery.inArray(item.priority, info_class) !== -1) {
|
|
||||||
item.priority = '<span class="label label-info">' + item.priority + '</span>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
};
|
};
|
||||||
// Initial table drawings
|
// Initial table drawings
|
||||||
draw_postfix_logs();
|
|
||||||
draw_autodiscover_logs();
|
|
||||||
draw_dovecot_logs();
|
|
||||||
draw_sogo_logs();
|
|
||||||
draw_fail2ban_logs();
|
|
||||||
draw_domain_admins();
|
draw_domain_admins();
|
||||||
draw_fwd_hosts();
|
draw_fwd_hosts();
|
||||||
draw_relayhosts();
|
draw_relayhosts();
|
||||||
draw_rspamd_history();
|
|
||||||
// Relayhost
|
// Relayhost
|
||||||
$('#testRelayhostModal').on('show.bs.modal', function (e) {
|
$('#testRelayhostModal').on('show.bs.modal', function (e) {
|
||||||
$('#test_relayhost_result').text("-");
|
$('#test_relayhost_result').text("-");
|
||||||
|
@ -485,31 +162,6 @@ jQuery(function($){
|
||||||
$('#priv_key_pre').text(decoded_key);
|
$('#priv_key_pre').text(decoded_key);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$('.add_log_lines').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var log_table= $(this).data("table")
|
|
||||||
var new_nrows = ($(this).data("nrows") - 1)
|
|
||||||
var post_process = $(this).data("post-process")
|
|
||||||
var log_url = $(this).data("log-url")
|
|
||||||
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
|
||||||
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ft = FooTable.get($('#' + log_table))) {
|
|
||||||
var heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
|
||||||
var ft_paging = ft.use(FooTable.Paging)
|
|
||||||
var load_rows = ft_paging.totalRows + '-' + (ft_paging.totalRows + new_nrows)
|
|
||||||
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
|
|
||||||
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
|
|
||||||
var rows = process_table_data(data, post_process);
|
|
||||||
var rows_now = (ft_paging.totalRows + data.length);
|
|
||||||
$(heading).children('.log-lines').text(rows_now)
|
|
||||||
mailcow_alert_box(data.length + lang.additional_rows, "success");
|
|
||||||
ft.rows.load(rows, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// App links
|
// App links
|
||||||
function add_table_row(table_id) {
|
function add_table_row(table_id) {
|
||||||
var row = $('<tr />');
|
var row = $('<tr />');
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
function is_active(elem) {
|
||||||
|
if ($(elem).data('submitted') == '1') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$(elem).text(loading_text);
|
||||||
|
$(elem).attr('data-submitted', '1');
|
||||||
|
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
|
||||||
|
$(document).on("keydown", disableF5);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
$.fn.serializeObject = function() {
|
$.fn.serializeObject = function() {
|
||||||
var o = {};
|
var o = {};
|
||||||
var a = this.serializeArray();
|
var a = this.serializeArray();
|
||||||
|
@ -80,12 +91,12 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($(this).attr("max")) {
|
if ($(this).attr("max")) {
|
||||||
if ($(this).val() > $(this).attr("max")) {
|
if (Number($(this).val()) > Number($(this).attr("max"))) {
|
||||||
invalid = true;
|
invalid = true;
|
||||||
$(this).addClass('inputMissingAttr');
|
$(this).addClass('inputMissingAttr');
|
||||||
} else {
|
} else {
|
||||||
if ($(this).attr("min")) {
|
if ($(this).attr("min")) {
|
||||||
if ($(this).val() < $(this).attr("min")) {
|
if (Number($(this).val()) < Number($(this).attr("min"))) {
|
||||||
invalid = true;
|
invalid = true;
|
||||||
$(this).addClass('inputMissingAttr');
|
$(this).addClass('inputMissingAttr');
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,6 +113,7 @@ $(document).ready(function() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// alert(JSON.stringify(api_attr));
|
||||||
// If clicked element #edit_selected has data-item attribute, it is added to "items"
|
// If clicked element #edit_selected has data-item attribute, it is added to "items"
|
||||||
if (typeof $(this).data('item') !== 'undefined') {
|
if (typeof $(this).data('item') !== 'undefined') {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
@ -114,6 +126,7 @@ $(document).ready(function() {
|
||||||
api_items = multi_data[id];
|
api_items = multi_data[id];
|
||||||
// alert(JSON.stringify(api_attr));
|
// alert(JSON.stringify(api_attr));
|
||||||
if (Object.keys(api_items).length !== 0) {
|
if (Object.keys(api_items).length !== 0) {
|
||||||
|
if (is_active($(this))) { return false; }
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
|
@ -126,7 +139,9 @@ $(document).ready(function() {
|
||||||
jsonp: false,
|
jsonp: false,
|
||||||
complete: function(data) {
|
complete: function(data) {
|
||||||
var response = (data.responseText);
|
var response = (data.responseText);
|
||||||
response_obj = JSON.parse(response);
|
if (typeof response !== 'undefined' && response.length !== 0) {
|
||||||
|
response_obj = JSON.parse(response);
|
||||||
|
}
|
||||||
if (api_reload_window === true) {
|
if (api_reload_window === true) {
|
||||||
window.location = window.location.href.split("#")[0];
|
window.location = window.location.href.split("#")[0];
|
||||||
}
|
}
|
||||||
|
@ -141,6 +156,11 @@ $(document).ready(function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
var api_url = $(this).data('api-url');
|
var api_url = $(this).data('api-url');
|
||||||
var api_attr = $(this).data('api-attr');
|
var api_attr = $(this).data('api-attr');
|
||||||
|
if (typeof $(this).data('api-reload-window') !== 'undefined') {
|
||||||
|
api_reload_window = $(this).data('api-reload-window');
|
||||||
|
} else {
|
||||||
|
api_reload_window = true;
|
||||||
|
}
|
||||||
// If clicked button is in a form with the same data-id as the button,
|
// If clicked button is in a form with the same data-id as the button,
|
||||||
// we merge all input fields by {"name":"value"} into api-attr
|
// we merge all input fields by {"name":"value"} into api-attr
|
||||||
if ($(this).closest("form").data('id') == id) {
|
if ($(this).closest("form").data('id') == id) {
|
||||||
|
@ -155,12 +175,13 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($(this).attr("max")) {
|
if ($(this).attr("max")) {
|
||||||
if ($(this).val() > $(this).attr("max")) {
|
if (Number($(this).val()) > Number($(this).attr("max"))) {
|
||||||
|
alert($(this).attr("max"))
|
||||||
invalid = true;
|
invalid = true;
|
||||||
$(this).addClass('inputMissingAttr');
|
$(this).addClass('inputMissingAttr');
|
||||||
} else {
|
} else {
|
||||||
if ($(this).attr("min")) {
|
if ($(this).attr("min")) {
|
||||||
if ($(this).val() < $(this).attr("min")) {
|
if (Number($(this).val()) < Number($(this).attr("min"))) {
|
||||||
invalid = true;
|
invalid = true;
|
||||||
$(this).addClass('inputMissingAttr');
|
$(this).addClass('inputMissingAttr');
|
||||||
} else {
|
} else {
|
||||||
|
@ -177,6 +198,7 @@ $(document).ready(function() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is_active($(this))) { return false; }
|
||||||
// alert(JSON.stringify(api_attr));
|
// alert(JSON.stringify(api_attr));
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
@ -188,10 +210,20 @@ $(document).ready(function() {
|
||||||
url: '/api/v1/' + api_url,
|
url: '/api/v1/' + api_url,
|
||||||
jsonp: false,
|
jsonp: false,
|
||||||
complete: function(data) {
|
complete: function(data) {
|
||||||
// var reponse = (JSON.parse(data.responseText));
|
var response = (data.responseText);
|
||||||
// console.log(reponse.type);
|
if (typeof response !== 'undefined' && response.length !== 0) {
|
||||||
// console.log(reponse.msg);
|
response_obj = JSON.parse(response);
|
||||||
window.location = window.location.href.split("#")[0];
|
if (response_obj.type == 'success') {
|
||||||
|
$('form').formcache('clear');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var add_modal = $('.modal.in').attr('id');
|
||||||
|
localStorage.setItem("add_modal", add_modal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (api_reload_window === true) {
|
||||||
|
window.location = window.location.href.split("#")[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,503 @@
|
||||||
|
jQuery(function($){
|
||||||
|
// 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]}
|
||||||
|
$("#refresh_postfix_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_postfix_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_autodiscover_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_autodiscover_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_dovecot_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_dovecot_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_sogo_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_sogo_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_watchdog_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_watchdog_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_api_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_api_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_acme_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_acme_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_fail2ban_log").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_fail2ban_logs();
|
||||||
|
});
|
||||||
|
$("#refresh_rspamd_history").on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
draw_rspamd_history();
|
||||||
|
});
|
||||||
|
function draw_autodiscover_logs() {
|
||||||
|
ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
|
||||||
|
{"name":"user","title":"Username","style":{"min-width":"200px"}},
|
||||||
|
{"name":"service","title":"Service"},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/autodiscover/100',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw autodiscover log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'autodiscover_log');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_postfix_logs() {
|
||||||
|
ft_postfix_logs = FooTable.init('#postfix_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/postfix',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw postfix log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'general_syslog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_watchdog_logs() {
|
||||||
|
ft_watchdog_logs = FooTable.init('#watchdog_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"service","title":"Service"},
|
||||||
|
{"name":"trend","title":"Trend"},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/watchdog',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw watchdog log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'watchdog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_api_logs() {
|
||||||
|
ft_api_logs = FooTable.init('#api_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"uri","title":"URI","style":{"width":"310px"}},
|
||||||
|
{"name":"method","title":"Method","style":{"width":"80px"}},
|
||||||
|
{"name":"remote","title":"IP","style":{"width":"80px"}},
|
||||||
|
{"name":"data","title":"Data","style":{"word-break":"break-all"}},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/api',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw api log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'apilog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_acme_logs() {
|
||||||
|
ft_acme_logs = FooTable.init('#acme_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/acme',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw acme log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'general_syslog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_fail2ban_logs() {
|
||||||
|
ft_fail2ban_logs = FooTable.init('#fail2ban_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/fail2ban',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw fail2ban log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'general_syslog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_sogo_logs() {
|
||||||
|
ft_sogo_logs = FooTable.init('#sogo_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/sogo',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw sogo log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'general_syslog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_dovecot_logs() {
|
||||||
|
ft_dovecot_logs = FooTable.init('#dovecot_log', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
|
||||||
|
{"name":"message","title":lang.message},
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/dovecot',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw dovecot log table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'general_syslog');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_rspamd_history() {
|
||||||
|
ft_rspamd_history = FooTable.init('#rspamd_history', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"unix_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
|
||||||
|
{"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
|
||||||
|
{"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
|
||||||
|
{"name": "rcpt_mime","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
|
||||||
|
{"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
|
||||||
|
{"name": "action","title": "Action","style": {"minwidth": 82}},
|
||||||
|
{"name": "score","title": "Score","style": {"maxWidth": 110},},
|
||||||
|
{"name": "symbols","title": "Symbols","breakpoints": "all",},
|
||||||
|
{"name": "size","title": "Msg size","breakpoints": "all","style": {"minwidth": 50},"formatter": function(value){return humanFileSize(value);}},
|
||||||
|
{"name": "scan_time","title": "Scan time","breakpoints": "all","style": {"maxWidth": 72},},
|
||||||
|
{"name": "message-id","title": "ID","breakpoints": "all","style": {"minWidth": 130,"overflow": "hidden","textOverflow": "ellipsis","wordBreak": "break-all","whiteSpace": "normal"}},
|
||||||
|
{"name": "user","title": "Authenticated user","breakpoints": "xs sm md","style": {"minWidth": 100}}
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/logs/rspamd-history',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw rspamd history table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
return process_table_data(data, 'rspamd_history');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": function(e, ft){
|
||||||
|
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
$(heading).children('.log-lines').text(function(){
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
return ft_paging.totalRows;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_table_data(data, table) {
|
||||||
|
if (table == 'rspamd_history') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.rcpt_mime = item.rcpt_mime.join(",​");
|
||||||
|
Object.keys(item.symbols).map(function(key) {
|
||||||
|
var sym = item.symbols[key];
|
||||||
|
if (sym.score <= 0) {
|
||||||
|
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
|
||||||
|
}
|
||||||
|
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
||||||
|
if (sym.options) {
|
||||||
|
str += ' [' + sym.options.join(",") + "]";
|
||||||
|
}
|
||||||
|
item.symbols[key].str = str;
|
||||||
|
});
|
||||||
|
item.symbols = Object.keys(item.symbols).
|
||||||
|
map(function(key) {
|
||||||
|
return item.symbols[key];
|
||||||
|
}).sort(function(e1, e2) {
|
||||||
|
return Math.abs(e1.score) < Math.abs(e2.score);
|
||||||
|
}).map(function(e) {
|
||||||
|
return e.str;
|
||||||
|
}).join("<br>\n");
|
||||||
|
var scan_time = item.time_real.toFixed(3) + ' / ' + item.time_virtual.toFixed(3);
|
||||||
|
item.scan_time = {
|
||||||
|
"options": {
|
||||||
|
"sortValue": item.time_real
|
||||||
|
},
|
||||||
|
"value": scan_time
|
||||||
|
};
|
||||||
|
if (item.action === 'clean' || item.action === 'no action') {
|
||||||
|
item.action = "<div class='label label-success'>" + item.action + "</div>";
|
||||||
|
} else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
|
||||||
|
item.action = "<div class='label label-warning'>" + item.action + "</div>";
|
||||||
|
} else if (item.action === 'spam' || item.action === 'reject') {
|
||||||
|
item.action = "<div class='label label-danger'>" + item.action + "</div>";
|
||||||
|
} else {
|
||||||
|
item.action = "<div class='label label-info'>" + item.action + "</div>";
|
||||||
|
}
|
||||||
|
var score_content;
|
||||||
|
if (item.score < item.required_score) {
|
||||||
|
score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
|
||||||
|
} else {
|
||||||
|
score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
|
||||||
|
}
|
||||||
|
item.score = {
|
||||||
|
"options": {
|
||||||
|
"sortValue": item.score
|
||||||
|
},
|
||||||
|
"value": score_content
|
||||||
|
};
|
||||||
|
if (item.user == null) {
|
||||||
|
item.user = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (table == 'autodiscover_log') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.ua = '<span style="font-size:small">' + item.ua + '</span>';
|
||||||
|
if (item.service == "activesync") {
|
||||||
|
item.service = '<span class="label label-info">ActiveSync</span>';
|
||||||
|
}
|
||||||
|
else if (item.service == "imap") {
|
||||||
|
item.service = '<span class="label label-success">IMAP, SMTP, Cal-/CardDAV</span>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.service = '<span class="label label-danger">' + item.service + '</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (table == 'watchdog') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
if (item.message == null) {
|
||||||
|
item.message = 'Health level: ' + item.lvl + '% (' + item.hpnow + '/' + item.hptotal + ')';
|
||||||
|
if (item.hpdiff < 0) {
|
||||||
|
item.trend = '<span class="label label-danger"><span class="glyphicon glyphicon-arrow-down"></span> ' + item.hpdiff + '</span>';
|
||||||
|
}
|
||||||
|
else if (item.hpdiff == 0) {
|
||||||
|
item.trend = '<span class="label label-info"><span class="glyphicon glyphicon-arrow-right"></span> ' + item.hpdiff + '</span>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.trend = '<span class="label label-success"><span class="glyphicon glyphicon-arrow-up"></span> ' + item.hpdiff + '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.trend = '';
|
||||||
|
item.service = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (table == 'general_syslog') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
if (item === null) { return true; }
|
||||||
|
item.message = escapeHtml(item.message);
|
||||||
|
var danger_class = ["emerg", "alert", "crit", "err"];
|
||||||
|
var warning_class = ["warning", "warn"];
|
||||||
|
var info_class = ["notice", "info", "debug"];
|
||||||
|
if (jQuery.inArray(item.priority, danger_class) !== -1) {
|
||||||
|
item.priority = '<span class="label label-danger">' + item.priority + '</span>';
|
||||||
|
} else if (jQuery.inArray(item.priority, warning_class) !== -1) {
|
||||||
|
item.priority = '<span class="label label-warning">' + item.priority + '</span>';
|
||||||
|
} else if (jQuery.inArray(item.priority, info_class) !== -1) {
|
||||||
|
item.priority = '<span class="label label-info">' + item.priority + '</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (table == 'apilog') {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
if (item === null) { return true; }
|
||||||
|
if (item.method == 'GET') {
|
||||||
|
item.method = '<span class="label label-success">' + item.method + '</span>';
|
||||||
|
} else if (item.method == 'POST') {
|
||||||
|
item.method = '<span class="label label-warning">' + item.method + '</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
};
|
||||||
|
$('.add_log_lines').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var log_table= $(this).data("table")
|
||||||
|
var new_nrows = ($(this).data("nrows") - 1)
|
||||||
|
var post_process = $(this).data("post-process")
|
||||||
|
var log_url = $(this).data("log-url")
|
||||||
|
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
||||||
|
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ft = FooTable.get($('#' + log_table))) {
|
||||||
|
var heading = ft.$el.parents('.tab-pane').find('.panel-heading')
|
||||||
|
var ft_paging = ft.use(FooTable.Paging)
|
||||||
|
var load_rows = ft_paging.totalRows + '-' + (ft_paging.totalRows + new_nrows)
|
||||||
|
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
|
||||||
|
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
|
||||||
|
var rows = process_table_data(data, post_process);
|
||||||
|
var rows_now = (ft_paging.totalRows + data.length);
|
||||||
|
$(heading).children('.log-lines').text(rows_now)
|
||||||
|
mailcow_alert_box(data.length + lang.additional_rows, "success");
|
||||||
|
ft.rows.load(rows, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Initial table drawings
|
||||||
|
draw_postfix_logs();
|
||||||
|
draw_autodiscover_logs();
|
||||||
|
draw_dovecot_logs();
|
||||||
|
draw_sogo_logs();
|
||||||
|
draw_watchdog_logs();
|
||||||
|
draw_acme_logs();
|
||||||
|
draw_api_logs();
|
||||||
|
draw_fail2ban_logs();
|
||||||
|
draw_rspamd_history();
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*!
|
||||||
|
* Form Cache v@VERSION
|
||||||
|
* https://github.com/fengyuanchen/formcache
|
||||||
|
*
|
||||||
|
* Copyright 2014 Fengyuan Chen
|
||||||
|
* Released under the MIT license
|
||||||
|
*
|
||||||
|
* Date: @DATE
|
||||||
|
*/
|
||||||
|
!function(t){"function"==typeof define&&define.amd?define("formcache",["jquery"],t):t(jQuery)}(function(t){"use strict";var e=t(window),i=window.sessionStorage,s=window.localStorage,n="undefined",o=".formcache",a=/[\.\*\+\^\$\:\!\[\]#>~]+/g,c="change"+o,h="beforeunload"+o,r=function(t){return"checkbox"===t.type||"radio"===t.type},f=function(t){return parseInt(t,10)},u=function(e,i){this.form=e,this.$form=t(e),this.defaults=t.extend({},u.DEFAULTS,t.isPlainObject(i)?i:{}),this.init()};u.prototype={constructor:u,init:function(){var e=this.defaults;e.maxAge=Math.abs(e.maxAge||e.maxage),e.autoStore=Boolean(e.autoStore||e.autostore),this.initKey(),this.initStorage(),this.caches=this.storage.caches,this.index=0,this.activeIndex=0,this.storing=null,t.isArray(e.controls)||(e.controls=[]),this.$controls=this.$form.find(e.controls.join()).not(":file"),this.addListeners(),this.outputCache()},initKey:function(){var e=this.$form,i=this.defaults.key||e.data("key");i||(t("form").each(function(e){t(this).data("key",e)}),i=e.data("key")),this.key=location.pathname+"#formcache-"+i},initStorage:function(){var e,n=this.defaults,o=this.key,a=new Date,c={date:a,maxAge:n.maxAge,caches:[]};i&&(e=i.getItem(o)),!e&&s&&(e=s.getItem(o)),e="string"==typeof e?JSON.parse(e):null,t.isPlainObject(e)?"number"==typeof e.maxAge&&(a-new Date(e.date))/1e3>e.maxAge&&(e=c):e=c,this.storage=e},addListeners:function(){this.defaults.autoStore&&(this.$controls.on(c,t.proxy(this.change,this)),e.on(h,t.proxy(this.beforeunload,this)))},removeListeners:function(){this.defaults.autoStore&&(this.$controls.off(c,this.change),e.off(h,this.beforeunload))},change:function(e){var i,s,n=e.target,o=t(n),c=o.attr("name"),h=[];c&&(i=c.replace(a,""),this.$controls.filter('[name*="'+i+'"]').each(function(){r(n)?h.push(this.checked):(s=t(this).val(),s&&h.push(s))}),h.length&&(this.update(c,h),clearTimeout(this.storing),this.storing=setTimeout(t.proxy(this.store,this),1e3)))},beforeunload:function(){this.update(),this.store()},update:function(t,e){var i=this.activeIndex||this.index,s=this.getCache(i);"string"==typeof t?s[t]=e:s=this.serialize(),this.setCache(i,s)},serialize:function(){var e={};return this.$controls.each(function(){var i,s,n=t(this),o=n.attr("name");o&&(i=e[o],i=t.isArray(i)?i:[],r(this)?i.push(this.checked):(s=n.val(),s&&i.push(s)),i.length&&(e[o]=i))}),e},getCache:function(t){return this.caches[f(t)||this.index]||{}},getCaches:function(){return this.caches},setCache:function(e,i){typeof i===n&&(i=e,e=NaN),t.isPlainObject(i)&&(e=f(e)||this.index,this.caches[e]=i,this.store())},setCaches:function(e){t.isArray(e)&&(this.caches=e,this.store())},removeCache:function(t){this.caches.splice(f(t)||this.index,1),this.store()},removeCaches:function(){this.caches=[],this.store()},outputCache:function(e){var i=this.getCache(e);t.isPlainObject(i)&&(this.activeIndex=f(e)||this.index,i=t.extend(!0,{},i),this.$controls.each(function(){var e,s,n=t(this),o=n.attr("name");o&&(e=i[o],t.isArray(e)&&e.length&&(s=e.shift(),r(this)?this.checked=s:n.val(s)))}))},store:function(){var t=this.storage,e=this.key,n=this.defaults;t.date=new Date,t.maxAge=n.maxAge,t=JSON.stringify(t),n.session&&i&&i.setItem(e,t),n.local&&s&&s.setItem(e,t)},clear:function(){var t=this.key,e=this.defaults;e.session&&i&&i.removeItem(t),e.local&&s&&s.removeItem(t)},destroy:function(){this.removeListeners(),this.$form.removeData("formcache")}},u.DEFAULTS={key:"",local:!0,session:0,autoStore:!0,maxAge:void 0,controls:["select","textarea","input"]},u.setDefaults=function(e){t.extend(u.DEFAULTS,e)},u.other=t.fn.formcache,t.fn.formcache=function(e){var i,s=[].slice.call(arguments,1);return this.each(function(){var n,o=t(this),a=o.data("formcache");a||o.data("formcache",a=new u(this,e)),"string"==typeof e&&t.isFunction(n=a[e])&&(i=n.apply(a,s))}),typeof i!==n?i:this},t.fn.formcache.Constructor=u,t.fn.formcache.setDefaults=u.setDefaults,t.fn.formcache.noConflict=function(){return t.fn.formcache=u.other,this},t(function(){t('form[data-toggle="formcache"]').formcache()})});
|
|
@ -55,6 +55,23 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Log modal
|
||||||
|
$('#dnsInfoModal').on('show.bs.modal', function(e) {
|
||||||
|
var domain = $(e.relatedTarget).data('domain');
|
||||||
|
$('.dns-modal-body').html('<center><span style="font-size:18pt;margin:50px" class="glyphicon glyphicon-refresh glyphicon-spin"></span></center>');
|
||||||
|
$.ajax({
|
||||||
|
url: '/inc/ajax/dns_diagnostics.php',
|
||||||
|
data: { domain: domain },
|
||||||
|
dataType: 'text',
|
||||||
|
success: function(data){
|
||||||
|
$('.dns-modal-body').html(data);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
$('.dns-modal-body').html(xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Sieve data modal
|
// Sieve data modal
|
||||||
$('#sieveDataModal').on('show.bs.modal', function(e) {
|
$('#sieveDataModal').on('show.bs.modal', function(e) {
|
||||||
var sieveScript = $(e.relatedTarget).data('sieve-script');
|
var sieveScript = $(e.relatedTarget).data('sieve-script');
|
||||||
|
@ -154,7 +171,7 @@ jQuery(function($){
|
||||||
{"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm"},
|
{"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm"},
|
||||||
{"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm"},
|
{"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm"},
|
||||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"240px","width":"240px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||||
],
|
],
|
||||||
"rows": $.ajax({
|
"rows": $.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
@ -170,17 +187,15 @@ jQuery(function($){
|
||||||
item.quota = item.quota_used_in_domain + "/" + item.max_quota_for_domain;
|
item.quota = item.quota_used_in_domain + "/" + item.max_quota_for_domain;
|
||||||
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
|
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
|
||||||
item.chkbox = '<input type="checkbox" data-id="domain" name="multi_select" value="' + item.domain_name + '" />';
|
item.chkbox = '<input type="checkbox" data-id="domain" name="multi_select" value="' + item.domain_name + '" />';
|
||||||
|
item.action = '<div class="btn-group">';
|
||||||
if (role == "admin") {
|
if (role == "admin") {
|
||||||
item.action = '<div class="btn-group">' +
|
item.action += '<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||||
'<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
'<a href="#" id="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>';
|
||||||
'<a href="#" id="delete_selected" data-id="single-domain" data-api-url="delete/domain" data-item="' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
|
||||||
'</div>';
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item.action = '<div class="btn-group">' +
|
item.action += '<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>';
|
||||||
'<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
|
||||||
'</div>';
|
|
||||||
}
|
}
|
||||||
|
item.action += '<a href="#dnsInfoModal" class="btn btn-xs btn-info" data-toggle="modal" data-domain="' + encodeURI(item.domain_name) + '"><span class="glyphicon glyphicon-question-sign"></span> DNS</a></div>';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -193,6 +208,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -263,6 +279,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -307,6 +324,58 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
|
"placeholder": lang.filter_table
|
||||||
|
},
|
||||||
|
"sorting": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function draw_bcc_table() {
|
||||||
|
ft_bcc_table = FooTable.init('#bcc_table', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||||
|
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||||
|
{"name":"type","title":lang.bcc_type},
|
||||||
|
{"name":"local_dest","title":lang.bcc_local_dest},
|
||||||
|
{"name":"bcc_dest","title":lang.bcc_destinations},
|
||||||
|
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
|
||||||
|
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||||
|
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||||
|
],
|
||||||
|
"empty": lang.empty,
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/bcc/all',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw bcc table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.action = '<div class="btn-group">' +
|
||||||
|
'<a href="/edit.php?bcc=' + item.id + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||||
|
'<a href="#" id="delete_selected" data-id="single-bcc" data-api-url="delete/bcc" data-item="' + item.id + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||||
|
'</div>';
|
||||||
|
item.chkbox = '<input type="checkbox" data-id="bcc" name="multi_select" value="' + item.id + '" />';
|
||||||
|
if (item.type == 'sender') {
|
||||||
|
item.type = '<span id="active-script" class="label label-success">Sender</span>';
|
||||||
|
} else {
|
||||||
|
item.type = '<span id="inactive-script" class="label label-warning">Recipient</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"paging": {
|
||||||
|
"enabled": true,
|
||||||
|
"limit": 5,
|
||||||
|
"size": pagination_size
|
||||||
|
},
|
||||||
|
"filtering": {
|
||||||
|
"enabled": true,
|
||||||
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -314,7 +383,6 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_alias_table() {
|
function draw_alias_table() {
|
||||||
ft_alias_table = FooTable.init('#alias_table', {
|
ft_alias_table = FooTable.init('#alias_table', {
|
||||||
"columns": [
|
"columns": [
|
||||||
|
@ -360,6 +428,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -375,7 +444,7 @@ jQuery(function($){
|
||||||
{"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
|
{"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
|
||||||
{"name":"target_domain","title":lang.target_domain},
|
{"name":"target_domain","title":lang.target_domain},
|
||||||
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
|
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
|
||||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"250px","width":"250px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||||
],
|
],
|
||||||
"empty": lang.empty,
|
"empty": lang.empty,
|
||||||
"rows": $.ajax({
|
"rows": $.ajax({
|
||||||
|
@ -390,6 +459,7 @@ jQuery(function($){
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="/edit.php?aliasdomain=' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
'<a href="/edit.php?aliasdomain=' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||||
'<a href="#" id="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
'<a href="#" id="delete_selected" data-id="single-alias-domain" data-api-url="delete/alias-domain" data-item="' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||||
|
'<a href="#dnsInfoModal" class="btn btn-xs btn-info" data-toggle="modal" data-domain="' + encodeURI(item.alias_domain) + '"><span class="glyphicon glyphicon-question-sign"></span> DNS</a></div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
item.chkbox = '<input type="checkbox" data-id="alias-domain" name="multi_select" value="' + item.alias_domain + '" />';
|
item.chkbox = '<input type="checkbox" data-id="alias-domain" name="multi_select" value="' + item.alias_domain + '" />';
|
||||||
});
|
});
|
||||||
|
@ -403,6 +473,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -418,6 +489,7 @@ jQuery(function($){
|
||||||
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||||
{"name":"user2","title":lang.owner},
|
{"name":"user2","title":lang.owner},
|
||||||
{"name":"server_w_port","title":"Server","breakpoints":"xs"},
|
{"name":"server_w_port","title":"Server","breakpoints":"xs"},
|
||||||
|
{"name":"exclude","title":lang.excludes,"breakpoints":"all"},
|
||||||
{"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"},
|
{"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"},
|
||||||
{"name":"last_run","title":lang.last_run,"breakpoints":"all"},
|
{"name":"last_run","title":lang.last_run,"breakpoints":"all"},
|
||||||
{"name":"log","title":"Log"},
|
{"name":"log","title":"Log"},
|
||||||
|
@ -436,7 +508,11 @@ jQuery(function($){
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
item.log = '<a href="#syncjobLogModal" data-toggle="modal" data-syncjob-id="' + encodeURI(item.id) + '">Open logs</a>'
|
item.log = '<a href="#syncjobLogModal" data-toggle="modal" data-syncjob-id="' + encodeURI(item.id) + '">Open logs</a>'
|
||||||
item.exclude = '<code>' + item.exclude + '</code>'
|
if (!item.exclude > 0) {
|
||||||
|
item.exclude = '-';
|
||||||
|
} else {
|
||||||
|
item.exclude = '<code>' + item.exclude + '</code>';
|
||||||
|
}
|
||||||
item.server_w_port = item.user1 + '@' + item.host1 + ':' + item.port1;
|
item.server_w_port = item.user1 + '@' + item.host1 + ':' + item.port1;
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="/edit.php?syncjob=' + item.id + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
'<a href="/edit.php?syncjob=' + item.id + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||||
|
@ -462,6 +538,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -515,6 +592,7 @@ jQuery(function($){
|
||||||
"filtering": {
|
"filtering": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"position": "left",
|
"position": "left",
|
||||||
|
"connectors": false,
|
||||||
"placeholder": lang.filter_table
|
"placeholder": lang.filter_table
|
||||||
},
|
},
|
||||||
"sorting": {
|
"sorting": {
|
||||||
|
@ -530,5 +608,6 @@ jQuery(function($){
|
||||||
draw_aliasdomain_table();
|
draw_aliasdomain_table();
|
||||||
draw_sync_job_table();
|
draw_sync_job_table();
|
||||||
draw_filter_table();
|
draw_filter_table();
|
||||||
|
draw_bcc_table();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
// 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($){
|
||||||
|
// 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_quarantaine_table() {
|
||||||
|
ft_quarantainetable = FooTable.init('#quarantainetable', {
|
||||||
|
"columns": [
|
||||||
|
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||||
|
{"name":"id","type":"ID","filterable": false,"sorted": true,"direction":"DESC","title":"ID","style":{"width":"50px"}},
|
||||||
|
{"name":"qid","type":"text","title":lang.qid,"style":{"width":"125px"}},
|
||||||
|
{"name":"sender","title":lang.sender,"breakpoints":"xs sm"},
|
||||||
|
{"name":"rcpt","title":lang.rcpt, "type": "text"},
|
||||||
|
{"name":"created","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.received,"style":{"width":"170px"}},
|
||||||
|
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"205px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||||
|
],
|
||||||
|
"rows": $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: '/api/v1/get/quarantaine/all',
|
||||||
|
jsonp: false,
|
||||||
|
error: function () {
|
||||||
|
console.log('Cannot draw quarantaine table');
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.action = '<div class="btn-group">' +
|
||||||
|
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><span class="glyphicon glyphicon-modal-window"></span> ' + lang.show_item + '</a>' +
|
||||||
|
'<a href="#" id="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||||
|
'</div>';
|
||||||
|
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"empty": lang.empty,
|
||||||
|
"paging": {"enabled": true,"limit": 5,"size": pagination_size},
|
||||||
|
"sorting": {"enabled": true},
|
||||||
|
"on": {
|
||||||
|
"ready.ft.table": 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();
|
||||||
|
}
|
||||||
|
$('#qid_detail_subj').text(escapeHtml(data.subject));
|
||||||
|
$('#qid_detail_text').text(escapeHtml(data.text_plain));
|
||||||
|
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('-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial table drawings
|
||||||
|
draw_quarantaine_table();
|
||||||
|
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
|
@ -86,14 +86,14 @@ jQuery(function($){
|
||||||
function draw_sync_job_table() {
|
function draw_sync_job_table() {
|
||||||
ft_syncjob_table = FooTable.init('#sync_job_table', {
|
ft_syncjob_table = FooTable.init('#sync_job_table', {
|
||||||
"columns": [
|
"columns": [
|
||||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
|
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
|
||||||
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||||
{"name":"server_w_port","title":"Server"},
|
{"name":"server_w_port","title":"Server"},
|
||||||
{"name":"enc1","title":lang.encryption,"breakpoints":"xs sm"},
|
{"name":"enc1","title":lang.encryption,"breakpoints":"xs sm"},
|
||||||
{"name":"user1","title":lang.username},
|
{"name":"user1","title":lang.username},
|
||||||
{"name":"exclude","title":lang.excludes,"breakpoints":"xs sm"},
|
{"name":"exclude","title":lang.excludes,"breakpoints":"all"},
|
||||||
{"name":"mins_interval","title":lang.interval + " (min)"},
|
{"name":"mins_interval","title":lang.interval + " (min)","breakpoints":"all"},
|
||||||
{"name":"last_run","title":lang.last_run,"breakpoints":"xs sm"},
|
{"name":"last_run","title":lang.last_run,"breakpoints":"all"},
|
||||||
{"name":"log","title":"Log"},
|
{"name":"log","title":"Log"},
|
||||||
{"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active},
|
{"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active},
|
||||||
{"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status},
|
{"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status},
|
||||||
|
|
|
@ -15,6 +15,41 @@ delete/alias => POST data:
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||||
error_reporting(0);
|
error_reporting(0);
|
||||||
|
|
||||||
|
function api_log($postarray) {
|
||||||
|
global $redis;
|
||||||
|
$data_var = array();
|
||||||
|
foreach ($postarray as $data => &$value) {
|
||||||
|
if ($data == 'csrf_token') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($value = json_decode($value, true)) {
|
||||||
|
unset($value["csrf_token"]);
|
||||||
|
$value = json_encode($value);
|
||||||
|
}
|
||||||
|
$data_var[] = $data . "='" . $value . "'";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$log_line = array(
|
||||||
|
'time' => time(),
|
||||||
|
'uri' => $_SERVER['REQUEST_URI'],
|
||||||
|
'method' => $_SERVER['REQUEST_METHOD'],
|
||||||
|
'remote' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
'data' => implode(', ', $data_var)
|
||||||
|
);
|
||||||
|
$redis->lPush('API_LOG', json_encode($log_line));
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'Redis: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api_log($_POST);
|
||||||
|
|
||||||
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
|
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
|
||||||
if (isset($_GET['query'])) {
|
if (isset($_GET['query'])) {
|
||||||
|
|
||||||
|
@ -522,6 +557,39 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "bcc":
|
||||||
|
if (isset($_POST['attr'])) {
|
||||||
|
$attr = (array)json_decode($_POST['attr'], true);
|
||||||
|
if (bcc('add', $attr) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot add item'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot find attributes in post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "get":
|
case "get":
|
||||||
|
@ -719,6 +787,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "relayhost":
|
case "relayhost":
|
||||||
switch ($object) {
|
switch ($object) {
|
||||||
case "all":
|
case "all":
|
||||||
|
@ -837,6 +906,54 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
echo '{}';
|
echo '{}';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "watchdog":
|
||||||
|
// 0 is first record, so empty is fine
|
||||||
|
if (isset($extra)) {
|
||||||
|
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||||
|
$logs = get_logs('watchdog-mailcow', $extra);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$logs = get_logs('watchdog-mailcow');
|
||||||
|
}
|
||||||
|
if (isset($logs) && !empty($logs)) {
|
||||||
|
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "acme":
|
||||||
|
// 0 is first record, so empty is fine
|
||||||
|
if (isset($extra)) {
|
||||||
|
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||||
|
$logs = get_logs('acme-mailcow', $extra);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$logs = get_logs('acme-mailcow');
|
||||||
|
}
|
||||||
|
if (isset($logs) && !empty($logs)) {
|
||||||
|
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "api":
|
||||||
|
// 0 is first record, so empty is fine
|
||||||
|
if (isset($extra)) {
|
||||||
|
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||||
|
$logs = get_logs('api-mailcow', $extra);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$logs = get_logs('api-mailcow');
|
||||||
|
}
|
||||||
|
if (isset($logs) && !empty($logs)) {
|
||||||
|
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "rspamd-history":
|
case "rspamd-history":
|
||||||
// 0 is first record, so empty is fine
|
// 0 is first record, so empty is fine
|
||||||
if (isset($extra)) {
|
if (isset($extra)) {
|
||||||
|
@ -1034,6 +1151,41 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "bcc":
|
||||||
|
switch ($object) {
|
||||||
|
case "all":
|
||||||
|
$bcc_items = bcc('get');
|
||||||
|
if (!empty($bcc_items)) {
|
||||||
|
foreach ($bcc_items as $bcc_item) {
|
||||||
|
if ($details = bcc('details', $bcc_item)) {
|
||||||
|
$data[] = $details;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($data) || empty($data)) {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data = bcc('details', $object);
|
||||||
|
if (!empty($data)) {
|
||||||
|
$data[] = $details;
|
||||||
|
}
|
||||||
|
if (!isset($data) || empty($data)) {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "policy_wl_mailbox":
|
case "policy_wl_mailbox":
|
||||||
switch ($object) {
|
switch ($object) {
|
||||||
default:
|
default:
|
||||||
|
@ -1161,6 +1313,29 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "quarantaine":
|
||||||
|
// "all" will not print details
|
||||||
|
switch ($object) {
|
||||||
|
case "all":
|
||||||
|
$data = quarantaine('get');
|
||||||
|
if (!isset($data) || empty($data)) {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data = quarantaine('details', $object);
|
||||||
|
if (!isset($data) || empty($data)) {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "alias-domain":
|
case "alias-domain":
|
||||||
switch ($object) {
|
switch ($object) {
|
||||||
case "all":
|
case "all":
|
||||||
|
@ -1464,6 +1639,88 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "qitem":
|
||||||
|
if (isset($_POST['items'])) {
|
||||||
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
if (is_array($items)) {
|
||||||
|
if (quarantaine('delete', array('id' => $items)) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Deletion of items/s failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot find id array in post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot find items in post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "bcc":
|
||||||
|
if (isset($_POST['items'])) {
|
||||||
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
if (is_array($items)) {
|
||||||
|
if (bcc('delete', array('id' => $items)) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Deletion of items/s failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot find id array in post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Cannot find items in post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "fwdhost":
|
case "fwdhost":
|
||||||
if (isset($_POST['items'])) {
|
if (isset($_POST['items'])) {
|
||||||
$items = (array)json_decode($_POST['items'], true);
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
@ -1919,6 +2176,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
break;
|
break;
|
||||||
case "edit":
|
case "edit":
|
||||||
switch ($category) {
|
switch ($category) {
|
||||||
|
case "bcc":
|
||||||
|
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
||||||
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
$attr = (array)json_decode($_POST['attr'], true);
|
||||||
|
$postarray = array_merge(array('id' => $items), $attr);
|
||||||
|
if (is_array($postarray['id'])) {
|
||||||
|
if (bcc('edit', $postarray) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Edit failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "alias":
|
case "alias":
|
||||||
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
||||||
$items = (array)json_decode($_POST['items'], true);
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
@ -2137,6 +2438,85 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "qitem":
|
||||||
|
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
||||||
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
$attr = (array)json_decode($_POST['attr'], true);
|
||||||
|
$postarray = array_merge(array('id' => $items), $attr);
|
||||||
|
if (is_array($postarray['id'])) {
|
||||||
|
if (quarantaine('edit', $postarray) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Edit failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "quarantaine":
|
||||||
|
// Edit settings, does not need IDs
|
||||||
|
if (isset($_POST['attr'])) {
|
||||||
|
$postarray = json_decode($_POST['attr'], true);
|
||||||
|
if (quarantaine('edit', $postarray) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Edit failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "time_limited_alias":
|
case "time_limited_alias":
|
||||||
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
||||||
$items = (array)json_decode($_POST['items'], true);
|
$items = (array)json_decode($_POST['items'], true);
|
||||||
|
@ -2364,7 +2744,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
$attr = (array)json_decode($_POST['attr'], true);
|
$attr = (array)json_decode($_POST['attr'], true);
|
||||||
$postarray = array_merge(array('domain' => $items), $attr);
|
$postarray = array_merge(array('domain' => $items), $attr);
|
||||||
if (is_array($postarray['domain'])) {
|
if (is_array($postarray['domain'])) {
|
||||||
if (mailbox('edit', 'domain', $postarray) === false) {
|
if (mailbox('edit', 'domain', $postarray)) {
|
||||||
if (isset($_SESSION['return'])) {
|
if (isset($_SESSION['return'])) {
|
||||||
echo json_encode($_SESSION['return']);
|
echo json_encode($_SESSION['return']);
|
||||||
}
|
}
|
||||||
|
@ -2657,6 +3037,41 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "ui_texts":
|
||||||
|
// No items
|
||||||
|
if (isset($_POST['attr'])) {
|
||||||
|
$attr = (array)json_decode($_POST['attr'], true);
|
||||||
|
if (customize('edit', 'ui_texts', $attr) === false) {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Edit failed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isset($_SESSION['return'])) {
|
||||||
|
echo json_encode($_SESSION['return']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => 'Task completed'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo json_encode(array(
|
||||||
|
'type' => 'error',
|
||||||
|
'msg' => 'Incomplete post data'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "self":
|
case "self":
|
||||||
// No items, logged-in user, users and domain admins
|
// No items, logged-in user, users and domain admins
|
||||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
$lang['footer']['loading'] = 'Einen Moment bitte...';
|
$lang['footer']['loading'] = 'Einen Moment bitte...';
|
||||||
$lang['header']['restart_sogo'] = 'SOGo neustarten';
|
$lang['header']['restart_sogo'] = 'SOGo neustarten';
|
||||||
$lang['footer']['restart_sogo'] = 'SOGo neustarten';
|
$lang['footer']['restart_container'] = 'Container neustarten';
|
||||||
$lang['footer']['restart_now'] = 'Jetzt neustarten';
|
$lang['footer']['restart_now'] = 'Jetzt neustarten';
|
||||||
$lang['footer']['restart_sogo_info'] = 'Einige Änderungen an Domains benötigen einen Neustart SOGos. Hier können Sie SOGo neustarten.<br><br><b>Wichtig:</b> Ein korrekter Neustart SOGos kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
|
$lang['footer']['restart_container_info'] = '<b>Wichtig:</b> Ein korrekter Neustart eines Containers kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
|
||||||
|
|
||||||
$lang['footer']['confirm_delete'] = 'Löschen bestätigen';
|
$lang['footer']['confirm_delete'] = 'Löschen bestätigen';
|
||||||
$lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen an Elementen mit folgender ID durchgeführt werden sollen?';
|
$lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen an Elementen mit folgender ID durchgeführt werden sollen?';
|
||||||
|
@ -73,7 +73,7 @@ $lang['danger']['resource_invalid'] = 'Ressourcenname ist ungültig';
|
||||||
$lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig';
|
$lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig';
|
||||||
$lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht %s?';
|
$lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht %s?';
|
||||||
$lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse';
|
$lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse';
|
||||||
$lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox oder ein Alias mit der Adresse %s ist bereits vorhanden";
|
$lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden";
|
||||||
$lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse';
|
$lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse';
|
||||||
$lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein';
|
$lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein';
|
||||||
$lang['danger']['domain_not_found'] = 'Domain %s nicht gefunden';
|
$lang['danger']['domain_not_found'] = 'Domain %s nicht gefunden';
|
||||||
|
@ -121,6 +121,8 @@ $lang['user']['did_you_know'] = '<b>Wussten Sie schon?</b> Sie können Ihre E-Ma
|
||||||
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
|
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
|
||||||
$lang['user']['alias'] = 'Alias';
|
$lang['user']['alias'] = 'Alias';
|
||||||
$lang['user']['aliases'] = 'Aliasse';
|
$lang['user']['aliases'] = 'Aliasse';
|
||||||
|
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen';
|
||||||
|
$lang['user']['direct_aliases'] = 'Direkte Alias-Adressen';
|
||||||
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
|
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
|
||||||
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
|
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
|
||||||
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
|
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
|
||||||
|
@ -248,7 +250,7 @@ $lang['mailbox']['target_domain'] = 'Ziel-Domain';
|
||||||
$lang['mailbox']['target_address'] = 'Ziel-Adresse';
|
$lang['mailbox']['target_address'] = 'Ziel-Adresse';
|
||||||
$lang['mailbox']['username'] = 'Benutzername';
|
$lang['mailbox']['username'] = 'Benutzername';
|
||||||
$lang['mailbox']['fname'] = 'Name';
|
$lang['mailbox']['fname'] = 'Name';
|
||||||
$lang['mailbox']['filter_table'] = 'Tabelle filtern';
|
$lang['mailbox']['filter_table'] = 'Filtern';
|
||||||
$lang['mailbox']['yes'] = '✔';
|
$lang['mailbox']['yes'] = '✔';
|
||||||
$lang['mailbox']['no'] = '✘';
|
$lang['mailbox']['no'] = '✘';
|
||||||
$lang['mailbox']['quota'] = 'Speicherplatz';
|
$lang['mailbox']['quota'] = 'Speicherplatz';
|
||||||
|
@ -491,6 +493,8 @@ $lang['admin']['dkim_key_unused'] = 'Key ohne Zuweisung';
|
||||||
$lang['admin']['dkim_key_missing'] = 'Key fehlt';
|
$lang['admin']['dkim_key_missing'] = 'Key fehlt';
|
||||||
$lang['admin']['dkim_key_hint'] = 'Der Selector für DKIM-Keys lautet immer <code>dkim</code>.';
|
$lang['admin']['dkim_key_hint'] = 'Der Selector für DKIM-Keys lautet immer <code>dkim</code>.';
|
||||||
$lang['admin']['add'] = 'Hinzufügen';
|
$lang['admin']['add'] = 'Hinzufügen';
|
||||||
|
$lang['add']['add_domain_restart'] = 'Domain hinzufügen und SOGo neustarten';
|
||||||
|
$lang['add']['add_domain_only'] = 'Nur Domain hinzufügen';
|
||||||
$lang['admin']['configuration'] = 'Konfiguration';
|
$lang['admin']['configuration'] = 'Konfiguration';
|
||||||
$lang['admin']['password'] = 'Passwort';
|
$lang['admin']['password'] = 'Passwort';
|
||||||
$lang['admin']['password_repeat'] = 'Passwort (Wiederholung)';
|
$lang['admin']['password_repeat'] = 'Passwort (Wiederholung)';
|
||||||
|
@ -533,6 +537,10 @@ $lang['admin']['host'] = 'Host';
|
||||||
$lang['admin']['source'] = 'Quelle';
|
$lang['admin']['source'] = 'Quelle';
|
||||||
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
|
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
|
||||||
$lang['admin']['add_relayhost'] = 'Relayhost hinzufügen';
|
$lang['admin']['add_relayhost'] = 'Relayhost hinzufügen';
|
||||||
|
$lang['admin']['api_allow_from'] = "IP-Adressen für Zugriff";
|
||||||
|
$lang['admin']['api_key'] = "API-Key";
|
||||||
|
$lang['admin']['activate_api'] = "API aktivieren";
|
||||||
|
$lang['admin']['regen_api_key'] = "API-Key regenerieren";
|
||||||
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!';
|
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!';
|
||||||
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
|
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
|
||||||
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
|
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
|
||||||
|
@ -544,6 +552,8 @@ $lang['diagnostics']['dns_records_name'] = 'Name';
|
||||||
$lang['diagnostics']['dns_records_type'] = 'Typ';
|
$lang['diagnostics']['dns_records_type'] = 'Typ';
|
||||||
$lang['diagnostics']['dns_records_data'] = 'Korrekte Daten';
|
$lang['diagnostics']['dns_records_data'] = 'Korrekte Daten';
|
||||||
$lang['diagnostics']['dns_records_status'] = 'Aktueller Status';
|
$lang['diagnostics']['dns_records_status'] = 'Aktueller Status';
|
||||||
|
$lang['diagnostics']['optional'] = 'Dieser Eintrag ist optional.';
|
||||||
|
$lang['diagnostics']['cname_from_a'] = 'Wert abgeleitet von A/AAAA Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.';
|
||||||
$lang['admin']['relay_from'] = "Absenderadresse";
|
$lang['admin']['relay_from'] = "Absenderadresse";
|
||||||
$lang['admin']['relay_run'] = "Test durchführen";
|
$lang['admin']['relay_run'] = "Test durchführen";
|
||||||
$lang['admin']['customize'] = "Anpassung";
|
$lang['admin']['customize'] = "Anpassung";
|
||||||
|
@ -560,3 +570,80 @@ $lang['admin']['merged_vars_hint'] = 'Ausgegraute Zeilen wurden aus der Datei <c
|
||||||
$lang['mailbox']['waiting'] = "Wartend";
|
$lang['mailbox']['waiting'] = "Wartend";
|
||||||
$lang['mailbox']['status'] = "Status";
|
$lang['mailbox']['status'] = "Status";
|
||||||
$lang['mailbox']['running'] = "In Ausführung";
|
$lang['mailbox']['running'] = "In Ausführung";
|
||||||
|
|
||||||
|
$lang['admin']['ui_texts'] = "UI Label und Texte";
|
||||||
|
$lang['admin']['help_text'] = "Hilfstext unter Login-Maske (HTML zulässig)";
|
||||||
|
$lang['admin']['main_name'] = '"mailcow UI" Name';
|
||||||
|
$lang['admin']['apps_name'] = '"mailcow Apps" Name';
|
||||||
|
|
||||||
|
$lang['admin']['customize'] = "UI Anpassung";
|
||||||
|
$lang['admin']['change_logo'] = "Logo ändern";
|
||||||
|
$lang['admin']['logo_info'] = "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.";
|
||||||
|
$lang['admin']['upload'] = "Hochladen";
|
||||||
|
$lang['admin']['app_links'] = "App Links";
|
||||||
|
$lang['admin']['app_name'] = "App Name";
|
||||||
|
$lang['admin']['link'] = "Link";
|
||||||
|
$lang['admin']['remove_row'] = "Entfernen";
|
||||||
|
$lang['admin']['add_row'] = "Reihe hinzufügen";
|
||||||
|
$lang['admin']['reset_default'] = "Zurücksetzen auf Standard";
|
||||||
|
$lang['admin']['merged_vars_hint'] = 'Ausgegraute Reihen wurden aus der Datei <code>vars.inc.(local.)php</code> gelesen und können hier nicht verändert werden.';
|
||||||
|
|
||||||
|
$lang['edit']['tls_policy'] = "TLS Policy ändern";
|
||||||
|
$lang['edit']['spam_score'] = "Einen benutzerdefiniterten Spam-Score festlegen";
|
||||||
|
$lang['edit']['spam_policy'] = "Hinzufügen und Entfernen von Einträgen in White- und Blacklists";
|
||||||
|
$lang['edit']['delimiter_action'] = "Delimiter Aktion verändern";
|
||||||
|
$lang['edit']['syncjobs'] = "Sync job hinzufügen oder anpassen";
|
||||||
|
$lang['edit']['eas_reset'] = "ActiveSync Geräte-Cache zurücksetzen";
|
||||||
|
$lang['edit']['spam_alias'] = "Anpassen temporärer Alias-Adressen";
|
||||||
|
|
||||||
|
$lang['danger']['img_tmp_missing'] = "Grafik konnte nicht validiert werden: Erstellung temporärer Datei fehlgeschlagen";
|
||||||
|
$lang['danger']['img_invalid'] = "Grafik konnte nicht validiert werden";
|
||||||
|
$lang['danger']['invalid_mime_type'] = "Grafik konnte nicht validiert werden: Ungültiger MIME-Type";
|
||||||
|
$lang['success']['upload_success'] = "Datei wurde erfolgreich hochgeladen";
|
||||||
|
$lang['success']['app_links'] = "Änderungen an App Links wurden gespeichert";
|
||||||
|
$lang['success']['ui_texts'] = "Änderungen an UI-Texten";
|
||||||
|
$lang['success']['reset_main_logo'] = "Standardgrafik wurde wiederhergestellt";
|
||||||
|
$lang['success']['items_released'] = "Ausgewählte Objekte wurden an Mailbox versendet";
|
||||||
|
$lang['danger']['imagick_exception'] = "Fataler Bildverarbeitungsfehler";
|
||||||
|
|
||||||
|
$lang['quarantaine']['quarantaine'] = "Quarantäne";
|
||||||
|
$lang['quarantaine']['qinfo'] = "Das Quarantänesystem speichert abgelehnte Nachrichten in der Datenbank. Die Nachricht wird <em>nicht</em> angekommen. Der Absender erhält keinen Eindruck einer zugestellten E-Mail.<br />
|
||||||
|
E-Mails mit einer maximalen Größe von 10 MiB werden gespeichert.";
|
||||||
|
$lang['quarantaine']['release'] = "Freigeben";
|
||||||
|
$lang['quarantaine']['empty'] = 'Keine Einträge';
|
||||||
|
$lang['quarantaine']['toggle_all'] = 'Alle auswählen';
|
||||||
|
$lang['quarantaine']['quick_actions'] = 'Aktionen';
|
||||||
|
$lang['quarantaine']['remove'] = 'Entfernen';
|
||||||
|
$lang['quarantaine']['received'] = "Empfangen";
|
||||||
|
$lang['quarantaine']['action'] = "Aktion";
|
||||||
|
$lang['quarantaine']['rcpt'] = "Empfänger";
|
||||||
|
$lang['quarantaine']['qid'] = "Rspamd QID";
|
||||||
|
$lang['quarantaine']['sender'] = "Sender";
|
||||||
|
$lang['quarantaine']['show_item'] = "Details";
|
||||||
|
$lang['quarantaine']['check_hash'] = "Checksumme auf VirusTotal suchen";
|
||||||
|
$lang['quarantaine']['qitem'] = "Quarantäneeintrag";
|
||||||
|
$lang['quarantaine']['subj'] = "Betreff";
|
||||||
|
$lang['quarantaine']['text_plain_content'] = "Inhalt (text/plain)";
|
||||||
|
$lang['quarantaine']['atts'] = "Anhänge";
|
||||||
|
|
||||||
|
$lang['header']['quarantaine'] = "Quarantäne";
|
||||||
|
$lang['header']['debug'] = "Debugging";
|
||||||
|
|
||||||
|
$lang['quarantaine']['release_body'] = "Die ursprüngliche Nachricht wurde als EML-Datei im Anhang hinterlegt.";
|
||||||
|
$lang['danger']['release_send_failed'] = "Die Nachricht konnte nicht versendet werden: %s";
|
||||||
|
$lang['quarantaine']['release_subject'] = "Potentiell schädliche Nachricht aus Quarantäne: %s";
|
||||||
|
|
||||||
|
$lang['mailbox']['bcc_map_type'] = "BCC Typ";
|
||||||
|
$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(e)";
|
||||||
|
|
||||||
|
$lang['mailbox']['bcc'] = "BCC";
|
||||||
|
$lang['mailbox']['bcc_maps'] = "BCC-Maps";
|
||||||
|
$lang['mailbox']['bcc_to_sender'] = "Map senderabhängig verwenden";
|
||||||
|
$lang['mailbox']['bcc_to_rcpt'] = "Map empfängerabhängig verwenden";
|
||||||
|
$lang['mailbox']['add_bcc_entry'] = "BCC-Eintrag hinzufügen";
|
||||||
|
$lang['mailbox']['bcc_info'] = "Eine empfängerabhängige Map wird verwendet, wenn die BCC-Map Eintragung auf den Eingang einer E-Mail auf das lokale Ziel reagieren soll. Senderabhängige Maps verfahren nach dem gleichen Prinzip.<br/>
|
||||||
|
Das lokale Ziel wird bei Fehlzustellungen an ein BCC-Ziel nicht informiert.";
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
$lang['footer']['loading'] = "Please wait...";
|
$lang['footer']['loading'] = "Please wait...";
|
||||||
$lang['header']['restart_sogo'] = 'Restart SOGo';
|
$lang['header']['restart_sogo'] = 'Restart SOGo';
|
||||||
$lang['footer']['restart_sogo'] = 'Restart SOGo';
|
$lang['footer']['restart_container'] = 'Restart container';
|
||||||
$lang['footer']['restart_now'] = 'Restart now';
|
$lang['footer']['restart_now'] = 'Restart now';
|
||||||
$lang['footer']['restart_sogo_info'] = 'Some tasks, e.g. adding a domain, require you to restart SOGo to catch changes made in the mailcow UI.<br><br><b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
|
$lang['footer']['restart_container_info'] = '<b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
|
||||||
|
|
||||||
$lang['footer']['confirm_delete'] = 'Confirm deletion';
|
$lang['footer']['confirm_delete'] = 'Confirm deletion';
|
||||||
$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id:';
|
$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id:';
|
||||||
|
@ -73,7 +73,7 @@ $lang['danger']['description_invalid'] = 'Resource description is invalid';
|
||||||
$lang['danger']['resource_invalid'] = "Resource name is invalid";
|
$lang['danger']['resource_invalid'] = "Resource name is invalid";
|
||||||
$lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?';
|
$lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?';
|
||||||
$lang['danger']['is_alias'] = "%s is already known as an alias address";
|
$lang['danger']['is_alias'] = "%s is already known as an alias address";
|
||||||
$lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias or a mailbox";
|
$lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias, a mailbox or an alias address expanded from an alias domain.";
|
||||||
$lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
|
$lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
|
||||||
$lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0";
|
$lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0";
|
||||||
$lang['danger']['domain_not_found'] = 'Domain %s not found';
|
$lang['danger']['domain_not_found'] = 'Domain %s not found';
|
||||||
|
@ -121,6 +121,8 @@ $lang['user']['did_you_know'] = '<b>Did you know?</b> You can use tags in your e
|
||||||
$lang['user']['spam_aliases'] = 'Temporary email aliases';
|
$lang['user']['spam_aliases'] = 'Temporary email aliases';
|
||||||
$lang['user']['alias'] = 'Alias';
|
$lang['user']['alias'] = 'Alias';
|
||||||
$lang['user']['aliases'] = 'Aliases';
|
$lang['user']['aliases'] = 'Aliases';
|
||||||
|
$lang['user']['shared_aliases'] = 'Shared alias addresses';
|
||||||
|
$lang['user']['direct_aliases'] = 'Direct alias addresses';
|
||||||
$lang['user']['domain_aliases'] = 'Domain alias addresses';
|
$lang['user']['domain_aliases'] = 'Domain alias addresses';
|
||||||
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
|
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
|
||||||
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
|
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
|
||||||
|
@ -272,6 +274,7 @@ $lang['mailbox']['deactivate'] = 'Deactivate';
|
||||||
$lang['mailbox']['owner'] = 'Owner';
|
$lang['mailbox']['owner'] = 'Owner';
|
||||||
$lang['mailbox']['mins_interval'] = 'Interval (min)';
|
$lang['mailbox']['mins_interval'] = 'Interval (min)';
|
||||||
$lang['mailbox']['last_run'] = 'Last run';
|
$lang['mailbox']['last_run'] = 'Last run';
|
||||||
|
$lang['mailbox']['excludes'] = 'Excludes';
|
||||||
$lang['mailbox']['last_run_reset'] = 'Schedule next';
|
$lang['mailbox']['last_run_reset'] = 'Schedule next';
|
||||||
$lang['mailbox']['sieve_info'] = 'You can store multiple filters per user, but only one prefilter and one postfilter can be active at the same time.<br>
|
$lang['mailbox']['sieve_info'] = 'You can store multiple filters per user, but only one prefilter and one postfilter can be active at the same time.<br>
|
||||||
Each filter will be processed in the described order. Neither a failed script nor an issued "keep;" will stop processing of further scripts.<br>
|
Each filter will be processed in the described order. Neither a failed script nor an issued "keep;" will stop processing of further scripts.<br>
|
||||||
|
@ -496,6 +499,8 @@ $lang['admin']['sys_info'] = 'System information';
|
||||||
$lang['admin']['dkim_add_key'] = 'Add ARC/DKIM key';
|
$lang['admin']['dkim_add_key'] = 'Add ARC/DKIM key';
|
||||||
$lang['admin']['dkim_keys'] = 'ARC/DKIM keys';
|
$lang['admin']['dkim_keys'] = 'ARC/DKIM keys';
|
||||||
$lang['admin']['add'] = 'Add';
|
$lang['admin']['add'] = 'Add';
|
||||||
|
$lang['add']['add_domain_restart'] = 'Add domain and restart SOGo';
|
||||||
|
$lang['add']['add_domain_only'] = 'Add domain only';
|
||||||
$lang['admin']['configuration'] = 'Configuration';
|
$lang['admin']['configuration'] = 'Configuration';
|
||||||
$lang['admin']['password'] = 'Password';
|
$lang['admin']['password'] = 'Password';
|
||||||
$lang['admin']['password_repeat'] = 'Confirmation password (repeat)';
|
$lang['admin']['password_repeat'] = 'Confirmation password (repeat)';
|
||||||
|
@ -549,8 +554,19 @@ $lang['diagnostics']['dns_records_name'] = 'Name';
|
||||||
$lang['diagnostics']['dns_records_type'] = 'Type';
|
$lang['diagnostics']['dns_records_type'] = 'Type';
|
||||||
$lang['diagnostics']['dns_records_data'] = 'Correct Data';
|
$lang['diagnostics']['dns_records_data'] = 'Correct Data';
|
||||||
$lang['diagnostics']['dns_records_status'] = 'Current State';
|
$lang['diagnostics']['dns_records_status'] = 'Current State';
|
||||||
|
$lang['diagnostics']['optional'] = 'This record is optional.';
|
||||||
|
$lang['diagnostics']['cname_from_a'] = 'Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.';
|
||||||
|
|
||||||
$lang['admin']['relay_from'] = '"From:" address';
|
$lang['admin']['relay_from'] = '"From:" address';
|
||||||
$lang['admin']['relay_run'] = "Run test";
|
$lang['admin']['api_allow_from'] = "Allow API access from these IPs";
|
||||||
|
$lang['admin']['api_key'] = "API key";
|
||||||
|
$lang['admin']['activate_api'] = "Activate API";
|
||||||
|
$lang['admin']['regen_api_key'] = "Regenerate API key";
|
||||||
|
|
||||||
|
$lang['admin']['ui_texts'] = "UI labels and texts";
|
||||||
|
$lang['admin']['help_text'] = "Override help text below login mask (HTML allowed)";
|
||||||
|
$lang['admin']['main_name'] = '"mailcow UI" name';
|
||||||
|
$lang['admin']['apps_name'] = '"mailcow Apps" name';
|
||||||
|
|
||||||
$lang['admin']['customize'] = "Customize";
|
$lang['admin']['customize'] = "Customize";
|
||||||
$lang['admin']['change_logo'] = "Change logo";
|
$lang['admin']['change_logo'] = "Change logo";
|
||||||
|
@ -574,3 +590,55 @@ $lang['edit']['delimiter_action'] = "Change delimiter action";
|
||||||
$lang['edit']['syncjobs'] = "Add or change sync jobs";
|
$lang['edit']['syncjobs'] = "Add or change sync jobs";
|
||||||
$lang['edit']['eas_reset'] = "Reset EAS devices";
|
$lang['edit']['eas_reset'] = "Reset EAS devices";
|
||||||
$lang['edit']['spam_alias'] = "Create or change time limited alias addresses";
|
$lang['edit']['spam_alias'] = "Create or change time limited alias addresses";
|
||||||
|
|
||||||
|
$lang['danger']['img_tmp_missing'] = "Cannot validate image file: Temporary file not found";
|
||||||
|
$lang['danger']['img_invalid'] = "Cannot validate image file";
|
||||||
|
$lang['danger']['invalid_mime_type'] = "Invalid mime type";
|
||||||
|
$lang['success']['upload_success'] = "File uploaded successfully";
|
||||||
|
$lang['success']['app_links'] = "Saved changes to app links";
|
||||||
|
$lang['success']['ui_texts'] = "Saved changes to UI texts";
|
||||||
|
$lang['success']['reset_main_logo'] = "Reset to default logo";
|
||||||
|
$lang['success']['items_released'] = "Selected items were released";
|
||||||
|
$lang['danger']['imagick_exception'] = "Error: Imagick exception while reading image";
|
||||||
|
|
||||||
|
$lang['quarantaine']['quarantaine'] = "Quarantaine";
|
||||||
|
$lang['quarantaine']['qinfo'] = "The quarantaine system will save rejected mail to the database, while the sender will <em>not</em> be given the impression of a delivered mail.<br />
|
||||||
|
Only mails up to 10 MiB will be saved in the quarantaine.";
|
||||||
|
$lang['quarantaine']['release'] = "Release";
|
||||||
|
$lang['quarantaine']['empty'] = 'No results';
|
||||||
|
$lang['quarantaine']['toggle_all'] = 'Toggle all';
|
||||||
|
$lang['quarantaine']['quick_actions'] = 'Actions';
|
||||||
|
$lang['quarantaine']['remove'] = 'Remove';
|
||||||
|
$lang['quarantaine']['received'] = "Received";
|
||||||
|
$lang['quarantaine']['action'] = "Action";
|
||||||
|
$lang['quarantaine']['rcpt'] = "Recipient";
|
||||||
|
$lang['quarantaine']['qid'] = "Rspamd QID";
|
||||||
|
$lang['quarantaine']['sender'] = "Sender";
|
||||||
|
$lang['quarantaine']['show_item'] = "Show item";
|
||||||
|
$lang['quarantaine']['check_hash'] = "Search file hash @ VT";
|
||||||
|
$lang['quarantaine']['qitem'] = "Quarantaine item";
|
||||||
|
$lang['quarantaine']['subj'] = "Subject";
|
||||||
|
$lang['quarantaine']['text_plain_content'] = "Content (text/plain)";
|
||||||
|
$lang['quarantaine']['atts'] = "Attachments";
|
||||||
|
|
||||||
|
$lang['header']['quarantaine'] = "Quarantaine";
|
||||||
|
$lang['header']['debug'] = "Debug";
|
||||||
|
|
||||||
|
$lang['quarantaine']['release_body'] = "We have attached your message as eml file to this message.";
|
||||||
|
$lang['danger']['release_send_failed'] = "Message could not be released: %s";
|
||||||
|
$lang['quarantaine']['release_subject'] = "Potentially damaging quarantaine item %s";
|
||||||
|
|
||||||
|
$lang['mailbox']['bcc_map_type'] = "BCC type";
|
||||||
|
$lang['mailbox']['bcc_type'] = "BCC type";
|
||||||
|
$lang['mailbox']['bcc_sender_map'] = "Sender map";
|
||||||
|
$lang['mailbox']['bcc_rcpt_map'] = "Recipient map";
|
||||||
|
$lang['mailbox']['bcc_local_dest'] = "Local destination";
|
||||||
|
$lang['mailbox']['bcc_destinations'] = "BCC destination/s";
|
||||||
|
|
||||||
|
$lang['mailbox']['bcc'] = "BCC";
|
||||||
|
$lang['mailbox']['bcc_maps'] = "BCC maps";
|
||||||
|
$lang['mailbox']['bcc_to_sender'] = "Switch to sender map type";
|
||||||
|
$lang['mailbox']['bcc_to_rcpt'] = "Switch to recipient map type";
|
||||||
|
$lang['mailbox']['add_bcc_entry'] = "Add BCC map";
|
||||||
|
$lang['mailbox']['bcc_info'] = "A recipient map type entry is used, when the local destination acts as recipient of a mail. Sender maps conform to the same principle.<br/>
|
||||||
|
The local destination will not be informed about a failed delivery.";
|
||||||
|
|
|
@ -21,6 +21,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation"><a href="#tab-syncjobs" aria-controls="tab-syncjobs" role="tab" data-toggle="tab"><?=$lang['mailbox']['sync_jobs'];?></a></li>
|
<li role="presentation"><a href="#tab-syncjobs" aria-controls="tab-syncjobs" role="tab" data-toggle="tab"><?=$lang['mailbox']['sync_jobs'];?></a></li>
|
||||||
<li role="presentation"><a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['filters'];?></a></li>
|
<li role="presentation"><a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['filters'];?></a></li>
|
||||||
|
<li role="presentation"><a href="#tab-bcc" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['bcc_maps'];?></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -207,6 +208,33 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div role="tabpanel" class="tab-pane" id="tab-bcc">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title"><?=$lang['mailbox']['bcc_maps'];?></h3>
|
||||||
|
</div>
|
||||||
|
<p style="margin:10px" class="help-block"><?=$lang['mailbox']['bcc_info'];?></p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped" id="bcc_table"></table>
|
||||||
|
</div>
|
||||||
|
<div class="mass-actions-mailbox">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="bcc" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
|
||||||
|
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||||
|
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"sender"}' href="#"><?=$lang['mailbox']['bcc_to_sender'];?></a></li>
|
||||||
|
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"rcpt"}' href="#"><?=$lang['mailbox']['bcc_to_rcpt'];?></a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a id="delete_selected" data-id="bcc" data-api-url='delete/bcc' href="#"><?=$lang['mailbox']['remove'];?></a></li>
|
||||||
|
</ul>
|
||||||
|
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addBCCModalAdmin"><span class="glyphicon glyphicon-plus"></span> <?=$lang['mailbox']['add_bcc_entry'];?></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div> <!-- /tab-content -->
|
</div> <!-- /tab-content -->
|
||||||
</div> <!-- /col-md-12 -->
|
</div> <!-- /col-md-12 -->
|
||||||
</div> <!-- /row -->
|
</div> <!-- /row -->
|
||||||
|
|
|
@ -13,7 +13,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['admin']['add_domain_admin'];?></h3>
|
<h3 class="modal-title"><?=$lang['admin']['add_domain_admin'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" data-id="domain_admin" role="form" method="post">
|
<form class="form-horizontal" data-cached-form="true" data-id="domain_admin" role="form" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
|
<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -71,7 +71,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Relayhost</h3>
|
<h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Relayhost</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" id="test_relayhost_form" role="form" method="post">
|
<form class="form-horizontal" data-cached-form="true" id="test_relayhost_form" role="form" method="post">
|
||||||
<input type="hidden" class="form-control" name="relayhost_id" id="relayhost_id">
|
<input type="hidden" class="form-control" name="relayhost_id" id="relayhost_id">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="mail_from"><?=$lang['admin']['relay_from'];?></label>
|
<label class="control-label col-sm-2" for="mail_from"><?=$lang['admin']['relay_from'];?></label>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
|
header('Location: /');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
|
@ -49,7 +49,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<p><?=$lang['tfa']['waiting_usb_register'];?></p>
|
<p id="u2f_status_reg"></p>
|
||||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||||
<input type="hidden" name="token" id="u2f_register_data"/>
|
<input type="hidden" name="token" id="u2f_register_data"/>
|
||||||
<input type="hidden" name="tfa_method" value="u2f">
|
<input type="hidden" name="tfa_method" value="u2f">
|
||||||
|
@ -146,7 +146,7 @@ if (isset($_SESSION['pending_tfa_method'])):
|
||||||
case "u2f":
|
case "u2f":
|
||||||
?>
|
?>
|
||||||
<form role="form" method="post" id="u2f_auth_form">
|
<form role="form" method="post" id="u2f_auth_form">
|
||||||
<p><?=$lang['tfa']['waiting_usb_auth'];?></p>
|
<p id="u2f_status_auth"></p>
|
||||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||||
<input type="hidden" name="token" id="u2f_auth_data"/>
|
<input type="hidden" name="token" id="u2f_auth_data"/>
|
||||||
<input type="hidden" name="tfa_method" value="u2f">
|
<input type="hidden" name="tfa_method" value="u2f">
|
||||||
|
@ -183,19 +183,19 @@ if (isset($_SESSION['pending_tfa_method'])):
|
||||||
endif;
|
endif;
|
||||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin'):
|
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin'):
|
||||||
?>
|
?>
|
||||||
<div id="RestartSOGo" class="modal fade" role="dialog">
|
<div id="RestartContainer" class="modal fade" role="dialog">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
<h4 class="modal-title"><?= $lang['footer']['restart_sogo']; ?></h4>
|
<h4 class="modal-title"><?= $lang['footer']['restart_container']; ?> (<code id="containerName"></code>)</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p><?= $lang['footer']['restart_sogo_info']; ?></p>
|
<p><?= $lang['footer']['restart_container_info']; ?></p>
|
||||||
<hr>
|
<hr>
|
||||||
<button class="btn btn-md btn-primary" id="triggerRestartSogo"><?= $lang['footer']['restart_now']; ?></button>
|
<button class="btn btn-md btn-primary" id="triggerRestartContainer"><?= $lang['footer']['restart_now']; ?></button>
|
||||||
<br><br>
|
<br><br>
|
||||||
<div id="statusTriggerRestartSogo"></div>
|
<div id="statusTriggerRestartContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['mailbox']['add_mailbox'];?></h3>
|
<h3 class="modal-title"><?=$lang['mailbox']['add_mailbox'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" data-id="add_mailbox" role="form">
|
<form class="form-horizontal" data-cached-form="true" data-id="add_mailbox" role="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label>
|
<label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -86,7 +86,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['mailbox']['add_domain'];?></h3>
|
<h3 class="modal-title"><?=$lang['mailbox']['add_domain'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" data-id="add_domain" role="form">
|
<form class="form-horizontal" data-cached-form="true" data-id="add_domain" role="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
|
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -96,7 +96,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" name="description" id="description">
|
<input type="text" class="form-control" name="description" id="description" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -143,7 +143,8 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button class="btn btn-default" id="add_item" data-id="add_domain" data-api-url='add/domain' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
|
<button class="btn btn-default" id="add_item" data-id="add_domain" data-api-url='add/domain' data-api-attr='{}' href="#"><?=$lang['add']['add_domain_only'];?></button>
|
||||||
|
<button class="btn btn-default" id="add_item" data-id="add_domain" data-api-url='add/domain' data-api-attr='{"restart_sogo":"1"}' href="#"><?=$lang['add']['add_domain_restart'];?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
|
<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
|
||||||
|
@ -161,7 +162,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['mailbox']['add_resource'];?></h3>
|
<h3 class="modal-title"><?=$lang['mailbox']['add_resource'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" role="form" data-id="add_resource">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_resource">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -223,7 +224,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['mailbox']['add_alias'];?></h3>
|
<h3 class="modal-title"><?=$lang['mailbox']['add_alias'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" role="form" data-id="add_alias">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_alias">
|
||||||
<input type="hidden" value="0" name="active">
|
<input type="hidden" value="0" name="active">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label>
|
<label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label>
|
||||||
|
@ -268,7 +269,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title"><?=$lang['mailbox']['add_domain_alias'];?></h3>
|
<h3 class="modal-title"><?=$lang['mailbox']['add_domain_alias'];?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" role="form" data-id="add_alias_domain">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_alias_domain">
|
||||||
<input type="hidden" value="0" name="active">
|
<input type="hidden" value="0" name="active">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label>
|
<label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label>
|
||||||
|
@ -316,7 +317,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="help-block"><?=$lang['add']['syncjob_hint'];?></p>
|
<p class="help-block"><?=$lang['add']['syncjob_hint'];?></p>
|
||||||
<form class="form-horizontal" role="form" data-id="add_syncjob">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_syncjob">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -417,13 +418,6 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
|
||||||
<div class="checkbox">
|
|
||||||
<label><input type="checkbox" value="1" name="delete2"> <?=$lang['add']['delete2'];?></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
@ -450,7 +444,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<h3 class="modal-title">Filter</h3>
|
<h3 class="modal-title">Filter</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" role="form" data-id="add_filter">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_filter">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -509,6 +503,77 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- add add_filter modal -->
|
</div><!-- add add_filter modal -->
|
||||||
|
<!-- add add_bcc modal -->
|
||||||
|
<div class="modal fade" id="addBCCModalAdmin" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||||
|
<h3 class="modal-title"><?=$lang['mailbox']['bcc_maps'];?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_bcc">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="local_dest"><?=$lang['mailbox']['bcc_local_dest'];?>:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select id="addSelectLocalDest" name="local_dest" id="local_dest" required>
|
||||||
|
<?php
|
||||||
|
$domains = mailbox('get', 'domains');
|
||||||
|
$alias_domains = mailbox('get', 'alias_domains');
|
||||||
|
if (!empty($domains)) {
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
echo "<option>".htmlspecialchars($domain)."</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($alias_domains)) {
|
||||||
|
foreach ($alias_domains as $alias_domain) {
|
||||||
|
echo "<option>".htmlspecialchars($alias_domain)."</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($domains)) {
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
$mailboxes = mailbox('get', 'mailboxes', $domain);
|
||||||
|
foreach ($mailboxes as $mailbox) {
|
||||||
|
echo "<option>".htmlspecialchars($mailbox)."</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="type"><?=$lang['mailbox']['bcc_map_type'];?>:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select id="addFBCCType" name="type" id="type" required>
|
||||||
|
<option value="sender"><?=$lang['mailbox']['bcc_sender_map'];?></option>
|
||||||
|
<option value="rcpt"><?=$lang['mailbox']['bcc_rcpt_map'];?></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="bcc_dest"><?=$lang['mailbox']['bcc_destinations'];?>:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="bcc_dest" name="bcc_dest" required></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button class="btn btn-success" id="add_item" data-id="add_bcc" data-api-url='add/bcc' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- add add_bcc modal -->
|
||||||
<!-- log modal -->
|
<!-- log modal -->
|
||||||
<div class="modal fade" id="syncjobLogModal" tabindex="-1" role="dialog" aria-labelledby="syncjobLogModalLabel">
|
<div class="modal fade" id="syncjobLogModal" tabindex="-1" role="dialog" aria-labelledby="syncjobLogModalLabel">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
@ -520,3 +585,15 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- log modal -->
|
</div><!-- log modal -->
|
||||||
|
<!-- DNS info modal -->
|
||||||
|
<div class="modal fade" id="dnsInfoModal" tabindex="-1" role="dialog" aria-labelledby="dnsInfoModalLabel">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header"><h4 class="modal-title"><?=$lang['diagnostics']['dns_records'];?></h4></div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p><?=$lang['diagnostics']['dns_records_24hours'];?></p>
|
||||||
|
<div class="dns-modal-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- DNS info modal -->
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
|
header('Location: /');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="modal fade" id="qidDetailModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||||
|
<h3 class="modal-title"><span class="glyphicon glyphicon-info"></span> <?=$lang['quarantaine']['qitem'];?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="qid_error" style="display:none" class="alert alert-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="qid_detail_subj"><h4><?=$lang['quarantaine']['subj'];?>:</h4></label>
|
||||||
|
<p id="qid_detail_subj"></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="qid_detail_text"><h4><?=$lang['quarantaine']['text_plain_content'];?>:</h4></label>
|
||||||
|
<pre id="qid_detail_text"></pre>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="qid_detail_atts"><h4><?=$lang['quarantaine']['atts'];?>:</h4></label>
|
||||||
|
<div id="qid_detail_atts">-</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -14,7 +14,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p><?=$lang['add']['syncjob_hint'];?></p>
|
<p><?=$lang['add']['syncjob_hint'];?></p>
|
||||||
<form class="form-horizontal" role="form" data-id="add_syncjob">
|
<form class="form-horizontal" data-cached-form="true" role="form" data-id="add_syncjob">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
|
<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -130,7 +130,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="form-horizontal" data-id="pwchange" role="form" method="post" autocomplete="off">
|
<form class="form-horizontal" data-cached-form="true" data-id="pwchange" role="form" method="post" autocomplete="off">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
|
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
require_once "inc/prerequisites.inc.php";
|
||||||
|
|
||||||
|
if (isset($_SESSION['mailcow_cc_role'])) {
|
||||||
|
require_once "inc/header.inc.php";
|
||||||
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title"><?=$lang['quarantaine']['quarantaine'];?></h3>
|
||||||
|
</div>
|
||||||
|
<p style="margin:10px" class="help-block"><?=$lang['quarantaine']['qinfo'];?></p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="quarantainetable" class="table table-striped"></table>
|
||||||
|
</div>
|
||||||
|
<div class="mass-actions-quarantaine">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="qitems" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['quarantaine']['toggle_all'];?></a>
|
||||||
|
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['quarantaine']['quick_actions'];?> <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a id="edit_selected" data-id="qitems" data-api-url='edit/qitem' data-api-attr='{"action":"release"}' href="#"><?=$lang['quarantaine']['release'];?></a></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li><a id="delete_selected" data-id="qitems" data-api-url='delete/qitem' href="#"><?=$lang['quarantaine']['remove'];?></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /col-md-12 -->
|
||||||
|
</div> <!-- /row -->
|
||||||
|
</div> <!-- /container -->
|
||||||
|
<?php
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/quarantaine.php';
|
||||||
|
?>
|
||||||
|
<script type='text/javascript'>
|
||||||
|
<?php
|
||||||
|
$lang_mailbox = json_encode($lang['quarantaine']);
|
||||||
|
echo "var lang = ". $lang_mailbox . ";\n";
|
||||||
|
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
|
||||||
|
$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin';
|
||||||
|
echo "var role = '". $role . "';\n";
|
||||||
|
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
|
||||||
|
?>
|
||||||
|
</script>
|
||||||
|
<script src="js/footable.min.js"></script>
|
||||||
|
<script src="js/quarantaine.js"></script>
|
||||||
|
<?php
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||||
|
} else {
|
||||||
|
header('Location: /');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
|
@ -23,7 +23,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
|
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
|
||||||
<div class="col-sm-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
||||||
<div id="tfa_additional">
|
<div id="tfa_additional">
|
||||||
|
@ -40,8 +40,8 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div>
|
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div>
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
||||||
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
||||||
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
||||||
|
@ -105,11 +105,18 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
||||||
$user_get_alias_details = user_get_alias_details($username);
|
$user_get_alias_details = user_get_alias_details($username);
|
||||||
?>
|
?>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div>
|
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:</div>
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-md-9 col-xs-7">
|
||||||
<p><?=$user_get_alias_details['aliases'];?></p>
|
<p><?=$user_get_alias_details['direct_aliases'];?></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:</div>
|
||||||
|
<div class="col-md-9 col-xs-7">
|
||||||
|
<p><?=$user_get_alias_details['shared_aliases'];?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
|
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-md-9 col-xs-7">
|
||||||
|
|
|
@ -2,9 +2,11 @@ version: '2.1'
|
||||||
services:
|
services:
|
||||||
|
|
||||||
unbound-mailcow:
|
unbound-mailcow:
|
||||||
image: mailcow/unbound:1.0
|
image: mailcow/unbound:1.1
|
||||||
build: ./data/Dockerfiles/unbound
|
build: ./data/Dockerfiles/unbound
|
||||||
command: /usr/sbin/unbound
|
command: /usr/sbin/unbound
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro
|
- ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -16,11 +18,11 @@ services:
|
||||||
|
|
||||||
mysql-mailcow:
|
mysql-mailcow:
|
||||||
image: mariadb:10.2
|
image: mariadb:10.2
|
||||||
command: mysqld --max_allowed_packet=192M --max-connections=1500 --innodb-strict-mode=0 --skip-host-cache --skip-name-resolve --log-warnings=0
|
|
||||||
volumes:
|
volumes:
|
||||||
- mysql-vol-1:/var/lib/mysql/
|
- mysql-vol-1:/var/lib/mysql/
|
||||||
- ./data/conf/mysql/:/etc/mysql/conf.d/:ro
|
- ./data/conf/mysql/:/etc/mysql/conf.d/:ro
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
- MYSQL_ROOT_PASSWORD=${DBROOT}
|
- MYSQL_ROOT_PASSWORD=${DBROOT}
|
||||||
- MYSQL_DATABASE=${DBNAME}
|
- MYSQL_DATABASE=${DBNAME}
|
||||||
- MYSQL_USER=${DBUSER}
|
- MYSQL_USER=${DBUSER}
|
||||||
|
@ -39,6 +41,8 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- redis-vol-1:/data/
|
- redis-vol-1:/data/
|
||||||
restart: always
|
restart: always
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
dns:
|
dns:
|
||||||
- 172.22.1.254
|
- 172.22.1.254
|
||||||
networks:
|
networks:
|
||||||
|
@ -48,11 +52,12 @@ services:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
clamd-mailcow:
|
clamd-mailcow:
|
||||||
image: mailcow/clamd:1.5
|
image: mailcow/clamd:1.6
|
||||||
build: ./data/Dockerfiles/clamd
|
build: ./data/Dockerfiles/clamd
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- SKIP_CLAMD=${SKIP_CLAMD:-n}
|
- SKIP_CLAMD=${SKIP_CLAMD:-n}
|
||||||
|
- TZ=${TZ}
|
||||||
dns:
|
dns:
|
||||||
- 172.22.1.254
|
- 172.22.1.254
|
||||||
networks:
|
networks:
|
||||||
|
@ -61,11 +66,13 @@ services:
|
||||||
- clamd
|
- clamd
|
||||||
|
|
||||||
rspamd-mailcow:
|
rspamd-mailcow:
|
||||||
image: mailcow/rspamd:1.14
|
image: mailcow/rspamd:1.15
|
||||||
build: ./data/Dockerfiles/rspamd
|
build: ./data/Dockerfiles/rspamd
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- nginx-mailcow
|
- nginx-mailcow
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/conf/rspamd/custom/:/etc/rspamd/custom:ro
|
- ./data/conf/rspamd/custom/:/etc/rspamd/custom:ro
|
||||||
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:ro
|
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:ro
|
||||||
|
@ -84,7 +91,7 @@ services:
|
||||||
- rspamd
|
- rspamd
|
||||||
|
|
||||||
php-fpm-mailcow:
|
php-fpm-mailcow:
|
||||||
image: mailcow/phpfpm:1.5
|
image: mailcow/phpfpm:1.7
|
||||||
build: ./data/Dockerfiles/phpfpm
|
build: ./data/Dockerfiles/phpfpm
|
||||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -93,7 +100,10 @@ services:
|
||||||
- ./data/web:/web:rw
|
- ./data/web:/web:rw
|
||||||
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
||||||
- dkim-vol-1:/data/dkim
|
- dkim-vol-1:/data/dkim
|
||||||
|
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
|
||||||
environment:
|
environment:
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
|
- TZ=${TZ}
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
- DBPASS=${DBPASS}
|
- DBPASS=${DBPASS}
|
||||||
|
@ -115,13 +125,14 @@ services:
|
||||||
- phpfpm
|
- phpfpm
|
||||||
|
|
||||||
sogo-mailcow:
|
sogo-mailcow:
|
||||||
image: mailcow/sogo:1.10
|
image: mailcow/sogo:1.13
|
||||||
build: ./data/Dockerfiles/sogo
|
build: ./data/Dockerfiles/sogo
|
||||||
environment:
|
environment:
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
- DBPASS=${DBPASS}
|
- DBPASS=${DBPASS}
|
||||||
- TZ=${TZ}
|
- TZ=${TZ}
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/conf/sogo/:/etc/sogo/
|
- ./data/conf/sogo/:/etc/sogo/
|
||||||
|
@ -135,7 +146,7 @@ services:
|
||||||
- sogo
|
- sogo
|
||||||
|
|
||||||
dovecot-mailcow:
|
dovecot-mailcow:
|
||||||
image: mailcow/dovecot:1.12
|
image: mailcow/dovecot:1.14
|
||||||
build: ./data/Dockerfiles/dovecot
|
build: ./data/Dockerfiles/dovecot
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_BIND_SERVICE
|
- NET_BIND_SERVICE
|
||||||
|
@ -146,9 +157,11 @@ services:
|
||||||
- vmail-vol-1:/var/vmail
|
- vmail-vol-1:/var/vmail
|
||||||
- crypt-vol-1:/mail_crypt/
|
- crypt-vol-1:/mail_crypt/
|
||||||
environment:
|
environment:
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
- DBPASS=${DBPASS}
|
- DBPASS=${DBPASS}
|
||||||
|
- TZ=${TZ}
|
||||||
ports:
|
ports:
|
||||||
- "${DOVEADM_PORT:-127.0.0.1:19991}:12345"
|
- "${DOVEADM_PORT:-127.0.0.1:19991}:12345"
|
||||||
- "${IMAP_PORT:-143}:143"
|
- "${IMAP_PORT:-143}:143"
|
||||||
|
@ -171,7 +184,7 @@ services:
|
||||||
- dovecot
|
- dovecot
|
||||||
|
|
||||||
postfix-mailcow:
|
postfix-mailcow:
|
||||||
image: mailcow/postfix:1.7
|
image: mailcow/postfix:1.11
|
||||||
build: ./data/Dockerfiles/postfix
|
build: ./data/Dockerfiles/postfix
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/conf/postfix:/opt/postfix/conf
|
- ./data/conf/postfix:/opt/postfix/conf
|
||||||
|
@ -179,6 +192,8 @@ services:
|
||||||
- postfix-vol-1:/var/spool/postfix
|
- postfix-vol-1:/var/spool/postfix
|
||||||
- crypt-vol-1:/var/lib/zeyple
|
- crypt-vol-1:/var/lib/zeyple
|
||||||
environment:
|
environment:
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
|
- TZ=${TZ}
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
- DBPASS=${DBPASS}
|
- DBPASS=${DBPASS}
|
||||||
|
@ -228,6 +243,7 @@ services:
|
||||||
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
||||||
- ./data/assets/ssl/:/etc/ssl/mail/:ro
|
- ./data/assets/ssl/:/etc/ssl/mail/:ro
|
||||||
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
|
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
|
||||||
|
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
|
||||||
ports:
|
ports:
|
||||||
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
||||||
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
||||||
|
@ -244,11 +260,12 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- nginx-mailcow
|
- nginx-mailcow
|
||||||
- mysql-mailcow
|
- mysql-mailcow
|
||||||
image: mailcow/acme:1.24
|
image: mailcow/acme:1.27
|
||||||
build: ./data/Dockerfiles/acme
|
build: ./data/Dockerfiles/acme
|
||||||
dns:
|
dns:
|
||||||
- 172.22.1.254
|
- 172.22.1.254
|
||||||
environment:
|
environment:
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
- ADDITIONAL_SAN=${ADDITIONAL_SAN}
|
- ADDITIONAL_SAN=${ADDITIONAL_SAN}
|
||||||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
|
@ -267,7 +284,7 @@ services:
|
||||||
- acme
|
- acme
|
||||||
|
|
||||||
fail2ban-mailcow:
|
fail2ban-mailcow:
|
||||||
image: mailcow/fail2ban:1.9
|
image: mailcow/fail2ban:1.10
|
||||||
build: ./data/Dockerfiles/fail2ban
|
build: ./data/Dockerfiles/fail2ban
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -288,12 +305,16 @@ services:
|
||||||
- /lib/modules:/lib/modules:ro
|
- /lib/modules:/lib/modules:ro
|
||||||
|
|
||||||
watchdog-mailcow:
|
watchdog-mailcow:
|
||||||
image: mailcow/watchdog:1.10
|
image: mailcow/watchdog:1.12
|
||||||
|
# Debug
|
||||||
|
#command: /watchdog.sh
|
||||||
build: ./data/Dockerfiles/watchdog
|
build: ./data/Dockerfiles/watchdog
|
||||||
volumes:
|
volumes:
|
||||||
- vmail-vol-1:/vmail:ro
|
- vmail-vol-1:/vmail:ro
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
|
- LOG_LINES=${LOG_LINES}
|
||||||
|
- TZ=${TZ}
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
- DBPASS=${DBPASS}
|
- DBPASS=${DBPASS}
|
||||||
|
@ -307,12 +328,15 @@ services:
|
||||||
- watchdog
|
- watchdog
|
||||||
|
|
||||||
dockerapi-mailcow:
|
dockerapi-mailcow:
|
||||||
image: mailcow/dockerapi:1.2
|
image: mailcow/dockerapi:1.4
|
||||||
restart: always
|
restart: always
|
||||||
build: ./data/Dockerfiles/dockerapi
|
build: ./data/Dockerfiles/dockerapi
|
||||||
oom_score_adj: -10
|
oom_score_adj: -10
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ./data/conf/rspamd/override.d/worker-controller-password.inc:/access.inc:rw
|
||||||
networks:
|
networks:
|
||||||
mailcow-network:
|
mailcow-network:
|
||||||
aliases:
|
aliases:
|
||||||
|
|
|
@ -101,6 +101,8 @@ USE_WATCHDOG=n
|
||||||
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
|
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
|
||||||
#WATCHDOG_NOTIFY_EMAIL=
|
#WATCHDOG_NOTIFY_EMAIL=
|
||||||
|
|
||||||
|
LOG_LINES=9999
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
mkdir -p data/assets/ssl
|
mkdir -p data/assets/ssl
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ ! ${1} =~ (backup|restore) ]]; then
|
||||||
|
echo "First parameter needs to be 'backup' or 'restore'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${1} == "backup" && ! ${2} =~ (vmail|redis|rspamd|postfix|mysql|all) ]]; then
|
||||||
|
echo "Second parameter needs to be 'vmail', 'redis', 'rspamd', 'postfix', 'mysql' or 'all'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z ${BACKUP_LOCATION} ]]; then
|
||||||
|
while [[ -z ${BACKUP_LOCATION} ]]; do
|
||||||
|
read -ep "Backup location (absolute path, starting with /): " BACKUP_LOCATION
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! ${BACKUP_LOCATION} =~ ^/ ]]; then
|
||||||
|
echo "Backup directory needs to be given as absolute path (starting with /)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f ${BACKUP_LOCATION} ]]; then
|
||||||
|
echo "${BACKUP_LOCATION} is a file!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d ${BACKUP_LOCATION} ]]; then
|
||||||
|
echo "${BACKUP_LOCATION} is not a directory"
|
||||||
|
read -p "Create it now? [y|N] " CREATE_BACKUP_LOCATION
|
||||||
|
if [[ ! ${CREATE_BACKUP_LOCATION,,} =~ ^(yes|y)$ ]]; then
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
mkdir ${BACKUP_LOCATION}
|
||||||
|
chmod 755 ${BACKUP_LOCATION}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ${1} == "backup" ]] && [[ -z $(echo $(stat -Lc %a ${BACKUP_LOCATION}) | grep -oE '[0-9][0-9][5-7]') ]]; then
|
||||||
|
echo "${BACKUP_LOCATION} is not write-able for others, that's required for a backup."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
BACKUP_LOCATION=$(echo ${BACKUP_LOCATION} | sed 's#/$##')
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
|
||||||
|
echo "Using ${BACKUP_LOCATION} as backup/restore location."
|
||||||
|
echo
|
||||||
|
source ${SCRIPT_DIR}/../mailcow.conf
|
||||||
|
|
||||||
|
function backup() {
|
||||||
|
DATE=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||||
|
mkdir -p "${BACKUP_LOCATION}/mailcow-${DATE}"
|
||||||
|
chmod 755 "${BACKUP_LOCATION}/mailcow-${DATE}"
|
||||||
|
while (( "$#" )); do
|
||||||
|
case "$1" in
|
||||||
|
vmail|all)
|
||||||
|
docker run --rm \
|
||||||
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=vmail):/vmail \
|
||||||
|
debian:stretch-slim /bin/tar -cvpzf /backup/backup_vmail.tar.gz /vmail
|
||||||
|
;;&
|
||||||
|
redis|all)
|
||||||
|
docker exec -it $(docker ps -qf name=redis) redis-cli save
|
||||||
|
docker run --rm \
|
||||||
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=redis):/redis \
|
||||||
|
debian:stretch-slim /bin/tar -cvpzf /backup/backup_redis.tar.gz /redis
|
||||||
|
;;&
|
||||||
|
rspamd|all)
|
||||||
|
docker run --rm \
|
||||||
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=rspamd):/rspamd \
|
||||||
|
debian:stretch-slim /bin/tar -cvpzf /backup/backup_rspamd.tar.gz /rspamd
|
||||||
|
;;&
|
||||||
|
postfix|all)
|
||||||
|
docker run -it --rm \
|
||||||
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=postfix):/postfix \
|
||||||
|
debian:stretch-slim /bin/tar -cvpzf /backup/backup_postfix.tar.gz /postfix
|
||||||
|
;;&
|
||||||
|
mysql|all)
|
||||||
|
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
|
||||||
|
docker run -it --rm \
|
||||||
|
--network $(docker network ls -qf name=mailcow) \
|
||||||
|
-v $(docker volume ls -qf name=mysql):/var/lib/mysql/ \
|
||||||
|
--entrypoint= \
|
||||||
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
|
||||||
|
${SQLIMAGE} /bin/sh -c "mysqldump -hmysql -uroot -p${DBROOT} --all-databases | gzip > /backup/backup_mysql.gz"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore() {
|
||||||
|
docker stop $(docker ps -qf name=watchdog-mailcow)
|
||||||
|
RESTORE_LOCATION="${1}"
|
||||||
|
shift
|
||||||
|
while (( "$#" )); do
|
||||||
|
case "$1" in
|
||||||
|
vmail)
|
||||||
|
docker stop $(docker ps -qf name=dovecot-mailcow)
|
||||||
|
docker run -it --rm \
|
||||||
|
-v ${RESTORE_LOCATION}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=vmail):/vmail \
|
||||||
|
debian:stretch-slim /bin/tar -xvzf /backup/backup_vmail.tar.gz
|
||||||
|
docker start $(docker ps -aqf name=dovecot-mailcow)
|
||||||
|
echo
|
||||||
|
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
|
||||||
|
echo
|
||||||
|
echo "docker exec $(docker ps -qf name=dovecot-mailcow) doveadm force-resync -A '*'"
|
||||||
|
echo
|
||||||
|
read -p "Force a resync now? [y|N] " FORCE_RESYNC
|
||||||
|
if [[ ${FORCE_RESYNC,,} =~ ^(yes|y)$ ]]; then
|
||||||
|
docker exec $(docker ps -qf name=dovecot-mailcow) doveadm force-resync -A '*'
|
||||||
|
else
|
||||||
|
echo "OK, skipped."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
redis)
|
||||||
|
docker stop $(docker ps -qf name=redis-mailcow)
|
||||||
|
docker run -it --rm \
|
||||||
|
-v ${RESTORE_LOCATION}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=redis):/redis \
|
||||||
|
debian:stretch-slim /bin/tar -xvzf /backup/backup_redis.tar.gz
|
||||||
|
docker start $(docker ps -aqf name=redis-mailcow)
|
||||||
|
;;
|
||||||
|
rspamd)
|
||||||
|
docker stop $(docker ps -qf name=rspamd-mailcow)
|
||||||
|
docker run -it --rm \
|
||||||
|
-v ${RESTORE_LOCATION}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=rspamd):/rspamd \
|
||||||
|
debian:stretch-slim /bin/tar -xvzf /backup/backup_rspamd.tar.gz
|
||||||
|
docker start $(docker ps -aqf name=rspamd-mailcow)
|
||||||
|
;;
|
||||||
|
postfix)
|
||||||
|
docker stop $(docker ps -qf name=postfix-mailcow)
|
||||||
|
docker run -it --rm \
|
||||||
|
-v ${RESTORE_LOCATION}:/backup \
|
||||||
|
-v $(docker volume ls -qf name=postfix):/postfix \
|
||||||
|
debian:stretch-slim /bin/tar -xvzf /backup/backup_postfix.tar.gz
|
||||||
|
docker start $(docker ps -aqf name=postfix-mailcow)
|
||||||
|
;;
|
||||||
|
mysql)
|
||||||
|
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
|
||||||
|
docker stop $(docker ps -qf name=mysql-mailcow)
|
||||||
|
docker run \
|
||||||
|
-it --rm \
|
||||||
|
-v $(docker volume ls -qf name=mysql):/var/lib/mysql/ \
|
||||||
|
--entrypoint= \
|
||||||
|
-u mysql \
|
||||||
|
-v ${RESTORE_LOCATION}:/backup \
|
||||||
|
${SQLIMAGE} /bin/sh -c "mysqld --skip-grant-tables & \
|
||||||
|
until mysqladmin ping; do sleep 3; done && \
|
||||||
|
echo Restoring... && \
|
||||||
|
gunzip < backup/backup_mysql.gz | mysql -uroot && \
|
||||||
|
mysql -uroot -e SHUTDOWN;"
|
||||||
|
docker start $(docker ps -aqf name=mysql-mailcow)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
docker start $(docker ps -aqf name=watchdog-mailcow)
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${1} == "backup" ]]; then
|
||||||
|
backup ${@,,}
|
||||||
|
elif [[ ${1} == "restore" ]]; then
|
||||||
|
i=1
|
||||||
|
declare -A FOLDER_SELECTION
|
||||||
|
if [[ $(find ${BACKUP_LOCATION}/mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then
|
||||||
|
echo "Selected backup location has no subfolders"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for folder in $(ls -d ${BACKUP_LOCATION}/mailcow-*/); do
|
||||||
|
echo "[ ${i} ] - ${folder}"
|
||||||
|
FOLDER_SELECTION[${i}]="${folder}"
|
||||||
|
((i++))
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
input_sel=0
|
||||||
|
while [[ ${input_sel} -lt 1 || ${input_sel} -gt ${i} ]]; do
|
||||||
|
read -p "Select a restore point: " input_sel
|
||||||
|
done
|
||||||
|
i=1
|
||||||
|
echo
|
||||||
|
declare -A FILE_SELECTION
|
||||||
|
RESTORE_POINT="${FOLDER_SELECTION[${input_sel}]}"
|
||||||
|
if [[ -z $(find "${FOLDER_SELECTION[${input_sel}]}" -maxdepth 1 -type f -regex ".*\(redis\|rspamd\|mysql\|vmail\|postfix\).*") ]]; then
|
||||||
|
echo "No datasets found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for file in $(ls -f "${FOLDER_SELECTION[${input_sel}]}"); do
|
||||||
|
if [[ ${file} =~ vmail ]]; then
|
||||||
|
echo "[ ${i} ] - Mail directory (/var/vmail)"
|
||||||
|
FILE_SELECTION[${i}]="vmail"
|
||||||
|
((i++))
|
||||||
|
elif [[ ${file} =~ redis ]]; then
|
||||||
|
echo "[ ${i} ] - Redis DB"
|
||||||
|
FILE_SELECTION[${i}]="redis"
|
||||||
|
((i++))
|
||||||
|
elif [[ ${file} =~ rspamd ]]; then
|
||||||
|
echo "[ ${i} ] - Rspamd data"
|
||||||
|
FILE_SELECTION[${i}]="rspamd"
|
||||||
|
((i++))
|
||||||
|
elif [[ ${file} =~ postfix ]]; then
|
||||||
|
echo "[ ${i} ] - Postfix data"
|
||||||
|
FILE_SELECTION[${i}]="postfix"
|
||||||
|
((i++))
|
||||||
|
elif [[ ${file} =~ mysql ]]; then
|
||||||
|
echo "[ ${i} ] - SQL DB"
|
||||||
|
FILE_SELECTION[${i}]="mysql"
|
||||||
|
((i++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
input_sel=0
|
||||||
|
while [[ ${input_sel} -lt 1 || ${input_sel} -gt ${i} ]]; do
|
||||||
|
read -p "Select a dataset to restore: " input_sel
|
||||||
|
done
|
||||||
|
echo "Restoring ${FILE_SELECTION[${input_sel}]} from ${RESTORE_POINT}..."
|
||||||
|
restore "${RESTORE_POINT}" ${FILE_SELECTION[${input_sel}]}
|
||||||
|
fi
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue